2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 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 vector<int> ups = Stem::note_head_positions (Note_column::get_stem (clash_up));
60 vector<int> dps = Stem::note_head_positions (Note_column::get_stem (clash_down));
62 /* Too far apart to collide. */
63 if (ups[0] > dps.back () + 1)
66 /* Merge heads if the notes lie the same line, or if the "stem-up-note" is
67 above the "stem-down-note". */
68 bool merge_possible = (ups[0] >= dps[0]) && (ups.back () >= dps.back ());
70 /* Do not merge notes typeset in different style. */
71 if (!ly_is_equal (head_up->get_property ("style"),
72 head_down->get_property ("style")))
73 merge_possible = false;
75 int up_ball_type = Rhythmic_head::duration_log (head_up);
76 int down_ball_type = Rhythmic_head::duration_log (head_down);
78 /* Do not merge whole notes (or longer, like breve, longa, maxima). */
79 if (merge_possible && (up_ball_type <= 0 || down_ball_type <= 0))
80 merge_possible = false;
83 && Rhythmic_head::dot_count (head_up) != Rhythmic_head::dot_count (head_down)
84 && !to_boolean (me->get_property ("merge-differently-dotted")))
85 merge_possible = false;
87 /* Can only merge different heads if merge-differently-headed is set. */
89 && up_ball_type != down_ball_type
90 && !to_boolean (me->get_property ("merge-differently-headed")))
91 merge_possible = false;
93 /* Should never merge quarter and half notes, as this would make
94 them indistinguishable. */
96 && ((Stem::duration_log (stems[UP]) == 1
97 && Stem::duration_log (stems[DOWN]) == 2)
98 || (Stem::duration_log (stems[UP]) == 2
99 && Stem::duration_log (stems[DOWN]) == 1)))
100 merge_possible = false;
103 this case (distant half collide),
110 the noteheads may be closer than this case (close half collide)
121 /* TODO: filter out the 'o's in this configuration, since they're no
122 part in the collision.
131 bool close_half_collide = false;
132 bool distant_half_collide = false;
133 bool full_collide = false;
135 for (vsize i = 0, j = 0; i < ups.size () && j < dps.size ();)
137 if (abs (ups[i] - dps[j]) == 1)
139 merge_possible = false;
141 close_half_collide = true;
143 distant_half_collide = true;
145 else if (ups[i] == dps[j])
147 else if (ups[i] > dps[0] && ups[i] < dps.back ())
148 merge_possible = false;
149 else if (dps[j] > ups[0] && dps[j] < ups.back ())
150 merge_possible = false;
154 else if (ups[i] > dps[j])
163 full_collide = full_collide || (close_half_collide
164 && distant_half_collide);
166 Real shift_amount = 1;
168 bool touch = (ups[0] >= dps.back ());
169 /* As a special case, if the topmost part of the downstem chord is a second,
170 the top note of which is the same pitch as the lowest upstem note, they
171 shouldn't count as touching.
173 if (dps.back () == ups[0] && dps.size () > 1 && dps[dps.size () - 2] == ups[0] - 1)
179 /* For full collisions, the right hand head may obscure dots, so
180 make sure the dotted heads go to the right. */
181 bool stem_to_stem = false;
184 if (Rhythmic_head::dot_count (head_up) > Rhythmic_head::dot_count (head_down))
186 else if (Rhythmic_head::dot_count (head_up) < Rhythmic_head::dot_count (head_down))
190 /* The solfa is a triangle, which is inverted depending on stem
191 direction. In case of a collision, one of them should be removed,
192 so the resulting note does not look like a block.
194 SCM up_style = head_up->get_property ("style");
195 SCM down_style = head_down->get_property ("style");
197 && (up_style == ly_symbol2scm ("fa") || up_style == ly_symbol2scm ("faThin"))
198 && (down_style == ly_symbol2scm ("fa") || down_style == ly_symbol2scm ("faThin")))
200 Interval uphead_size = head_up->extent (head_up, Y_AXIS);
201 Offset att = Offset (0.0, -1.0);
202 head_up->set_property ("stem-attachment", ly_offset2scm (att));
203 head_up->set_property ("transparent", SCM_BOOL_T);
210 /* If possible, don't wipe any heads. Else, wipe shortest head,
211 or head with smallest amount of dots. Note: when merging
212 different heads, dots on the smaller one disappear. */
214 Grob *dot_wipe_head = head_up;
216 if (up_ball_type == down_ball_type)
218 if (Rhythmic_head::dot_count (head_down) < Rhythmic_head::dot_count (head_up))
220 wipe_ball = head_down;
221 dot_wipe_head = head_down;
223 else if (Rhythmic_head::dot_count (head_down) > Rhythmic_head::dot_count (head_up))
225 dot_wipe_head = head_up;
229 dot_wipe_head = head_up;
231 else if (down_ball_type > up_ball_type)
233 wipe_ball = head_down;
234 dot_wipe_head = head_down;
236 else if (down_ball_type < up_ball_type)
239 dot_wipe_head = head_up;
241 If upper head is eighth note or shorter, and lower head is half note,
242 shift by the difference between the open and filled note head widths,
243 otherwise upper stem will be misaligned slightly.
245 if (Stem::duration_log (stems[DOWN]) == 1
246 && Stem::duration_log (stems[UP]) >= 3)
247 shift_amount = (1 - head_up->extent (head_up, X_AXIS).length ()
248 / head_down->extent (head_down, X_AXIS).length ()) * 0.5;
253 if (Grob *d = unsmob_grob (dot_wipe_head->get_object ("dot")))
257 if (wipe_ball && wipe_ball->is_live ())
258 wipe_ball->set_property ("transparent", SCM_BOOL_T);
260 /* TODO: these numbers are magic; should devise a set of grob props
261 to tune this behavior. */
262 else if (stem_to_stem)
263 shift_amount = -abs (shift_amount) * 0.65;
264 else if (close_half_collide && !touch)
265 shift_amount *= 0.52;
266 else if (distant_half_collide && !touch)
268 else if (distant_half_collide || close_half_collide || full_collide)
272 else if (Rhythmic_head::dot_count (head_up) || Rhythmic_head::dot_count (head_down))
275 shift_amount *= 0.17;
280 && down_ball_type *up_ball_type == 0)
282 if (up_ball_type == 0 && down_ball_type == 1)
283 shift_amount *= 1.25;
284 else if (up_ball_type == 0 && down_ball_type == 2)
285 shift_amount *= 1.35;
286 else if (down_ball_type == 0 && up_ball_type == 1)
288 else if (down_ball_type == 0 && up_ball_type == 2)
289 shift_amount *= 0.75;
295 * Dots from left note head collide with right note head. Only occurs
296 * with a close half collide, if the left note head is between
297 * lines and the right note head is on a line, and if right note head
298 * hasn't got any dots.
300 if (close_half_collide
301 && Rhythmic_head::dot_count (head_up)
302 && !Rhythmic_head::dot_count (head_down))
304 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
305 if (!Staff_symbol_referencer::on_line (staff, ups[0]))
308 TODO: consider junking the else body.
310 if (to_boolean (me->get_property ("prefer-dotted-right")))
314 Grob *d = unsmob_grob (head_up->get_object ("dot"));
315 Grob *parent = d->get_parent (X_AXIS);
316 if (Dot_column::has_interface (parent))
317 Side_position_interface::add_support (parent, head_down);
322 /* For full or close half collisions, the right hand head may
323 obscure dots. Move dots to the right. */
324 if (abs (shift_amount) > 1e-6
325 && Rhythmic_head::dot_count (head_down) > Rhythmic_head::dot_count (head_up)
326 && (full_collide || close_half_collide))
328 Grob *d = unsmob_grob (head_down->get_object ("dot"));
329 Grob *parent = d->get_parent (X_AXIS);
339 the . is put right of o which is erroneous o force-shifted
342 if (Dot_column::has_interface (parent))
344 Grob *stem = unsmob_grob (head_up->get_object ("stem"));
345 extract_grob_set (stem, "note-heads", heads);
346 for (vsize i = 0; i < heads.size (); i++)
347 Side_position_interface::add_support (parent, heads[i]);
354 for (vsize i = 0; i < clash_groups[d].size (); i++)
355 (*offsets)[d][i] += d * shift_amount;
357 while ((flip (&d)) != UP);
360 MAKE_SCHEME_CALLBACK (Note_collision_interface, calc_positioning_done, 1)
362 Note_collision_interface::calc_positioning_done (SCM smob)
364 Grob *me = unsmob_grob (smob);
365 me->set_property ("positioning-done", SCM_BOOL_T);
367 Drul_array<vector<Grob *> > clash_groups = get_clash_groups (me);
372 for (vsize i = clash_groups[d].size (); i--;)
377 clash_groups[d][i]->extent (me, X_AXIS);
380 while (flip (&d) != UP);
382 SCM autos (automatic_shift (me, clash_groups));
383 SCM hand (forced_shift (me));
388 if (clash_groups[d].size ())
390 Grob *h = clash_groups[d][0];
391 Grob *fh = Note_column::first_head (h);
393 wid = fh->extent (h, X_AXIS).length ();
396 while (flip (&d) != UP);
399 Real left_most = 1e6;
401 vector<Real> amounts;
402 for (; scm_is_pair (hand); hand = scm_cdr (hand))
404 Grob *s = unsmob_grob (scm_caar (hand));
405 Real amount = scm_to_double (scm_cdar (hand)) * wid;
408 amounts.push_back (amount);
409 if (amount < left_most)
412 for (; scm_is_pair (autos); autos = scm_cdr (autos))
414 Grob *s = unsmob_grob (scm_caar (autos));
415 Real amount = scm_to_double (scm_cdar (autos)) * wid;
417 vsize x = find (done, s) - done.begin ();
418 if (x == VPOS || x >= done.size ())
421 amounts.push_back (amount);
422 if (amount < left_most)
427 for (vsize i = 0; i < amounts.size (); i++)
428 done[i]->translate_axis (amounts[i] - left_most, X_AXIS);
433 Drul_array < vector<Grob *> >
434 Note_collision_interface::get_clash_groups (Grob *me)
436 Drul_array<vector<Grob *> > clash_groups;
438 extract_grob_set (me, "elements", elements);
439 for (vsize i = 0; i < elements.size (); i++)
441 Grob *se = elements[i];
442 if (Note_column::has_interface (se))
444 if (!Note_column::dir (se))
445 se->programming_error ("note-column has no direction");
447 clash_groups[Note_column::dir (se)].push_back (se);
454 vector<Grob *> &clashes (clash_groups[d]);
455 vector_sort (clashes, Note_column::shift_less);
457 while ((flip (&d)) != UP);
463 This complicated routine moves note columns around horizontally to
464 ensure that notes don't clash.
467 Note_collision_interface::automatic_shift (Grob *me,
468 Drul_array<vector<Grob *> > clash_groups)
470 Drul_array < vector<int> > shifts;
476 vector<int> &shift (shifts[d]);
477 vector<Grob *> &clashes (clash_groups[d]);
479 for (vsize i = 0; i < clashes.size (); i++)
482 = clashes[i]->get_property ("horizontal-shift");
484 if (scm_is_number (sh))
485 shift.push_back (scm_to_int (sh));
490 for (vsize i = 1; i < shift.size (); i++)
492 if (shift[i - 1] == shift[i])
494 clashes[0]->warning (_ ("ignoring too many clashing note columns"));
499 while ((flip (&d)) != UP);
501 Drul_array<vector<Slice> > extents;
502 Drul_array<vector<Real> > offsets;
506 for (vsize i = 0; i < clash_groups[d].size (); i++)
508 Slice s (Note_column::head_positions_interval (clash_groups[d][i]));
511 extents[d].push_back (s);
512 offsets[d].push_back (d * 0.5 * i);
515 while ((flip (&d)) != UP);
518 do horizontal shifts of each direction
528 for (vsize i = 1; i < clash_groups[d].size (); i++)
530 Slice prev = extents[d][i - 1];
531 prev.intersect (extents[d][i]);
532 if (prev.length () > 0
533 || (extents[-d].size () && d * (extents[d][i][-d] - extents[-d][0][d]) < 0))
534 for (vsize j = i; j < clash_groups[d].size (); j++)
535 offsets[d][j] += d * 0.5;
538 while ((flip (&d)) != UP);
541 see input/regression/dot-up-voice-collision.ly
543 for (vsize i = 0; i < clash_groups[UP].size (); i++)
545 Grob *g = clash_groups[UP][i];
546 Grob *dc = Note_column::dot_column (g);
549 for (vsize j = i + 1; j < clash_groups[UP].size (); j++)
551 Grob *stem = Note_column::get_stem (clash_groups[UP][j]);
552 Side_position_interface::add_support (dc, stem);
557 Check if chords are meshing
560 check_meshing_chords (me, &offsets, extents, clash_groups);
564 for (vsize i = 0; i < clash_groups[d].size (); i++)
565 tups = scm_cons (scm_cons (clash_groups[d][i]->self_scm (),
566 scm_from_double (offsets[d][i])),
569 while (flip (&d) != UP);
575 Note_collision_interface::forced_shift (Grob *me)
579 extract_grob_set (me, "elements", elements);
580 for (vsize i = 0; i < elements.size (); i++)
582 Grob *se = elements[i];
584 SCM force = se->get_property ("force-hshift");
585 if (scm_is_number (force))
586 tups = scm_cons (scm_cons (se->self_scm (), force),
593 Note_collision_interface::add_column (Grob *me, Grob *ncol)
595 ncol->set_property ("X-offset", Grob::x_parent_positioning_proc);
596 Axis_group_interface::add_element (me, ncol);
599 ADD_INTERFACE (Note_collision_interface,
600 "An object that handles collisions between notes with"
601 " different stem directions and horizontal shifts. Most of"
602 " the interesting properties are to be set in"
603 " @ref{note-column-interface}: these are @code{force-hshift}"
604 " and @code{horizontal-shift}.",
607 "merge-differently-dotted "
608 "merge-differently-headed "
610 "prefer-dotted-right "