]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-placement.cc
use classnames for interface naming; remove inclusion of
[lilypond.git] / lily / accidental-placement.cc
1 /*
2   accidental-placement.cc -- implement Accidental_placement
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2002--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9
10 #include "accidental-placement.hh"
11
12 #include "item.hh"
13 #include "rhythmic-head.hh"
14 #include "accidental-interface.hh"
15 #include "music.hh"
16 #include "note-collision.hh"
17 #include "note-column.hh"
18 #include "pitch.hh"
19 #include "pointer-group-interface.hh"
20 #include "skyline.hh"
21 #include "stream-event.hh"
22 #include "warn.hh"
23
24
25 void
26 Accidental_placement::add_accidental (Grob *me, Grob *a)
27 {
28   a->set_parent (me, X_AXIS);
29   a->set_property ("X-offset", Grob::x_parent_positioning_proc);
30   SCM cause = a->get_parent (Y_AXIS)->get_property ("cause");
31
32   Stream_event *mcause = unsmob_stream_event (cause);
33   if (!mcause)
34     {
35       programming_error ("note head has no event cause");
36       return;
37     }
38
39   Pitch *p = unsmob_pitch (mcause->get_property ("pitch"));
40
41   int n = p->get_notename ();
42
43   SCM accs = me->get_object ("accidental-grobs");
44   SCM key = scm_from_int (n);
45   SCM entry = scm_assq (key, accs);
46   if (entry == SCM_BOOL_F)
47     entry = SCM_EOL;
48   else
49     entry = scm_cdr (entry);
50
51   entry = scm_cons (a->self_scm (), entry);
52
53   accs = scm_assq_set_x (accs, key, entry);
54
55   me->set_object ("accidental-grobs", accs);
56 }
57
58 /*
59   Split into break reminders.
60 */
61 void
62 Accidental_placement::split_accidentals (Grob *accs,
63                                          vector<Grob*> *break_reminder,
64                                          vector<Grob*> *real_acc)
65 {
66   for (SCM acs = accs->get_object ("accidental-grobs"); scm_is_pair (acs);
67        acs = scm_cdr (acs))
68     for (SCM s = scm_cdar (acs); scm_is_pair (s); s = scm_cdr (s))
69       {
70         Grob *a = unsmob_grob (scm_car (s));
71
72         if (unsmob_grob (a->get_object ("tie")))
73           break_reminder->push_back (a);
74         else
75           real_acc->push_back (a);
76       }
77 }
78
79 /*
80   Accidentals are special, because they appear and disappear after
81   ties at will.
82 */
83 Interval
84 Accidental_placement::get_relevant_accidental_extent (Grob *me,
85                                                       Item *item_col,
86                                                       Grob *left_object)
87 {
88   vector<Grob*> br, ra;
89   vector<Grob*> *which = 0;
90
91   Accidental_placement::split_accidentals (me, &br, &ra);
92   concat (br, ra);
93
94   if (dynamic_cast<Item *> (left_object)->break_status_dir () == RIGHT)
95     which = &br;
96   else
97     which = &ra;
98
99   Interval extent;
100   for (vsize i = 0; i < which->size (); i++)
101     extent.unite (which->at (i)->extent (item_col, X_AXIS));
102
103   if (!extent.is_empty ())
104     {
105       Real p = robust_scm2double (me->get_property ("left-padding"), 0.2);
106       extent[LEFT] -= p;
107     }
108
109   return extent;
110 }
111
112 struct Accidental_placement_entry
113 {
114   Skyline left_skyline_;
115   Skyline right_skyline_;
116   Interval vertical_extent_;
117   vector<Box> extents_;
118   vector<Grob*> grobs_;
119   Real offset_;
120   int notename_;
121   Accidental_placement_entry ()
122   {
123     offset_ = 0.0;
124     notename_ = -1;
125   }
126 };
127
128 static Interval all_accidental_vertical_extent;
129 Real ape_priority (Accidental_placement_entry const *a)
130 {
131   return a->vertical_extent_[UP];
132 }
133
134 int ape_compare (Accidental_placement_entry *const &a,
135                  Accidental_placement_entry *const &b)
136 {
137   return sign (ape_priority (a) - ape_priority (b));
138 }
139
140 bool ape_less (Accidental_placement_entry *const &a,
141                Accidental_placement_entry *const &b)
142 {
143   return ape_priority (a) < ape_priority (b);
144 }
145
146 int ape_rcompare (Accidental_placement_entry *const &a,
147                   Accidental_placement_entry *const &b)
148 {
149   return -sign (ape_priority (a) - ape_priority (b));
150 }
151
152 /*
153   TODO: should favor
154
155   b
156   b
157
158   placement
159 */
160 void
161 stagger_apes (vector<Accidental_placement_entry*> *apes)
162 {
163   vector<Accidental_placement_entry*> asc = *apes;
164
165   vector_sort (asc, &ape_less);
166
167   apes->clear ();
168
169   int parity = 1;
170   for (vsize i = 0; i < asc.size ();)
171     {
172       Accidental_placement_entry *a = 0;
173       if (parity)
174         {
175           a = asc.back ();
176           asc.pop_back ();
177         }
178       else
179         a = asc[i++];
180
181       apes->push_back (a);
182       parity = !parity;
183     }
184
185   reverse (*apes);
186 }
187
188 /*
189   This routine computes placements of accidentals. During
190   add_accidental (), accidentals are already grouped by note, so that
191   octaves are placed above each other; they form columns. Then the
192   columns are sorted: the biggest columns go closest to the note.
193   Then the columns are spaced as closely as possible (using skyline
194   spacing).
195
196
197   TODO: more advanced placement. Typically, the accs should be placed
198   to form a C shape, like this
199
200
201   ##
202   b b
203   # #
204   b
205   b b
206
207   The naturals should be left of the C as well; they should
208   be separate accs.
209
210   Note that this placement problem looks NP hard, so we just use a
211   simple strategy, not an optimal choice.
212 */
213
214 /*
215   TODO: there should be more space in the following situation
216
217
218   Natural + downstem
219
220   *
221   *  |_
222   *  | |    X
223   *  |_|   |
224   *    |   |
225   *
226
227 */
228
229 MAKE_SCHEME_CALLBACK(Accidental_placement, calc_positioning_done, 1);
230 SCM
231 Accidental_placement::calc_positioning_done (SCM smob)
232 {
233   Grob *me = unsmob_grob (smob);
234   if (!me->is_live ())
235     return SCM_BOOL_T;
236
237   SCM accs = me->get_object ("accidental-grobs");
238   if (!scm_is_pair (accs))
239     return SCM_BOOL_T;
240
241   /*
242     TODO: there is a bug in this code. If two accs are on the same
243     Y-position, they share an Ape, and will be printed in overstrike.
244   */
245   vector<Accidental_placement_entry*> apes;
246   for (SCM s = accs; scm_is_pair (s); s = scm_cdr (s))
247     {
248       Accidental_placement_entry *ape = new Accidental_placement_entry;
249       ape->notename_ = scm_to_int (scm_caar (s));
250
251       for (SCM t = scm_cdar (s); scm_is_pair (t); t = scm_cdr (t))
252         ape->grobs_.push_back (unsmob_grob (scm_car (t)));
253
254       apes.push_back (ape);
255     }
256
257   Grob *common[] = {me, 0};
258
259   /*
260     First we must extract *all* pointers. We can only determine
261     extents if we're sure that we've found the right common refpoint
262   */
263   vector<Grob*> note_cols, heads;
264   for (vsize i = apes.size (); i--;)
265     {
266       Accidental_placement_entry *ape = apes[i];
267       for (vsize j = ape->grobs_.size (); j--;)
268         {
269           Grob *a = ape->grobs_[j];
270
271           if (common[Y_AXIS])
272             common[Y_AXIS] = common[Y_AXIS]->common_refpoint (a, Y_AXIS);
273           else
274             common[Y_AXIS] = a;
275
276           Grob *head = a->get_parent (Y_AXIS);
277
278           Grob *col = head->get_parent (X_AXIS);
279           if (Note_column::has_interface (col))
280             note_cols.push_back (col);
281           else
282             heads.push_back (head);
283         }
284     }
285
286   /*
287     This is a little kludgy: to get all notes, we look if there are
288     collisions as well.
289   */
290   for (vsize i = note_cols.size (); i--;)
291     {
292       Grob *c = note_cols[i]->get_parent (X_AXIS);
293       if (Note_collision_interface::has_interface (c))
294         {
295           extract_grob_set (c, "elements", gs);
296
297           concat (note_cols, gs);
298         }
299     }
300
301   for (vsize i = note_cols.size (); i--;)
302     concat (heads, extract_grob_array (note_cols[i], "note-heads"));
303
304   vector_sort (heads, less<Grob*> ());
305   uniq (heads);
306   common[Y_AXIS] = common_refpoint_of_array (heads, common[Y_AXIS], Y_AXIS);
307
308   for (vsize i = apes.size (); i--;)
309     {
310       Accidental_placement_entry *ape = apes[i];
311
312       for (vsize j = apes[i]->grobs_.size (); j--;)
313         {
314           Grob *a = apes[i]->grobs_[j];
315           vector<Box> boxes = Accidental_interface::accurate_boxes (a, common);
316
317           ape->extents_.insert (ape->extents_.end (), boxes.begin (), boxes.end ());
318         }
319       ape->left_skyline_ = Skyline (ape->extents_, Y_AXIS, LEFT);
320       ape->right_skyline_ = Skyline (ape->extents_, Y_AXIS, RIGHT);
321     }
322
323   Interval total;
324   for (vsize i = apes.size (); i--;)
325     {
326       Interval y;
327
328       for (vsize j = apes[i]->extents_.size (); j--;)
329         y.unite (apes[i]->extents_[j][Y_AXIS]);
330       apes[i]->vertical_extent_ = y;
331       total.unite (y);
332     }
333   all_accidental_vertical_extent = total;
334   stagger_apes (&apes);
335
336   Accidental_placement_entry *head_ape = new Accidental_placement_entry;
337   common[X_AXIS] = common_refpoint_of_array (heads, common[X_AXIS], X_AXIS);
338   
339   vector<Box> head_extents;
340   for (vsize i = heads.size (); i--;)
341     head_extents.push_back (Box (heads[i]->extent (common[X_AXIS], X_AXIS),
342                                  heads[i]->extent (common[Y_AXIS], Y_AXIS)));
343
344   vector<Grob *> stems;
345   for (vsize i = 0; i < heads.size  (); i++)
346     {
347       if (Grob *s = Rhythmic_head::get_stem (heads[i]))
348         stems.push_back (s);
349     }
350   
351   vector_sort (stems, less<Grob*> ());
352   uniq (stems);
353   for (vsize i = 0; i < stems.size (); i ++)
354     {
355       int very_large = INT_MAX;
356       
357       head_extents.push_back (Box (heads[i]->extent (common[X_AXIS], X_AXIS),
358                                    heads[i]->pure_height (common[Y_AXIS], 0, very_large)));
359     }
360
361   head_ape->left_skyline_ = Skyline (head_extents, Y_AXIS, LEFT);
362   head_ape->offset_ = 0.0;
363
364   Real padding = robust_scm2double (me->get_property ("padding"), 0.2);
365
366   Skyline left_skyline = head_ape->left_skyline_;
367   left_skyline.raise (-robust_scm2double (me->get_property ("right-padding"), 0))
368 ;
369   /*
370     Add accs entries right-to-left.
371   */
372   for (vsize i = apes.size (); i-- > 0;)
373     {
374       Real offset = -apes[i]->right_skyline_.distance (left_skyline);
375       if (isinf (offset))
376         offset = (i < apes.size () - 1) ? apes[i + 1]->offset_ : 0.0;
377       else
378         offset -= padding;
379
380       apes[i]->offset_ = offset;
381
382       Skyline new_left_skyline = apes[i]->left_skyline_;
383       new_left_skyline.raise (apes[i]->offset_);
384       new_left_skyline.merge (left_skyline);
385       left_skyline = new_left_skyline;
386     }
387
388   for (vsize i = apes.size (); i--;)
389     {
390       Accidental_placement_entry *ape = apes[i];
391       for (vsize j = ape->grobs_.size (); j--;)
392         ape->grobs_[j]->translate_axis (ape->offset_, X_AXIS);
393     }
394
395   Interval left_extent, right_extent;
396   Accidental_placement_entry *ape = apes[0];
397
398   for (vsize i = ape->extents_.size (); i--;)
399     left_extent.unite (ape->offset_ + ape->extents_[i][X_AXIS]);
400
401   ape = apes.back ();
402   for (vsize i = ape->extents_.size (); i--;)
403     right_extent.unite (ape->offset_ + ape->extents_[i][X_AXIS]);
404
405   left_extent[LEFT] -= robust_scm2double (me->get_property ("left-padding"), 0);
406   Interval width (left_extent[LEFT], right_extent[RIGHT]);
407
408   SCM scm_width = ly_interval2scm (width);
409   me->flush_extent_cache (X_AXIS);
410   me->set_property ("X-extent", scm_width);
411
412   for (vsize i = apes.size (); i--;)
413     delete apes[i];
414
415   return SCM_BOOL_T;
416 }
417
418 ADD_INTERFACE (Accidental_placement,
419                "Resolve accidental collisions.",
420
421                /* properties */
422                "accidental-grobs "
423                "left-padding "
424                "padding "
425                "positioning-done "
426                "right-padding "
427                "script-priority "
428                )