3 collision.cc -- implement Collision
5 source file of the GNU LilyPond music typesetter
7 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
11 #include "note-collision.hh"
12 #include "note-column.hh"
13 #include "note-head.hh"
14 #include "rhythmic-head.hh"
15 #include "paper-def.hh"
16 #include "axis-group-interface.hh"
20 MAKE_SCHEME_CALLBACK (Note_collision_interface
,force_shift_callback
,2);
23 Note_collision_interface::force_shift_callback (SCM element_smob
, SCM axis
)
25 Grob
*me
= unsmob_grob (element_smob
);
26 Axis a
= (Axis
) gh_scm2int (axis
);
29 me
= me
->get_parent (a
);
31 if (! to_boolean (me
->get_grob_property ("collision-done")))
33 me
->set_grob_property ("collision-done", SCM_BOOL_T
);
37 return gh_double2scm (0.0);
42 check_meshing_chords (Grob
*me
,
43 Drul_array
< Array
< Real
> > *offsets
,
44 Drul_array
< Array
< Slice
> > const &extents
,
45 Drul_array
<Link_array
<Grob
> > const &clash_groups
)
48 if (!extents
[UP
].size () || ! extents
[DOWN
].size ())
52 Grob
*cu
=clash_groups
[UP
][0];
53 Grob
*cd
=clash_groups
[DOWN
][0];
55 Grob
* nu
= Note_column::first_head (cu
);
56 Grob
* nd
= Note_column::first_head (cd
);
61 this case (distant half collide),
68 the noteheads may be closer than this case (close half collide)
79 bool close_half_collide
= false;
80 bool distant_half_collide
= false;
81 bool full_collide
= false;
86 if (!Note_column::get_stem (cu
)
87 || !Note_column::get_stem (cd
))
94 filter out the 'o's in this configuration, since they're no part
104 Array
<int> ups
= Stem::note_head_positions (Note_column::get_stem (cu
));
105 Array
<int> dps
= Stem::note_head_positions (Note_column::get_stem (cd
));
108 they're too far apart to collide.
112 if (ups
[0] > dps
.top () + 1)
115 bool touch
= (ups
[0] - dps
.top () >= 0);
117 bool merge_possible
= (ups
[0] >= dps
[0]) && (ups
.top () >= dps
.top ());
120 don't merge whole notes (or longer, like breve, longa, maxima)
123 int upball_type
= Note_head::get_balltype (nu
);
124 int dnball_type
= Note_head::get_balltype (nd
);
126 merge_possible
= merge_possible
&& (upball_type
> 0);
128 if (!to_boolean (me
->get_grob_property ("merge-differently-dotted")))
129 merge_possible
= merge_possible
&& Rhythmic_head::dot_count (nu
) == Rhythmic_head::dot_count (nd
);
132 if (!to_boolean (me
->get_grob_property ("merge-differently-headed")))
133 merge_possible
= merge_possible
&&
134 upball_type
== dnball_type
;
137 Can't merge quarter and half notes.
139 merge_possible
= merge_possible
&&
140 !((Rhythmic_head::duration_log (nu
) == 1
141 && Rhythmic_head::duration_log (nd
) == 2)
142 ||(Rhythmic_head::duration_log (nu
) == 2
143 && Rhythmic_head::duration_log (nd
) == 1));
146 while (i
< ups
.size () && j
< dps
.size ())
148 if (abs (ups
[i
] - dps
[j
]) == 1)
150 merge_possible
= false;
152 close_half_collide
= true;
154 distant_half_collide
= true;
156 else if (ups
[i
]==dps
[j
])
158 else if (ups
[i
] >dps
[0] && ups
[i
] < dps
.top ())
159 merge_possible
= false;
160 else if (dps
[j
] >ups
[0] && dps
[j
] < ups
.top ())
161 merge_possible
= false;
165 else if (ups
[i
] > dps
[j
])
174 Drul_array
<Real
> center_note_shifts
;
175 center_note_shifts
[LEFT
] = 0.0;
176 center_note_shifts
[RIGHT
] = 0.0;
179 Real shift_amount
= 1;
185 for full collisions, the right hand head may obscure dots, so
186 make sure the dotted heads go to the right.
188 if ((Rhythmic_head::dot_count (nu
) > Rhythmic_head::dot_count (nd
)
193 TODO: these numbers are magic; should devise a set of grob props
194 to tune this behavior. */
201 if (upball_type
< dnball_type
)
203 else if (upball_type
> dnball_type
)
206 if (wipe_ball
&& wipe_ball
->live ())
208 wipe_ball
->set_grob_property ("transparent", SCM_BOOL_T
);
209 wipe_ball
->set_grob_property ("molecule", SCM_EOL
);
212 else if (close_half_collide
&& !touch
)
213 shift_amount
*= 0.52;
214 else if (distant_half_collide
&& !touch
)
216 else if (distant_half_collide
|| close_half_collide
|| full_collide
)
222 else if (Rhythmic_head::dot_count (nu
) || Rhythmic_head::dot_count (nd
))
225 shift_amount
*= 0.25;
230 for (int i
=0; i
< clash_groups
[d
].size (); i
++)
231 (*offsets
)[d
][i
] += d
* shift_amount
;
233 while ((flip (&d
))!= UP
);
237 Note_collision_interface::do_shifts (Grob
* me
)
239 Drul_array
< Link_array
<Grob
> > cg
= get_clash_groups (me
);
241 SCM
autos (automatic_shift (me
, cg
));
242 SCM
hand (forced_shift (me
));
253 wid
= Note_column::first_head(h
)->extent(h
,X_AXIS
).length() ;
257 while (flip (&d
) != UP
);
260 Link_array
<Grob
> done
;
261 for (; gh_pair_p (hand
); hand
=ly_cdr (hand
))
263 Grob
* s
= unsmob_grob (ly_caar (hand
));
264 Real amount
= gh_scm2double (ly_cdar (hand
));
266 s
->translate_axis (amount
*wid
, X_AXIS
);
269 for (; gh_pair_p (autos
); autos
=ly_cdr (autos
))
271 Grob
* s
= unsmob_grob (ly_caar (autos
));
272 Real amount
= gh_scm2double (ly_cdar (autos
));
275 s
->translate_axis (amount
* wid
, X_AXIS
);
279 Drul_array
< Link_array
<Grob
> >
280 Note_collision_interface::get_clash_groups (Grob
*me
)
282 Drul_array
<Link_array
<Grob
> > clash_groups
;
284 SCM s
= me
->get_grob_property ("elements");
285 for (; gh_pair_p (s
); s
= ly_cdr (s
))
287 SCM car
= ly_car (s
);
289 Grob
* se
= unsmob_grob (car
);
290 if (Note_column::has_interface (se
))
291 clash_groups
[Note_column::dir (se
)].push (se
);
297 Link_array
<Grob
> & clashes (clash_groups
[d
]);
298 clashes
.sort (Note_column::shift_compare
);
300 while ((flip (&d
))!= UP
);
305 /** This complicated routine moves note columns around horizontally to
306 ensure that notes don't clash.
308 This should be put into Scheme.
311 Note_collision_interface::automatic_shift (Grob
*me
,
312 Drul_array
< Link_array
<Grob
> >
315 Drul_array
<Array
<int> > shifts
;
322 Array
<int> & shift (shifts
[d
]);
323 Link_array
<Grob
> & clashes (clash_groups
[d
]);
325 for (int i
=0; i
< clashes
.size (); i
++)
328 = clashes
[i
]->get_grob_property ("horizontal-shift");
330 if (gh_number_p (sh
))
331 shift
.push (gh_scm2int (sh
));
336 for (int i
=1; i
< shift
.size (); i
++)
338 if (shift
[i
-1] == shift
[i
])
340 clashes
[0]->warning (_ ("Too many clashing notecolumns. Ignoring them."));
345 while ((flip (&d
))!= UP
);
347 Drul_array
< Array
< Slice
> > extents
;
348 Drul_array
< Array
< Real
> > offsets
;
352 for (int i
=0; i
< clash_groups
[d
].size (); i
++)
354 Slice
s (Note_column::head_positions_interval (clash_groups
[d
][i
]));
358 offsets
[d
].push (d
* 0.5 * i
);
361 while ((flip (&d
))!= UP
);
364 do horizontal shifts of each direction
374 for (int i
=1; i
< clash_groups
[d
].size (); i
++)
376 Slice prev
=extents
[d
][i
-1];
377 prev
.intersect (extents
[d
][i
]);
378 if (prev
.length ()> 0 ||
379 (extents
[-d
].size () && d
* (extents
[d
][i
][-d
] - extents
[-d
][0][d
]) < 0))
380 for (int j
= i
; j
< clash_groups
[d
].size (); j
++)
381 offsets
[d
][j
] += d
* 0.5;
384 while ((flip (&d
))!= UP
);
388 Check if chords are meshing
391 check_meshing_chords (me
, &offsets
, extents
, clash_groups
);
395 for (int i
=0; i
< clash_groups
[d
].size (); i
++)
396 tups
= gh_cons (gh_cons (clash_groups
[d
][i
]->self_scm (), gh_double2scm (offsets
[d
][i
])),
399 while (flip (&d
) != UP
);
405 Note_collision_interface::forced_shift (Grob
*me
)
409 SCM s
= me
->get_grob_property ("elements");
410 for (; gh_pair_p (s
); s
= ly_cdr (s
))
412 Grob
* se
= unsmob_grob (ly_car (s
));
414 SCM force
= se
->get_grob_property ("force-hshift");
415 if (gh_number_p (force
))
417 tups
= gh_cons (gh_cons (se
->self_scm (), force
),
425 Note_collision_interface::add_column (Grob
*me
,Grob
* ncol
)
427 ncol
->add_offset_callback (Note_collision_interface::force_shift_callback_proc
, X_AXIS
);
428 Axis_group_interface::add_element (me
, ncol
);
429 me
->add_dependency (ncol
);
433 ADD_INTERFACE (Note_collision_interface
, "note-collision-interface",
434 "An object that handles collisions between notes with different stem "
435 "directions and horizontal shifts. Most of the interesting properties "
436 "are to be set in @ref{note-column-interface}: these are "
437 "@code{force-hshift} and @code{horizontal-shift}. ",
438 "merge-differently-dotted merge-differently-headed collision-done");