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