2 rest-collision.cc -- implement Rest_collision
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 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"));
32 if (rc
&& !to_boolean (rc
->get_grob_property ("rest-collision-done")))
34 rc
->set_grob_property ("rest-collision-done", SCM_BOOL_T
);
39 return gh_double2scm (0.0);
43 Rest_collision::add_column (Grob
*me
,Grob
*p
)
45 me
->add_dependency (p
);
46 Pointer_group_interface::add_grob (me
, ly_symbol2scm ("elements"), p
);
49 only add callback for the rests, since we don't move anything
54 p
->add_offset_callback (Rest_collision::force_shift_callback_proc
, Y_AXIS
);
55 p
->set_grob_property ("rest-collision", me
->self_scm ());
60 Combination of dot-count and duration-log.
63 head_characteristic (Grob
* col
)
65 Grob
* s
= unsmob_grob (col
->get_grob_property ("rest"));
70 return gh_cons (s
->get_grob_property ("duration-log"),
71 gh_int2scm (Rhythmic_head::dot_count (s
)));
75 TODO: fixme, fucks up if called twice on the same set of rests.
77 TODO: look at horizontal-shift to determine ordering between rests
78 for more than two voices.
80 TODO: look at previous note to determine vertical position?
84 Rest_collision::do_shift (Grob
*me
)
86 SCM elts
= me
->get_grob_property ("elements");
88 Link_array
<Grob
> rests
;
89 Link_array
<Grob
> notes
;
91 for (SCM s
= elts
; gh_pair_p (s
); s
= ly_cdr (s
))
93 Grob
* e
= unsmob_grob (ly_car (s
));
94 if (unsmob_grob (e
->get_grob_property ("rest")))
102 handle rest-rest and rest-note collisions
105 * decide not to print rest if too crowded?
107 * ignore rests under beams.
110 // no rests to collide
112 return SCM_UNSPECIFIED
;
114 // no partners to collide with
115 if (rests
.size () + notes
.size () < 2)
116 return SCM_UNSPECIFIED
;
118 // meisjes met meisjes
121 SCM characteristic
= head_characteristic (rests
[0]);
123 for (; i
< rests
.size (); i
++)
125 if (!gh_equal_p (head_characteristic (rests
[i
]), characteristic
))
130 If all durations are the same, we'll check if there are more
131 rests than maximum-rest-count.
132 Otherwise (different durations), we'll try to display them all
133 (urg: all 3 of them, currently).
136 SCM s
= me
->get_grob_property ("maximum-rest-count");
137 if (i
== rests
.size ()
138 && gh_number_p (s
) && gh_scm2int (s
) < rests
.size ())
140 display_count
= gh_scm2int (s
);
141 for (; i
> display_count
; i
--)
143 Grob
* r
= unsmob_grob (rests
[i
-1]->get_grob_property ("rest"));
147 Grob
* d
= unsmob_grob (r
->get_grob_property ("dot"));
152 rests
[i
-1]->suicide ();
157 r
->set_grob_property ("transparent", gh_bool2scm(1));
158 r
= unsmob_grob (r
->get_grob_property ("dot"));
160 r
->set_grob_property ("transparent", gh_bool2scm(1));
166 display_count
= rests
.size ();
169 Ugh. Should have minimum dist.
171 Ugh. What do we do if we have three different rests?
174 int dy
= display_count
> 2 ? 6 : 4; // FIXME Should get dims from table.
175 if (display_count
> 1)
177 Direction d0
= Note_column::dir (rests
[0]);
178 Direction d1
= Note_column::dir (rests
[1]);
190 Note_column::translate_rests (rests
[0],d0
*dy
);
191 Note_column::translate_rests (rests
[1], d1
*dy
);
194 // meisjes met jongetjes
197 if (rests
.size () > 1)
199 warning (_ ("too many colliding rests"));
201 Grob
* rcol
= rests
[0];
202 Direction dir
= Note_column::dir (rests
[0]);
206 dir
= - Note_column::dir (notes
[0]);
208 Grob
* r
= unsmob_grob (rcol
->get_grob_property ("rest"));
209 Interval restdim
= r
->extent (r
, Y_AXIS
); // ??
211 if (restdim
.empty_b ())
212 return SCM_UNSPECIFIED
;
215 Real staff_space
= Staff_symbol_referencer::staff_space (rcol
);
217 Real minimum_dist
= gh_scm2double (me
->get_grob_property ("minimum-distance")) * staff_space
;
220 Grob
*common
= common_refpoint_of_array (notes
, rcol
, Y_AXIS
);
223 for (int i
= 0; i
< notes
.size (); i
++)
225 notedim
.unite (notes
[i
]->extent (common
, Y_AXIS
));
228 Interval
inter (notedim
);
229 inter
.intersect (restdim
);
232 minimum_dist
+ dir
* (notedim
[dir
] - restdim
[-dir
]) >? 0;
234 int stafflines
= Staff_symbol_referencer::line_count (me
);
237 programming_error ("No staff line count ? ");
241 // move discretely by half spaces.
242 int discrete_dist
= int (ceil (dist
/ (0.5 *staff_space
)));
244 // move by whole spaces inside the staff.
245 if (discrete_dist
< stafflines
+1)
246 discrete_dist
= int (ceil (discrete_dist
/ 2.0)* 2.0);
248 Note_column::translate_rests (rcol
,dir
* discrete_dist
);
250 return SCM_UNSPECIFIED
;
254 ADD_INTERFACE (Rest_collision
,"rest-collision-interface",
255 "Move around ordinary rests (not multi-measure-rests) to avoid "
257 "maximum-rest-count minimum-distance rest-collision-done elements");