Use scalar instead of embedded_scm for context mod overrides.
[lilypond/mpolesky.git] / lily / accidental-placement.cc
blob02f0a2a6094c00d30bd2148d624dd82f5da137ff
1 /*
2 accidental-placement.cc -- implement Accidental_placement
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
10 #include "accidental-placement.hh"
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 "pointer-group-interface.hh"
19 #include "skyline.hh"
20 #include "stream-event.hh"
21 #include "warn.hh"
23 static Pitch*
24 accidental_pitch (Grob *acc)
26 SCM cause = acc->get_parent (Y_AXIS)->get_property ("cause");
28 Stream_event *mcause = unsmob_stream_event (cause);
29 if (!mcause)
31 programming_error ("note head has no event cause");
32 return 0;
35 return unsmob_pitch (mcause->get_property ("pitch"));
38 void
39 Accidental_placement::add_accidental (Grob *me, Grob *a)
41 Pitch *p = accidental_pitch (a);
42 if (!p)
43 return;
45 a->set_parent (me, X_AXIS);
46 a->set_property ("X-offset", Grob::x_parent_positioning_proc);
47 int n = p->get_notename ();
49 SCM accs = me->get_object ("accidental-grobs");
50 SCM key = scm_from_int (n);
51 SCM entry = scm_assq (key, accs);
52 if (entry == SCM_BOOL_F)
53 entry = SCM_EOL;
54 else
55 entry = scm_cdr (entry);
57 entry = scm_cons (a->self_scm (), entry);
59 accs = scm_assq_set_x (accs, key, entry);
61 me->set_object ("accidental-grobs", accs);
65 Split into break reminders.
67 void
68 Accidental_placement::split_accidentals (Grob *accs,
69 vector<Grob*> *break_reminder,
70 vector<Grob*> *real_acc)
72 for (SCM acs = accs->get_object ("accidental-grobs"); scm_is_pair (acs);
73 acs = scm_cdr (acs))
74 for (SCM s = scm_cdar (acs); scm_is_pair (s); s = scm_cdr (s))
76 Grob *a = unsmob_grob (scm_car (s));
78 if (unsmob_grob (a->get_object ("tie"))
79 && !to_boolean (a->get_property ("forced")))
80 break_reminder->push_back (a);
81 else
82 real_acc->push_back (a);
86 vector<Grob*>
87 Accidental_placement::get_relevant_accidentals (vector<Grob*> const &elts, Grob *left)
89 vector<Grob*> br;
90 vector<Grob*> ra;
91 vector<Grob*> ret;
92 bool right = dynamic_cast<Item *> (left)->break_status_dir () == RIGHT;
94 for (vsize i = 0; i < elts.size (); i++)
96 split_accidentals (elts[i], &br, &ra);
98 ret.insert (ret.end (), ra.begin (), ra.end ());
100 if (right)
101 ret.insert (ret.end (), br.begin (), br.end ());
103 return ret;
106 struct Accidental_placement_entry
108 Skyline left_skyline_;
109 Skyline right_skyline_;
110 Interval vertical_extent_;
111 vector<Box> extents_;
112 vector<Grob*> grobs_;
115 Real ape_priority (Accidental_placement_entry const *a)
117 return a->vertical_extent_[UP];
120 bool ape_less (Accidental_placement_entry *const &a,
121 Accidental_placement_entry *const &b)
123 return ape_priority (a) < ape_priority (b);
127 This function provides a method for sorting accidentals that belong to the
128 same note. The accidentals that this function considers to be "smallest"
129 will be placed to the left of the "larger" accidentals.
131 Naturals are the largest (so that they don't get confused with cancellation
132 naturals); apart from that, we order according to the alteration (so
133 double-flats are the smallest).
135 Precondition: the accidentals are attached to NoteHeads of the same note
136 name -- the octaves, however, may be different.
138 static bool
139 acc_less (Grob *const &a, Grob *const &b)
141 Pitch *p = accidental_pitch (a);
142 Pitch *q = accidental_pitch (b);
144 if (!p || !q)
146 programming_error ("these accidentals do not have a pitch");
147 return false;
150 if (p->get_octave () != q->get_octave ())
151 return p->get_octave () < q->get_octave ();
153 if (p->get_alteration () == Rational (0))
154 return false;
155 if (q->get_alteration () == Rational (0))
156 return true;
158 return p->get_alteration () < q->get_alteration ();
162 TODO: should favor
167 placement
169 void
170 stagger_apes (vector<Accidental_placement_entry*> *apes)
172 vector<Accidental_placement_entry*> asc = *apes;
174 vector_sort (asc, &ape_less);
176 apes->clear ();
178 int parity = 1;
179 for (vsize i = 0; i < asc.size ();)
181 Accidental_placement_entry *a = 0;
182 if (parity)
184 a = asc.back ();
185 asc.pop_back ();
187 else
188 a = asc[i++];
190 apes->push_back (a);
191 parity = !parity;
194 reverse (*apes);
197 static vector<Accidental_placement_entry*>
198 build_apes (SCM accs)
200 vector<Accidental_placement_entry*> apes;
201 for (SCM s = accs; scm_is_pair (s); s = scm_cdr (s))
203 Accidental_placement_entry *ape = new Accidental_placement_entry;
205 for (SCM t = scm_cdar (s); scm_is_pair (t); t = scm_cdr (t))
206 ape->grobs_.push_back (unsmob_grob (scm_car (t)));
208 apes.push_back (ape);
211 return apes;
214 static void
215 set_ape_skylines (Accidental_placement_entry *ape,
216 Grob **common)
218 vector<Grob*> accs (ape->grobs_);
219 vector_sort (accs, &acc_less);
221 /* We know that each accidental has the same note name and we assume that
222 accidentals in different octaves won't collide. If two or more
223 accidentals are in the same octave:
224 1) if they are the same accidental, print them in overstrike
225 2) otherwise, shift one to the left so they don't overlap. */
226 int last_octave = 0;
227 Real offset = 0;
228 Real last_offset = 0;
229 Rational last_alteration (0);
230 for (vsize i = accs.size (); i--;)
232 Grob *a = accs[i];
233 Pitch *p = accidental_pitch (a);
235 if (!p)
236 continue;
238 if (i == accs.size () - 1 || p->get_octave () != last_octave)
240 last_offset = 0;
241 offset = a->extent (a, X_AXIS)[LEFT] - 0.2;
243 else if (p->get_alteration () == last_alteration)
244 a->translate_axis (last_offset, X_AXIS);
245 else /* Our alteration is different from the last one */
247 Real this_offset = offset - a->extent (a, X_AXIS)[RIGHT];
248 a->translate_axis (this_offset, X_AXIS);
250 /* FIXME: read the padding from the AccidentalPlacement grob */
251 last_offset = this_offset;
252 offset -= a->extent (a, X_AXIS).length () + 0.2;
255 vector<Box> boxes = Accidental_interface::accurate_boxes (a, common);
256 ape->extents_.insert (ape->extents_.end (), boxes.begin (), boxes.end ());
258 for (vsize j = boxes.size (); j--;)
259 ape->vertical_extent_.unite (boxes[j][Y_AXIS]);
261 last_octave = p->get_octave ();
262 last_alteration = p->get_alteration ();
264 ape->left_skyline_ = Skyline (ape->extents_, 0, Y_AXIS, LEFT);
265 ape->right_skyline_ = Skyline (ape->extents_, 0, Y_AXIS, RIGHT);
268 static vector<Grob*>
269 extract_heads_and_stems (vector<Accidental_placement_entry*> const &apes)
271 vector<Grob*> note_cols;
272 vector<Grob*> ret;
274 for (vsize i = apes.size (); i--;)
276 Accidental_placement_entry *ape = apes[i];
277 for (vsize j = ape->grobs_.size (); j--;)
279 Grob *acc = ape->grobs_[j];
280 Grob *head = acc->get_parent (Y_AXIS);
281 Grob *col = head->get_parent (X_AXIS);
283 if (Note_column::has_interface (col))
284 note_cols.push_back (col);
285 else
286 ret.push_back (head);
291 This is a little kludgy: in case there are note columns without
292 accidentals, we get them from the Note_collision objects.
294 for (vsize i = note_cols.size (); i--;)
296 Grob *c = note_cols[i]->get_parent (X_AXIS);
297 if (Note_collision_interface::has_interface (c))
299 extract_grob_set (c, "elements", columns);
300 concat (note_cols, columns);
304 /* Now that we have all of the columns, grab all of the note-heads */
305 for (vsize i = note_cols.size (); i--;)
306 concat (ret, extract_grob_array (note_cols[i], "note-heads"));
308 /* Now that we have all of the heads, grab all of the stems */
309 for (vsize i = ret.size (); i--;)
310 if (Grob *s = Rhythmic_head::get_stem (ret[i]))
311 ret.push_back (s);
314 vector_sort (ret, less<Grob*> ());
315 uniq (ret);
316 return ret;
319 static Grob*
320 common_refpoint_of_accidentals (vector<Accidental_placement_entry*> const &apes, Axis a)
322 Grob *ret = 0;
324 for (vsize i = apes.size (); i--;)
325 for (vsize j = apes[i]->grobs_.size (); j--;)
327 if (!ret)
328 ret = apes[i]->grobs_[j];
329 else
330 ret = ret->common_refpoint (apes[i]->grobs_[j], a);
333 return ret;
336 static Skyline
337 build_heads_skyline (vector<Grob*> const &heads_and_stems,
338 Grob **common)
340 vector<Box> head_extents;
341 for (vsize i = heads_and_stems.size (); i--;)
342 head_extents.push_back (Box (heads_and_stems[i]->extent (common[X_AXIS], X_AXIS),
343 heads_and_stems[i]->pure_height (common[Y_AXIS], 0, INT_MAX)));
345 return Skyline (head_extents, 0, Y_AXIS, LEFT);
349 Position the apes, starting from the right, so that they don't collide.
350 Return the total width.
352 static Interval
353 position_apes (Grob *me,
354 vector<Accidental_placement_entry*> const &apes,
355 Skyline const &heads_skyline)
357 Real padding = robust_scm2double (me->get_property ("padding"), 0.2);
358 Skyline left_skyline = heads_skyline;
359 left_skyline.raise (-robust_scm2double (me->get_property ("right-padding"), 0));
362 Add accs entries right-to-left.
364 Interval width;
365 Real last_offset = 0.0;
366 for (vsize i = apes.size (); i-- > 0;)
368 Accidental_placement_entry *ape = apes[i];
370 Real offset = -ape->right_skyline_.distance (left_skyline);
371 if (isinf (offset))
372 offset = last_offset;
373 else
374 offset -= padding;
376 Skyline new_left_skyline = ape->left_skyline_;
377 new_left_skyline.raise (offset);
378 new_left_skyline.merge (left_skyline);
379 left_skyline = new_left_skyline;
381 /* Shift all of the accidentals in this ape */
382 for (vsize j = ape->grobs_.size (); j--;)
383 ape->grobs_[j]->translate_axis (offset, X_AXIS);
385 for (vsize j = ape->extents_.size (); j--;)
386 width.unite (offset + ape->extents_[j][X_AXIS]);
388 last_offset = offset;
391 return width;
396 This routine computes placements of accidentals. During
397 add_accidental (), accidentals are already grouped by note, so that
398 octaves are placed above each other; they form columns. Then the
399 columns are sorted: the biggest columns go closest to the note.
400 Then the columns are spaced as closely as possible (using skyline
401 spacing).
404 TODO: more advanced placement. Typically, the accs should be placed
405 to form a C shape, like this
414 The naturals should be left of the C as well; they should
415 be separate accs.
417 Note that this placement problem looks NP hard, so we just use a
418 simple strategy, not an optimal choice.
422 TODO: there should be more space in the following situation
425 Natural + downstem
428 * |_
429 * | | X
430 * |_| |
431 * | |
436 MAKE_SCHEME_CALLBACK (Accidental_placement, calc_positioning_done, 1);
438 Accidental_placement::calc_positioning_done (SCM smob)
440 Grob *me = unsmob_grob (smob);
441 if (!me->is_live ())
442 return SCM_BOOL_T;
444 me->set_property ("positioning-done", SCM_BOOL_T);
446 SCM accs = me->get_object ("accidental-grobs");
447 if (!scm_is_pair (accs))
448 return SCM_BOOL_T;
450 vector<Accidental_placement_entry*> apes = build_apes (accs);
452 Grob *common[] = {me, 0};
454 vector<Grob*> heads_and_stems = extract_heads_and_stems (apes);
456 common[Y_AXIS] = common_refpoint_of_accidentals (apes, Y_AXIS);
457 common[Y_AXIS] = common_refpoint_of_array (heads_and_stems, common[Y_AXIS], Y_AXIS);
458 common[X_AXIS] = common_refpoint_of_array (heads_and_stems, me, X_AXIS);
460 for (vsize i = apes.size (); i--;)
461 set_ape_skylines (apes[i], common);
462 Skyline heads_skyline = build_heads_skyline (heads_and_stems, common);
464 stagger_apes (&apes);
465 Interval width = position_apes (me, apes, heads_skyline);
467 me->flush_extent_cache (X_AXIS);
468 me->set_property ("X-extent", ly_interval2scm (width));
470 junk_pointers (apes);
472 return SCM_BOOL_T;
475 ADD_INTERFACE (Accidental_placement,
476 "Resolve accidental collisions.",
478 /* properties */
479 "accidental-grobs "
480 "direction "
481 "left-padding "
482 "padding "
483 "positioning-done "
484 "right-padding "
485 "script-priority "