2 collision.cc -- implement Collision
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "note-collision.hh"
11 #include "axis-group-interface.hh"
12 #include "dot-column.hh"
13 #include "international.hh"
14 #include "note-column.hh"
15 #include "note-head.hh"
16 #include "output-def.hh"
17 #include "pointer-group-interface.hh"
18 #include "rhythmic-head.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "side-position-interface.hh"
26 check_meshing_chords (Grob
*me
,
27 Drul_array
<vector
<Real
> > *offsets
,
28 Drul_array
<vector
<Slice
> > const &extents
,
29 Drul_array
<vector
<Grob
*> > const &clash_groups
)
32 if (!extents
[UP
].size () || ! extents
[DOWN
].size ())
35 Grob
*cu
= clash_groups
[UP
][0];
36 Grob
*cd
= clash_groups
[DOWN
][0];
38 /* Every note column should have a stem, but avoid a crash. */
39 if (!Note_column::get_stem (cu
) || !Note_column::get_stem (cd
))
42 Drul_array
<Grob
*> stems (Note_column::get_stem (cd
),
43 Note_column::get_stem (cu
));
45 Grob
*nu
= Note_column::first_head (cu
);
46 Grob
*nd
= Note_column::first_head (cd
);
48 vector
<int> ups
= Stem::note_head_positions (Note_column::get_stem (cu
));
49 vector
<int> dps
= Stem::note_head_positions (Note_column::get_stem (cd
));
51 /* Too far apart to collide. */
52 if (ups
[0] > dps
.back () + 1)
55 /* Merge heads if the notes lie the same line, or if the "stem-up-note" is
56 above the "stem-down-note". */
57 bool merge_possible
= (ups
[0] >= dps
[0]) && (ups
.back () >= dps
.back ());
59 /* Do not merge notes typeset in different style. */
60 if (!ly_is_equal (nu
->get_property ("style"),
61 nd
->get_property ("style")))
62 merge_possible
= false;
64 int upball_type
= Rhythmic_head::duration_log (nu
);
65 int dnball_type
= Rhythmic_head::duration_log (nd
);
67 /* Do not merge whole notes (or longer, like breve, longa, maxima). */
68 if (merge_possible
&& (upball_type
<= 0 || dnball_type
<= 0))
69 merge_possible
= false;
72 && Rhythmic_head::dot_count (nu
) != Rhythmic_head::dot_count (nd
)
73 && !to_boolean (me
->get_property ("merge-differently-dotted")))
74 merge_possible
= false;
76 /* Can only merge different heads if merge-differently-headed is
79 && upball_type
!= dnball_type
80 && !to_boolean (me
->get_property ("merge-differently-headed")))
81 merge_possible
= false;
84 && nu
->get_property ("style") == ly_symbol2scm ("fa")
85 && nd
->get_property ("style") == ly_symbol2scm ("fa"))
87 Interval uphead_size
= nu
->extent (nu
, Y_AXIS
);
88 Offset att
= Offset (0.0, -1.0);
89 nu
->set_property ("stem-attachment", ly_offset2scm (att
));
90 nu
->set_property ("transparent", SCM_BOOL_T
);
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 Drul_array
<Real
> center_note_shifts
;
167 center_note_shifts
[LEFT
] = 0.0;
168 center_note_shifts
[RIGHT
] = 0.0;
170 Real shift_amount
= 1;
172 bool touch
= (ups
[0] >= dps
.back ());
176 /* For full collisions, the right hand head may obscure dots, so
177 make sure the dotted heads go to the right. */
178 bool stem_to_stem
= false;
180 if (Rhythmic_head::dot_count (nu
) > Rhythmic_head::dot_count (nd
))
182 else if (Rhythmic_head::dot_count (nu
) < Rhythmic_head::dot_count (nd
))
189 /* If possible, don't wipe any heads. Else, wipe shortest head,
190 or head with smallest amount of dots. Note: when merging
191 different heads, dots on the smaller one disappear. */
193 Grob
*dot_wipe_head
= nu
;
195 if (upball_type
== dnball_type
)
197 if (Rhythmic_head::dot_count (nd
) < Rhythmic_head::dot_count (nu
))
202 else if (Rhythmic_head::dot_count (nd
) > Rhythmic_head::dot_count (nu
))
210 else if (dnball_type
> upball_type
)
215 else if (dnball_type
< upball_type
)
223 if (Grob
*d
= unsmob_grob (dot_wipe_head
->get_object ("dot")))
227 if (wipe_ball
&& wipe_ball
->is_live ())
229 wipe_ball
->set_property ("transparent", SCM_BOOL_T
);
232 /* TODO: these numbers are magic; should devise a set of grob props
233 to tune this behavior. */
234 else if (stem_to_stem
)
235 shift_amount
= -abs (shift_amount
) * 0.65;
236 else if (close_half_collide
&& !touch
)
237 shift_amount
*= 0.52;
238 else if (distant_half_collide
&& !touch
)
240 else if (distant_half_collide
|| close_half_collide
|| full_collide
)
244 else if (Rhythmic_head::dot_count (nu
) || Rhythmic_head::dot_count (nd
))
247 shift_amount
*= 0.17;
252 * Dots from left note head collide with right note head. Only occurs
253 * with a close half collide, if the left note head is between
254 * lines and the right note head is on a line, and if right note head
255 * hasn't got any dots.
257 if (close_half_collide
258 && Rhythmic_head::dot_count (nu
)
259 && !Rhythmic_head::dot_count (nd
))
261 Grob
*staff
= Staff_symbol_referencer::get_staff_symbol (me
);
262 if (!Staff_symbol_referencer::on_line (staff
, ups
[0]))
264 Grob
*d
= unsmob_grob (nu
->get_object ("dot"));
265 Grob
*parent
= d
->get_parent (X_AXIS
);
266 if (Dot_column::has_interface (parent
))
267 Side_position_interface::add_support (parent
, nd
);
271 /* For full or close half collisions, the right hand head may
272 obscure dots. Move dots to the right. */
273 if (abs (shift_amount
) > 1e-6
274 && Rhythmic_head::dot_count (nd
) > Rhythmic_head::dot_count (nu
)
275 && (full_collide
|| close_half_collide
))
277 Grob
*d
= unsmob_grob (nd
->get_object ("dot"));
278 Grob
*parent
= d
->get_parent (X_AXIS
);
288 the . is put right of o which is erroneous o force-shifted
291 if (Dot_column::has_interface (parent
))
292 Side_position_interface::add_support (parent
, nu
);
298 for (vsize i
= 0; i
< clash_groups
[d
].size (); i
++)
299 (*offsets
)[d
][i
] += d
* shift_amount
;
301 while ((flip (&d
)) != UP
);
305 MAKE_SCHEME_CALLBACK(Note_collision_interface
, calc_positioning_done
, 1)
307 Note_collision_interface::calc_positioning_done (SCM smob
)
309 Grob
*me
= unsmob_grob (smob
);
310 Drul_array
<vector
<Grob
*> > cg
= get_clash_groups (me
);
315 for (vsize i
= cg
[d
].size(); i
--; )
320 cg
[d
][i
]->extent (me
, X_AXIS
);
323 while (flip (&d
) != UP
);
325 SCM
autos (automatic_shift (me
, cg
));
326 SCM
hand (forced_shift (me
));
334 Grob
*fh
= Note_column::first_head (h
);
336 wid
= fh
->extent (h
, X_AXIS
).length ();
339 while (flip (&d
) != UP
);
342 Real left_most
= 1e6
;
344 vector
<Real
> amounts
;
345 for (; scm_is_pair (hand
); hand
= scm_cdr (hand
))
347 Grob
*s
= unsmob_grob (scm_caar (hand
));
348 Real amount
= scm_to_double (scm_cdar (hand
)) * wid
;
351 amounts
.push_back (amount
);
352 if (amount
< left_most
)
355 for (; scm_is_pair (autos
); autos
= scm_cdr (autos
))
357 Grob
*s
= unsmob_grob (scm_caar (autos
));
358 Real amount
= scm_to_double (scm_cdar (autos
)) * wid
;
360 vsize x
= find (done
, s
) - done
.begin ();
361 if (x
== VPOS
|| x
>= done
.size ())
364 amounts
.push_back (amount
);
365 if (amount
< left_most
)
370 for (vsize i
= 0; i
< amounts
.size (); i
++)
371 done
[i
]->translate_axis (amounts
[i
] - left_most
, X_AXIS
);
376 Drul_array
< vector
<Grob
*> >
377 Note_collision_interface::get_clash_groups (Grob
*me
)
379 Drul_array
<vector
<Grob
*> > clash_groups
;
381 extract_grob_set (me
, "elements", elements
);
382 for (vsize i
= 0; i
< elements
.size (); i
++)
384 Grob
*se
= elements
[i
];
385 if (Note_column::has_interface (se
))
387 if (!Note_column::dir (se
))
389 se
->programming_error ("note-column has no direction");
392 clash_groups
[Note_column::dir (se
)].push_back (se
);
399 vector
<Grob
*> &clashes (clash_groups
[d
]);
400 vector_sort (clashes
, Note_column::shift_less
);
402 while ((flip (&d
)) != UP
);
407 /** This complicated routine moves note columns around horizontally to
408 ensure that notes don't clash.
410 This should be put into Scheme.
413 Note_collision_interface::automatic_shift (Grob
*me
,
414 Drul_array
< vector
<Grob
*>
417 Drul_array
< vector
<int> > shifts
;
423 vector
<int> &shift (shifts
[d
]);
424 vector
<Grob
*> &clashes (clash_groups
[d
]);
426 for (vsize i
= 0; i
< clashes
.size (); i
++)
429 = clashes
[i
]->get_property ("horizontal-shift");
431 if (scm_is_number (sh
))
432 shift
.push_back (scm_to_int (sh
));
437 for (vsize i
= 1; i
< shift
.size (); i
++)
439 if (shift
[i
- 1] == shift
[i
])
441 clashes
[0]->warning (_ ("ignoring too many clashing note columns"));
446 while ((flip (&d
)) != UP
);
448 Drul_array
<vector
<Slice
> > extents
;
449 Drul_array
<vector
<Real
> > offsets
;
453 for (vsize i
= 0; i
< clash_groups
[d
].size (); i
++)
455 Slice
s (Note_column::head_positions_interval (clash_groups
[d
][i
]));
458 extents
[d
].push_back (s
);
459 offsets
[d
].push_back (d
* 0.5 * i
);
462 while ((flip (&d
)) != UP
);
465 do horizontal shifts of each direction
475 for (vsize i
= 1; i
< clash_groups
[d
].size (); i
++)
477 Slice prev
= extents
[d
][i
- 1];
478 prev
.intersect (extents
[d
][i
]);
479 if (prev
.length () > 0
480 || (extents
[-d
].size () && d
* (extents
[d
][i
][-d
] - extents
[-d
][0][d
]) < 0))
481 for (vsize j
= i
; j
< clash_groups
[d
].size (); j
++)
482 offsets
[d
][j
] += d
* 0.5;
485 while ((flip (&d
)) != UP
);
488 Check if chords are meshing
491 check_meshing_chords (me
, &offsets
, extents
, clash_groups
);
495 for (vsize i
= 0; i
< clash_groups
[d
].size (); i
++)
496 tups
= scm_cons (scm_cons (clash_groups
[d
][i
]->self_scm (),
497 scm_from_double (offsets
[d
][i
])),
500 while (flip (&d
) != UP
);
506 Note_collision_interface::forced_shift (Grob
*me
)
510 extract_grob_set (me
, "elements", elements
);
511 for (vsize i
= 0; i
< elements
.size (); i
++)
513 Grob
*se
= elements
[i
];
515 SCM force
= se
->get_property ("force-hshift");
516 if (scm_is_number (force
))
518 tups
= scm_cons (scm_cons (se
->self_scm (), force
),
526 Note_collision_interface::add_column (Grob
*me
, Grob
*ncol
)
528 ncol
->set_property ("X-offset", Grob::x_parent_positioning_proc
);
529 Axis_group_interface::add_element (me
, ncol
);
532 ADD_INTERFACE (Note_collision_interface
, "note-collision-interface",
533 "An object that handles collisions between notes with different stem "
534 "directions and horizontal shifts. Most of the interesting properties "
535 "are to be set in @ref{note-column-interface}: these are "
536 "@code{force-hshift} and @code{horizontal-shift}.",
539 "merge-differently-dotted "
540 "merge-differently-headed "
541 "positioning-done ");