2 rest-collision.cc -- implement Rest_collision
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include <math.h> // ceil.
12 #include "rest-collision.hh"
13 #include "note-column.hh"
15 #include "rhythmic-head.hh"
16 #include "paper-def.hh"
18 #include "group-interface.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "duration.hh"
22 MAKE_SCHEME_CALLBACK (Rest_collision
,force_shift_callback
,2);
24 Rest_collision::force_shift_callback (SCM element_smob
, SCM axis
)
26 Grob
*them
= unsmob_grob (element_smob
);
27 Axis a
= (Axis
) gh_scm2int (axis
);
30 Grob
* rc
= unsmob_grob (them
->get_grob_property ("rest-collision"));
35 Done: destruct pointers, so we do the shift only once.
37 TODO: use rest-collision-done
39 SCM elts
= rc
->get_grob_property ("elements");
40 rc
->set_grob_property ("elements", SCM_EOL
);
45 return gh_double2scm (0.0);
49 Rest_collision::add_column (Grob
*me
,Grob
*p
)
51 me
->add_dependency (p
);
52 Pointer_group_interface::add_grob (me
, ly_symbol2scm ("elements"), p
);
55 only add callback for the rests, since we don't move anything else.
59 p
->add_offset_callback (Rest_collision::force_shift_callback_proc
, Y_AXIS
);
60 p
->set_grob_property ("rest-collision", me
->self_scm ());
65 Combination of dot-count and duration-log.
68 head_characteristic (Grob
* col
)
70 Grob
* s
= unsmob_grob (col
->get_grob_property ("rest"));
75 return gh_cons (s
->get_grob_property ("duration-log"),
76 gh_int2scm (Rhythmic_head::dot_count (s
)));
80 TODO: fixme, fucks up if called twice on the same set of rests.
82 TODO: look at horizontal-shift to determine ordering between rests
83 for more than two voices.
86 Rest_collision::do_shift (Grob
*me
, SCM elts
)
89 ugh. -> score elt type
91 Link_array
<Grob
> rests
;
92 Link_array
<Grob
> notes
;
94 for (SCM s
= elts
; gh_pair_p (s
); s
= ly_cdr (s
))
96 Grob
* e
= unsmob_grob (ly_car (s
));
97 if (unsmob_grob (e
->get_grob_property ("rest")))
105 handle rest-rest and rest-note collisions
108 * decide not to print rest if too crowded?
110 * ignore rests under beams.
113 // no rests to collide
115 return SCM_UNSPECIFIED
;
117 // no partners to collide with
118 if (rests
.size () + notes
.size () < 2)
119 return SCM_UNSPECIFIED
;
121 // meisjes met meisjes
124 SCM characteristic
= head_characteristic (rests
[0]);
126 for (; i
< rests
.size (); i
++)
128 if (!gh_equal_p (head_characteristic (rests
[i
]), characteristic
))
133 If all durations are the same, we'll check if there are more
134 rests than maximum-rest-count.
135 Otherwise (different durations), we'll try to display them all
136 (urg: all 3 of them, currently).
139 SCM s
= me
->get_grob_property ("maximum-rest-count");
140 if (i
== rests
.size ()
141 && gh_number_p (s
) && gh_scm2int (s
) < rests
.size ())
143 display_count
= gh_scm2int (s
);
144 for (; i
> display_count
; i
--)
146 Grob
* r
= unsmob_grob (rests
[i
-1]->get_grob_property ("rest"));
149 rests
[i
-1]->suicide ();
153 display_count
= rests
.size ();
156 Ugh. Should have minimum dist.
158 Ugh. What do we do if we have three different rests?
161 int dy
= display_count
> 2 ? 6 : 4; // FIXME Should get dims from table.
162 if (display_count
> 1)
164 Direction d0
= Note_column::dir (rests
[0]);
165 Direction d1
= Note_column::dir (rests
[1]);
177 Note_column::translate_rests (rests
[0],d0
*dy
);
178 Note_column::translate_rests (rests
[1], d1
*dy
);
181 // meisjes met jongetjes
184 if (rests
.size () > 1)
186 warning (_ ("too many colliding rests"));
188 Grob
* rcol
= rests
[0];
189 Direction dir
= Note_column::dir (rests
[0]);
193 dir
= - Note_column::dir (notes
[0]);
195 Grob
* r
= unsmob_grob (rcol
->get_grob_property ("rest"));
196 Interval restdim
= r
->extent (r
, Y_AXIS
); // ??
198 if (restdim
.empty_b ())
199 return SCM_UNSPECIFIED
;
202 Real staff_space
= Staff_symbol_referencer::staff_space (rcol
);
204 Real minimum_dist
= gh_scm2double (me
->get_grob_property ("minimum-distance")) * staff_space
;
207 Grob
*common
= common_refpoint_of_array (notes
, rcol
, Y_AXIS
);
210 for (int i
= 0; i
< notes
.size (); i
++)
212 notedim
.unite (notes
[i
]->extent (common
, Y_AXIS
));
215 Interval
inter (notedim
);
216 inter
.intersect (restdim
);
219 minimum_dist
+ dir
* (notedim
[dir
] - restdim
[-dir
]) >? 0;
221 int stafflines
= Staff_symbol_referencer::line_count (me
);
224 programming_error ("No staff line count ? ");
228 // move discretely by half spaces.
229 int discrete_dist
= int (ceil (dist
/ (0.5 *staff_space
)));
231 // move by whole spaces inside the staff.
232 if (discrete_dist
< stafflines
+1)
233 discrete_dist
= int (ceil (discrete_dist
/ 2.0)* 2.0);
235 Note_column::translate_rests (rcol
,dir
* discrete_dist
);
237 return SCM_UNSPECIFIED
;
241 ADD_INTERFACE (Rest_collision
,"rest-collision-interface",
242 "Move around ordinary rests (not multi-measure-rests) to avoid
244 "maximum-rest-count minimum-distance elements");