2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2002--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "accidental-placement.hh"
22 #include "accidental-interface.hh"
25 #include "note-collision.hh"
26 #include "note-column.hh"
27 #include "pointer-group-interface.hh"
28 #include "rhythmic-head.hh"
30 #include "stream-event.hh"
34 accidental_pitch (Grob *acc)
36 SCM cause = acc->get_parent (Y_AXIS)->get_property ("cause");
38 Stream_event *mcause = unsmob_stream_event (cause);
41 programming_error ("note head has no event cause");
45 return unsmob_pitch (mcause->get_property ("pitch"));
49 Accidental_placement::add_accidental (Grob *me, Grob *a)
51 Pitch *p = accidental_pitch (a);
55 a->set_parent (me, X_AXIS);
56 a->set_property ("X-offset", Grob::x_parent_positioning_proc);
57 int n = p->get_notename ();
59 SCM accs = me->get_object ("accidental-grobs");
60 SCM key = scm_from_int (n);
61 SCM entry = scm_assq (key, accs);
62 if (entry == SCM_BOOL_F)
65 entry = scm_cdr (entry);
67 entry = scm_cons (a->self_scm (), entry);
69 accs = scm_assq_set_x (accs, key, entry);
71 me->set_object ("accidental-grobs", accs);
75 Split into break reminders.
78 Accidental_placement::split_accidentals (Grob *accs,
79 vector<Grob *> *break_reminder,
80 vector<Grob *> *real_acc)
82 for (SCM acs = accs->get_object ("accidental-grobs"); scm_is_pair (acs);
84 for (SCM s = scm_cdar (acs); scm_is_pair (s); s = scm_cdr (s))
86 Grob *a = unsmob_grob (scm_car (s));
88 if (unsmob_grob (a->get_object ("tie"))
89 && !to_boolean (a->get_property ("forced")))
90 break_reminder->push_back (a);
92 real_acc->push_back (a);
97 Accidental_placement::get_relevant_accidentals (vector<Grob *> const &elts, Grob *left)
102 bool right = dynamic_cast<Item *> (left)->break_status_dir () == RIGHT;
104 for (vsize i = 0; i < elts.size (); i++)
106 split_accidentals (elts[i], &br, &ra);
108 ret.insert (ret.end (), ra.begin (), ra.end ());
111 ret.insert (ret.end (), br.begin (), br.end ());
116 struct Accidental_placement_entry
118 Skyline left_skyline_;
119 Skyline right_skyline_;
120 Interval vertical_extent_;
121 vector<Box> extents_;
122 vector<Grob *> grobs_;
125 Real ape_priority (Accidental_placement_entry const *a)
127 return a->vertical_extent_[UP];
130 bool ape_less (Accidental_placement_entry *const &a,
131 Accidental_placement_entry *const &b)
133 return ape_priority (a) < ape_priority (b);
137 This function provides a method for sorting accidentals that belong to the
138 same note. The accidentals that this function considers to be "smallest"
139 will be placed to the left of the "larger" accidentals.
141 Naturals are the largest (so that they don't get confused with cancellation
142 naturals); apart from that, we order according to the alteration (so
143 double-flats are the smallest).
145 Precondition: the accidentals are attached to NoteHeads of the same note
146 name -- the octaves, however, may be different.
149 acc_less (Grob *const &a, Grob *const &b)
151 Pitch *p = accidental_pitch (a);
152 Pitch *q = accidental_pitch (b);
156 programming_error ("these accidentals do not have a pitch");
160 if (p->get_octave () != q->get_octave ())
161 return p->get_octave () < q->get_octave ();
163 if (p->get_alteration () == Rational (0))
165 if (q->get_alteration () == Rational (0))
168 return p->get_alteration () < q->get_alteration ();
180 stagger_apes (vector<Accidental_placement_entry *> *apes)
182 vector<Accidental_placement_entry *> asc = *apes;
184 vector_sort (asc, &ape_less);
189 for (vsize i = 0; i < asc.size ();)
191 Accidental_placement_entry *a = 0;
207 static vector<Accidental_placement_entry *>
208 build_apes (SCM accs)
210 vector<Accidental_placement_entry *> apes;
211 for (SCM s = accs; scm_is_pair (s); s = scm_cdr (s))
213 Accidental_placement_entry *ape = new Accidental_placement_entry;
215 for (SCM t = scm_cdar (s); scm_is_pair (t); t = scm_cdr (t))
216 ape->grobs_.push_back (unsmob_grob (scm_car (t)));
218 apes.push_back (ape);
225 set_ape_skylines (Accidental_placement_entry *ape,
228 vector<Grob *> accs (ape->grobs_);
229 vector_sort (accs, &acc_less);
231 /* We know that each accidental has the same note name and we assume that
232 accidentals in different octaves won't collide. If two or more
233 accidentals are in the same octave:
234 1) if they are the same accidental, print them in overstrike
235 2) otherwise, shift one to the left so they don't overlap. */
238 Real last_offset = 0;
239 Rational last_alteration (0);
240 for (vsize i = accs.size (); i--;)
243 Pitch *p = accidental_pitch (a);
248 if (i == accs.size () - 1 || p->get_octave () != last_octave)
251 offset = a->extent (a, X_AXIS)[LEFT] - 0.2;
253 else if (p->get_alteration () == last_alteration)
254 a->translate_axis (last_offset, X_AXIS);
255 else /* Our alteration is different from the last one */
257 Real this_offset = offset - a->extent (a, X_AXIS)[RIGHT];
258 a->translate_axis (this_offset, X_AXIS);
260 /* FIXME: read the padding from the AccidentalPlacement grob */
261 last_offset = this_offset;
262 offset -= a->extent (a, X_AXIS).length () + 0.2;
265 vector<Box> boxes = Accidental_interface::accurate_boxes (a, common);
266 ape->extents_.insert (ape->extents_.end (), boxes.begin (), boxes.end ());
268 for (vsize j = boxes.size (); j--;)
269 ape->vertical_extent_.unite (boxes[j][Y_AXIS]);
271 last_octave = p->get_octave ();
272 last_alteration = p->get_alteration ();
274 ape->left_skyline_ = Skyline (ape->extents_, 0, Y_AXIS, LEFT);
275 ape->right_skyline_ = Skyline (ape->extents_, 0, Y_AXIS, RIGHT);
278 static vector<Grob *>
279 extract_heads_and_stems (vector<Accidental_placement_entry *> const &apes)
281 vector<Grob *> note_cols;
284 for (vsize i = apes.size (); i--;)
286 Accidental_placement_entry *ape = apes[i];
287 for (vsize j = ape->grobs_.size (); j--;)
289 Grob *acc = ape->grobs_[j];
290 Grob *head = acc->get_parent (Y_AXIS);
291 Grob *col = head->get_parent (X_AXIS);
293 if (Note_column::has_interface (col))
294 note_cols.push_back (col);
296 ret.push_back (head);
301 This is a little kludgy: in case there are note columns without
302 accidentals, we get them from the Note_collision objects.
304 for (vsize i = note_cols.size (); i--;)
306 Grob *c = note_cols[i]->get_parent (X_AXIS);
307 if (Note_collision_interface::has_interface (c))
309 extract_grob_set (c, "elements", columns);
310 concat (note_cols, columns);
314 /* Now that we have all of the columns, grab all of the note-heads */
315 for (vsize i = note_cols.size (); i--;)
316 concat (ret, extract_grob_array (note_cols[i], "note-heads"));
318 /* Now that we have all of the heads, grab all of the stems */
319 for (vsize i = ret.size (); i--;)
320 if (Grob *s = Rhythmic_head::get_stem (ret[i]))
323 vector_sort (ret, less<Grob *> ());
329 common_refpoint_of_accidentals (vector<Accidental_placement_entry *> const &apes, Axis a)
333 for (vsize i = apes.size (); i--;)
334 for (vsize j = apes[i]->grobs_.size (); j--;)
337 ret = apes[i]->grobs_[j];
339 ret = ret->common_refpoint (apes[i]->grobs_[j], a);
346 build_heads_skyline (vector<Grob *> const &heads_and_stems,
349 vector<Box> head_extents;
350 for (vsize i = heads_and_stems.size (); i--;)
351 head_extents.push_back (Box (heads_and_stems[i]->extent (common[X_AXIS], X_AXIS),
352 heads_and_stems[i]->pure_height (common[Y_AXIS], 0, INT_MAX)));
354 return Skyline (head_extents, 0, Y_AXIS, LEFT);
358 Position the apes, starting from the right, so that they don't collide.
359 Return the total width.
362 position_apes (Grob *me,
363 vector<Accidental_placement_entry *> const &apes,
364 Skyline const &heads_skyline)
366 Real padding = robust_scm2double (me->get_property ("padding"), 0.2);
367 Skyline left_skyline = heads_skyline;
368 left_skyline.raise (-robust_scm2double (me->get_property ("right-padding"), 0));
371 Add accs entries right-to-left.
374 Real last_offset = 0.0;
375 for (vsize i = apes.size (); i-- > 0;)
377 Accidental_placement_entry *ape = apes[i];
379 Real offset = -ape->right_skyline_.distance (left_skyline);
381 offset = last_offset;
385 Skyline new_left_skyline = ape->left_skyline_;
386 new_left_skyline.raise (offset);
387 new_left_skyline.merge (left_skyline);
388 left_skyline = new_left_skyline;
390 /* Shift all of the accidentals in this ape */
391 for (vsize j = ape->grobs_.size (); j--;)
392 ape->grobs_[j]->translate_axis (offset, X_AXIS);
394 for (vsize j = ape->extents_.size (); j--;)
395 width.unite (offset + ape->extents_[j][X_AXIS]);
397 last_offset = offset;
404 This routine computes placements of accidentals. During
405 add_accidental (), accidentals are already grouped by note, so that
406 octaves are placed above each other; they form columns. Then the
407 columns are sorted: the biggest columns go closest to the note.
408 Then the columns are spaced as closely as possible (using skyline
412 TODO: more advanced placement. Typically, the accs should be placed
413 to form a C shape, like this
421 The naturals should be left of the C as well; they should
424 Note that this placement problem looks NP hard, so we just use a
425 simple strategy, not an optimal choice.
429 TODO: there should be more space in the following situation
443 MAKE_SCHEME_CALLBACK (Accidental_placement, calc_positioning_done, 1);
445 Accidental_placement::calc_positioning_done (SCM smob)
447 Grob *me = unsmob_grob (smob);
451 me->set_property ("positioning-done", SCM_BOOL_T);
453 SCM accs = me->get_object ("accidental-grobs");
454 if (!scm_is_pair (accs))
457 vector<Accidental_placement_entry *> apes = build_apes (accs);
459 Grob *common[] = {me, 0};
461 vector<Grob *> heads_and_stems = extract_heads_and_stems (apes);
463 common[Y_AXIS] = common_refpoint_of_accidentals (apes, Y_AXIS);
464 common[Y_AXIS] = common_refpoint_of_array (heads_and_stems, common[Y_AXIS], Y_AXIS);
465 common[X_AXIS] = common_refpoint_of_array (heads_and_stems, me, X_AXIS);
467 for (vsize i = apes.size (); i--;)
468 set_ape_skylines (apes[i], common);
469 Skyline heads_skyline = build_heads_skyline (heads_and_stems, common);
471 stagger_apes (&apes);
472 Interval width = position_apes (me, apes, heads_skyline);
474 me->flush_extent_cache (X_AXIS);
475 me->set_property ("X-extent", ly_interval2scm (width));
477 junk_pointers (apes);
482 ADD_INTERFACE (Accidental_placement,
483 "Resolve accidental collisions.",