2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2012 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 "note-collision.hh"
22 #include "axis-group-interface.hh"
23 #include "dot-column.hh"
24 #include "international.hh"
25 #include "note-column.hh"
26 #include "note-head.hh"
27 #include "output-def.hh"
28 #include "pointer-group-interface.hh"
30 #include "rhythmic-head.hh"
31 #include "staff-symbol-referencer.hh"
32 #include "side-position-interface.hh"
37 check_meshing_chords (Grob *me,
38 Drul_array<vector<Real> > *offsets,
39 Drul_array<vector<Slice> > const &extents,
40 Drul_array<vector<Grob *> > const &clash_groups)
43 if (!extents[UP].size () || !extents[DOWN].size ())
46 Grob *clash_up = clash_groups[UP][0];
47 Grob *clash_down = clash_groups[DOWN][0];
49 /* Every note column should have a stem, but avoid a crash. */
50 if (!Note_column::get_stem (clash_up) || !Note_column::get_stem (clash_down))
53 Drul_array<Grob *> stems (Note_column::get_stem (clash_down),
54 Note_column::get_stem (clash_up));
56 Grob *head_up = Note_column::first_head (clash_up);
57 Grob *head_down = Note_column::first_head (clash_down);
59 /* Staff-positions of all noteheads on each stem */
60 vector<int> ups = Stem::note_head_positions (stems[UP]);
61 vector<int> dps = Stem::note_head_positions (stems[DOWN]);
63 /* Too far apart to collide. */
64 if (ups[0] > dps.back () + 1)
67 /* If the chords just 'touch' their extreme noteheads,
68 then we can align their stems.
71 if (ups[0] >= dps.back ()
72 && (dps.size () < 2 || ups[0] >= dps[dps.size () - 2] + 2)
73 && (ups.size () < 2 || ups[1] >= dps.back () + 2))
76 /* Filter out the 'o's in this configuration, since they're no
77 * part in the collision.
85 ups = Stem::note_head_positions (stems[UP], true);
86 dps = Stem::note_head_positions (stems[DOWN], true);
88 /* Merge heads if the notes lie the same line, or if the "stem-up-note" is
89 above the "stem-down-note". */
90 bool merge_possible = (ups[0] >= dps[0]) && (ups.back () >= dps.back ());
92 /* Do not merge notes typeset in different style. */
93 if (!ly_is_equal (head_up->get_property ("style"),
94 head_down->get_property ("style")))
95 merge_possible = false;
97 int up_ball_type = Rhythmic_head::duration_log (head_up);
98 int down_ball_type = Rhythmic_head::duration_log (head_down);
100 /* Do not merge whole notes (or longer, like breve, longa, maxima). */
101 if (merge_possible && (up_ball_type <= 0 || down_ball_type <= 0))
102 merge_possible = false;
105 && Rhythmic_head::dot_count (head_up) != Rhythmic_head::dot_count (head_down)
106 && !to_boolean (me->get_property ("merge-differently-dotted")))
107 merge_possible = false;
109 /* Can only merge different heads if merge-differently-headed is set. */
111 && up_ball_type != down_ball_type
112 && !to_boolean (me->get_property ("merge-differently-headed")))
113 merge_possible = false;
115 /* Should never merge quarter and half notes, as this would make
116 them indistinguishable. */
118 && ((Stem::duration_log (stems[UP]) == 1
119 && Stem::duration_log (stems[DOWN]) == 2)
120 || (Stem::duration_log (stems[UP]) == 2
121 && Stem::duration_log (stems[DOWN]) == 1)))
122 merge_possible = false;
125 * this case (distant half collide),
132 * the noteheads may be closer than this case (close half collide)
143 bool close_half_collide = false;
144 bool distant_half_collide = false;
145 bool full_collide = false;
147 for (vsize i = 0, j = 0; i < ups.size () && j < dps.size ();)
149 if (abs (ups[i] - dps[j]) == 1)
151 merge_possible = false;
153 close_half_collide = true;
155 distant_half_collide = true;
157 else if (ups[i] == dps[j])
159 else if (ups[i] > dps[0] && ups[i] < dps.back ())
160 merge_possible = false;
161 else if (dps[j] > ups[0] && dps[j] < ups.back ())
162 merge_possible = false;
166 else if (ups[i] > dps[j])
175 full_collide = full_collide || (close_half_collide
176 && distant_half_collide);
178 /* Determine which chord goes on the left, and which goes right.
179 Up-stem usually goes on the right, but if chords just 'touch' we can put
180 both stems on a common vertical line. In the presense of collisions,
181 right hand heads may obscure dots, so dotted heads to go the right.
183 Real shift_amount = 1;
184 bool stem_to_stem = false;
186 || ((close_half_collide || distant_half_collide)
187 && to_boolean (me->get_property ("prefer-dotted-right"))))
188 && Rhythmic_head::dot_count (head_up) < Rhythmic_head::dot_count (head_down))
192 // remember to leave clearance between stems
197 // Up-stem note on a line has a raised dot, so no risk of collision
198 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
200 || (!Staff_symbol_referencer::on_line (staff, ups[0])
201 && to_boolean (me->get_property ("prefer-dotted-right"))))
202 && Rhythmic_head::dot_count (head_up) > Rhythmic_head::dot_count (head_down))
208 /* The solfa is a triangle, which is inverted depending on stem
209 direction. In case of a collision, one of them should be removed,
210 so the resulting note does not look like a block.
212 SCM up_style = head_up->get_property ("style");
213 SCM down_style = head_down->get_property ("style");
215 && (up_style == ly_symbol2scm ("fa") || up_style == ly_symbol2scm ("faThin"))
216 && (down_style == ly_symbol2scm ("fa") || down_style == ly_symbol2scm ("faThin")))
218 Offset att = Offset (0.0, -1.0);
219 head_up->set_property ("stem-attachment", ly_offset2scm (att));
220 head_up->set_property ("transparent", SCM_BOOL_T);
227 /* If possible, don't wipe any heads. Else, wipe shortest head,
228 or head with smallest amount of dots. Note: when merging
229 different heads, dots on the smaller one disappear. */
231 Grob *dot_wipe_head = head_up;
233 if (up_ball_type == down_ball_type)
235 if (Rhythmic_head::dot_count (head_down) < Rhythmic_head::dot_count (head_up))
237 wipe_ball = head_down;
238 dot_wipe_head = head_down;
240 else if (Rhythmic_head::dot_count (head_down) > Rhythmic_head::dot_count (head_up))
242 dot_wipe_head = head_up;
246 dot_wipe_head = head_up;
248 else if (down_ball_type > up_ball_type)
250 wipe_ball = head_down;
251 dot_wipe_head = head_down;
253 else if (down_ball_type < up_ball_type)
256 dot_wipe_head = head_up;
258 If upper head is eighth note or shorter, and lower head is half note,
259 shift by the difference between the open and filled note head widths,
260 otherwise upper stem will be misaligned slightly.
262 if (Stem::duration_log (stems[DOWN]) == 1
263 && Stem::duration_log (stems[UP]) >= 3)
264 shift_amount = (1 - head_up->extent (head_up, X_AXIS).length ()
265 / head_down->extent (head_down, X_AXIS).length ()) * 0.5;
270 if (Grob *d = unsmob_grob (dot_wipe_head->get_object ("dot")))
274 if (wipe_ball && wipe_ball->is_live ())
275 wipe_ball->set_property ("transparent", SCM_BOOL_T);
277 /* TODO: these numbers are magic; should devise a set of grob props
278 to tune this behavior. */
279 else if (stem_to_stem)
280 shift_amount *= 0.65;
283 else if (close_half_collide)
284 shift_amount *= 0.52;
285 else if (full_collide)
287 else if (distant_half_collide)
291 else if (Rhythmic_head::dot_count (head_up) || Rhythmic_head::dot_count (head_down))
294 shift_amount *= 0.17;
299 && down_ball_type *up_ball_type == 0)
301 if (up_ball_type == 0 && down_ball_type == 1)
302 shift_amount *= 1.25;
303 else if (up_ball_type == 0 && down_ball_type == 2)
304 shift_amount *= 1.35;
305 else if (down_ball_type == 0 && up_ball_type == 1)
307 else if (down_ball_type == 0 && up_ball_type == 2)
308 shift_amount *= 0.75;
311 /* If any dotted notes ended up on the left,
312 tell the Dot_Columnn to avoid the note heads on the right.
314 if (shift_amount < -1e-6
315 && Rhythmic_head::dot_count (head_up))
317 Grob *d = unsmob_grob (head_up->get_object ("dot"));
318 Grob *parent = d->get_parent (X_AXIS);
319 if (Dot_column::has_interface (parent))
320 Side_position_interface::add_support (parent, head_down);
322 else if (Rhythmic_head::dot_count (head_down))
324 Grob *d = unsmob_grob (head_down->get_object ("dot"));
325 Grob *parent = d->get_parent (X_AXIS);
326 if (Dot_column::has_interface (parent))
328 Grob *stem = unsmob_grob (head_up->get_object ("stem"));
329 // Loop over all heads on an up-pointing-stem to see if dots
330 // need to clear any heads suspended on its right side.
331 extract_grob_set (stem, "note-heads", heads);
332 for (vsize i = 0; i < heads.size (); i++)
333 Side_position_interface::add_support (parent, heads[i]);
337 // In meshed chords with dots on the left, adjust dot direction
338 if (shift_amount > 1e-6
339 && Rhythmic_head::dot_count (head_down))
341 Grob *dot_down = unsmob_grob (head_down->get_object ("dot"));
342 Grob *col_down = dot_down->get_parent (X_AXIS);
344 if (Rhythmic_head::dot_count (head_up))
346 Grob *dot_up = unsmob_grob (head_up->get_object ("dot"));
347 Grob *col_up = dot_up->get_parent (X_AXIS);
348 if (col_up == col_down) // let the common DotColumn arrange dots
350 else // conform to the dot direction on the up-stem chord
351 dir = robust_scm2dir (dot_up->get_property ("direction"), UP);
355 Grob *stem = unsmob_grob (head_down->get_object ("stem"));
356 extract_grob_set (stem, "note-heads", heads);
357 for (vsize i = 0; i < heads.size (); i++)
358 unsmob_grob (heads[i]->get_object ("dot"))
359 ->set_property ("direction", scm_from_int (dir));
366 for (vsize i = 0; i < clash_groups[d].size (); i++)
367 (*offsets)[d][i] += d * shift_amount;
369 while ((flip (&d)) != UP);
372 MAKE_SCHEME_CALLBACK (Note_collision_interface, calc_positioning_done, 1)
374 Note_collision_interface::calc_positioning_done (SCM smob)
376 Grob *me = unsmob_grob (smob);
377 me->set_property ("positioning-done", SCM_BOOL_T);
379 Drul_array<vector<Grob *> > clash_groups = get_clash_groups (me);
384 for (vsize i = clash_groups[d].size (); i--;)
389 clash_groups[d][i]->extent (me, X_AXIS);
392 while (flip (&d) != UP);
394 SCM autos (automatic_shift (me, clash_groups));
395 SCM hand (forced_shift (me));
400 if (clash_groups[d].size ())
402 Grob *h = clash_groups[d][0];
403 Grob *fh = Note_column::first_head (h);
405 wid = fh->extent (h, X_AXIS).length ();
408 while (flip (&d) != UP);
411 Real left_most = 1e6;
413 vector<Real> amounts;
414 for (; scm_is_pair (hand); hand = scm_cdr (hand))
416 Grob *s = unsmob_grob (scm_caar (hand));
417 Real amount = scm_to_double (scm_cdar (hand)) * wid;
420 amounts.push_back (amount);
421 if (amount < left_most)
424 for (; scm_is_pair (autos); autos = scm_cdr (autos))
426 Grob *s = unsmob_grob (scm_caar (autos));
427 Real amount = scm_to_double (scm_cdar (autos)) * wid;
429 vsize x = find (done, s) - done.begin ();
430 if (x == VPOS || x >= done.size ())
433 amounts.push_back (amount);
434 if (amount < left_most)
439 for (vsize i = 0; i < amounts.size (); i++)
440 done[i]->translate_axis (amounts[i] - left_most, X_AXIS);
445 Drul_array < vector<Grob *> >
446 Note_collision_interface::get_clash_groups (Grob *me)
448 Drul_array<vector<Grob *> > clash_groups;
450 extract_grob_set (me, "elements", elements);
451 for (vsize i = 0; i < elements.size (); i++)
453 Grob *se = elements[i];
454 if (Note_column::has_interface (se))
456 if (!Note_column::dir (se))
457 se->programming_error ("note-column has no direction");
459 clash_groups[Note_column::dir (se)].push_back (se);
466 vector<Grob *> &clashes (clash_groups[d]);
467 vector_sort (clashes, Note_column::shift_less);
469 while ((flip (&d)) != UP);
475 This complicated routine moves note columns around horizontally to
476 ensure that notes don't clash.
479 Note_collision_interface::automatic_shift (Grob *me,
480 Drul_array<vector<Grob *> > clash_groups)
482 Drul_array < vector<int> > shifts;
488 vector<int> &shift (shifts[d]);
489 vector<Grob *> &clashes (clash_groups[d]);
491 for (vsize i = 0; i < clashes.size (); i++)
494 = clashes[i]->get_property ("horizontal-shift");
496 if (scm_is_number (sh))
497 shift.push_back (scm_to_int (sh));
502 for (vsize i = 1; i < shift.size (); i++)
504 if (shift[i - 1] == shift[i])
506 clashes[0]->warning (_ ("ignoring too many clashing note columns"));
511 while ((flip (&d)) != UP);
513 Drul_array<vector<Slice> > extents;
514 Drul_array<vector<Real> > offsets;
518 for (vsize i = 0; i < clash_groups[d].size (); i++)
520 Slice s (Note_column::head_positions_interval (clash_groups[d][i]));
523 extents[d].push_back (s);
524 offsets[d].push_back (d * 0.5 * i);
527 while ((flip (&d)) != UP);
530 * do horizontal shifts of each direction
540 for (vsize i = 1; i < clash_groups[d].size (); i++)
542 Slice prev = extents[d][i - 1];
543 prev.intersect (extents[d][i]);
544 if (prev.length () > 0
545 || (extents[-d].size () && d * (extents[d][i][-d] - extents[-d][0][d]) < 0))
546 for (vsize j = i; j < clash_groups[d].size (); j++)
547 offsets[d][j] += d * 0.5;
550 while ((flip (&d)) != UP);
553 see input/regression/dot-up-voice-collision.ly
555 for (vsize i = 0; i < clash_groups[UP].size (); i++)
557 Grob *g = clash_groups[UP][i];
558 Grob *dc = Note_column::dot_column (g);
561 for (vsize j = i + 1; j < clash_groups[UP].size (); j++)
563 Grob *stem = Note_column::get_stem (clash_groups[UP][j]);
564 Side_position_interface::add_support (dc, stem);
569 Check if chords are meshing
572 check_meshing_chords (me, &offsets, extents, clash_groups);
576 for (vsize i = 0; i < clash_groups[d].size (); i++)
577 tups = scm_cons (scm_cons (clash_groups[d][i]->self_scm (),
578 scm_from_double (offsets[d][i])),
581 while (flip (&d) != UP);
587 Note_collision_interface::forced_shift (Grob *me)
591 extract_grob_set (me, "elements", elements);
592 for (vsize i = 0; i < elements.size (); i++)
594 Grob *se = elements[i];
596 SCM force = se->get_property ("force-hshift");
597 if (scm_is_number (force))
598 tups = scm_cons (scm_cons (se->self_scm (), force),
605 Note_collision_interface::add_column (Grob *me, Grob *ncol)
607 ncol->set_property ("X-offset", Grob::x_parent_positioning_proc);
608 Axis_group_interface::add_element (me, ncol);
611 ADD_INTERFACE (Note_collision_interface,
612 "An object that handles collisions between notes with"
613 " different stem directions and horizontal shifts. Most of"
614 " the interesting properties are to be set in"
615 " @ref{note-column-interface}: these are @code{force-hshift}"
616 " and @code{horizontal-shift}.",
619 "merge-differently-dotted "
620 "merge-differently-headed "
622 "prefer-dotted-right "