2 line-spanner.cc -- implement Line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2007 Jan Nieuwenhuizen <janneke@gnu.org>
9 #include "axis-group-interface.hh"
11 #include "output-def.hh"
13 #include "staff-symbol-referencer.hh"
14 #include "font-interface.hh"
16 #include "align-interface.hh"
17 #include "line-interface.hh"
21 #include "lily-proto.hh"
22 #include "grob-interface.hh"
23 #include "text-interface.hh"
28 DECLARE_SCHEME_CALLBACK (print, (SCM));
29 DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
30 DECLARE_SCHEME_CALLBACK (calc_left_bound_info, (SCM));
31 DECLARE_SCHEME_CALLBACK (calc_left_bound_info_and_text, (SCM));
32 DECLARE_SCHEME_CALLBACK (calc_right_bound_info, (SCM));
33 DECLARE_SCHEME_CALLBACK (calc_bound_info, (SCM, Direction));
34 DECLARE_GROB_INTERFACE ();
37 Spanner *parent_spanner (Grob *g)
39 if (Spanner::has_interface (g))
40 return dynamic_cast<Spanner*> (g);
41 return parent_spanner (g->get_parent (Y_AXIS));
45 Line_spanner::calc_bound_info (SCM smob, Direction dir)
47 Spanner *me = unsmob_spanner (smob);
49 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
50 commonx = me->common_refpoint (commonx, X_AXIS);
52 SCM bound_details = me->get_property ("bound-details");
54 SCM details = SCM_BOOL_F;
55 if (details == SCM_BOOL_F)
56 details = ly_assoc_get ((dir == LEFT)
57 ? ly_symbol2scm ("left")
58 : ly_symbol2scm ("right"), bound_details, SCM_BOOL_F);
60 if (me->get_bound (dir)->break_status_dir ())
62 SCM extra = ly_assoc_get ((dir == LEFT)
63 ? ly_symbol2scm ("left-broken")
64 : ly_symbol2scm ("right-broken"), bound_details, SCM_EOL);
66 for (SCM s = extra; scm_is_pair (s); s = scm_cdr (s))
67 details = scm_cons (scm_car (s), details);
70 if (details == SCM_BOOL_F)
71 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
73 SCM text = ly_assoc_get (ly_symbol2scm ("text"), details, SCM_BOOL_F);
74 if (Text_interface::is_markup (text))
76 Output_def *layout = me->layout ();
77 SCM properties = Font_interface::text_font_alist_chain (me);
78 details = scm_acons (ly_symbol2scm ("stencil"),
79 Text_interface::interpret_markup (layout->self_scm (),
84 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
86 Direction attach = (Direction)
87 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
91 details = scm_acons (ly_symbol2scm ("X"),
92 scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
93 .linear_combination (attach)),
98 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
102 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
105 Grob *common_y = me->common_refpoint (me->get_bound (dir), Y_AXIS);
106 if (me->get_bound (dir)->break_status_dir ())
108 Spanner *next_sp = me->broken_neighbor (dir);
109 Item *next_bound = next_sp->get_bound (dir);
111 if (next_bound->break_status_dir ())
113 programming_error ("no note heads for the line spanner on neighbor line?"
119 Spanner *next_bound_parent = parent_spanner (next_bound);
120 Interval next_ext = next_bound->extent (next_bound_parent, Y_AXIS);
122 /* We want to know what would be the
123 y-position of the next bound (relative to my y-parent) if it belonged
124 to the same system as this bound. We rely on the fact that
125 the y-parent of the next bound is a spanner (probably the
126 VerticalAxisGroup of a staff) that extends over the break.
128 Spanner *next_bound_parent_on_this_line =
129 next_bound_parent->broken_neighbor (other_dir (dir));
131 if (next_bound_parent_on_this_line)
133 Grob *common = me->common_refpoint (next_bound_parent_on_this_line, Y_AXIS);
134 Real bound_offset = next_bound_parent_on_this_line->relative_coordinate (common, Y_AXIS);
135 y = next_ext.center () + bound_offset - me->relative_coordinate (common, Y_AXIS);
139 /* We fall back to assuming that the distance between staves doesn't
140 change over line breaks. */
141 programming_error ("next-bound's parent doesn't extend to this line");
142 Grob *next_system = next_bound->get_system ();
143 Grob *this_system = me->get_system ();
144 y = next_ext.center () + next_bound_parent->relative_coordinate (next_system, Y_AXIS)
145 - me->relative_coordinate (this_system, Y_AXIS);
150 y = me->get_bound (dir)->extent (common_y, Y_AXIS).center ();
151 details = scm_acons (ly_symbol2scm ("common-Y"), common_y->self_scm (), details);
154 y += dir * extra_dy / 2;
155 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
161 MAKE_SCHEME_CALLBACK (Line_spanner, calc_right_bound_info, 1);
163 Line_spanner::calc_right_bound_info (SCM smob)
165 return Line_spanner::calc_bound_info (smob, RIGHT);
168 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info, 1);
170 Line_spanner::calc_left_bound_info (SCM smob)
172 return Line_spanner::calc_bound_info (smob, LEFT);
175 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info_and_text, 1);
177 Line_spanner::calc_left_bound_info_and_text (SCM smob)
179 SCM alist = Line_spanner::calc_bound_info (smob, LEFT);
180 Spanner *me = unsmob_spanner (smob);
182 SCM text = me->get_property ("text");
183 if (Text_interface::is_markup (text)
184 && me->get_bound (LEFT)->break_status_dir () == CENTER
185 && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
187 Output_def *layout = me->layout ();
188 SCM properties = Font_interface::text_font_alist_chain (me);
189 alist = scm_acons (ly_symbol2scm ("stencil"),
190 Text_interface::interpret_markup (layout->self_scm (),
198 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
200 Line_spanner::print (SCM smob)
202 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
204 Interval_t<Moment> moments = me->spanned_time ();
206 We remove the line at the start of the line. For piano voice
207 indicators, it makes no sense to have them at the start of the
210 I'm not sure what the official rules for glissandi are, but
211 usually the 2nd note of the glissando is "exact", so when playing
212 from the start of the line, there is no need to glide.
214 From a typographical p.o.v. this makes sense, since the amount of
215 space left of a note at the start of a line is very small.
220 if (moments.length () == Moment (0,0))
223 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
224 me->get_property ("right-bound-info"));
227 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
228 commonx = me->common_refpoint (commonx, X_AXIS);
230 Drul_array<Offset> span_points;
235 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
236 bounds[d], SCM_BOOL_F), 0.0)
237 + commonx->relative_coordinate (commonx, X_AXIS),
238 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
239 bounds[d], SCM_BOOL_F), 0.0));
243 while (flip (&d) != LEFT);
245 Drul_array<Real> gaps (0, 0);
246 Drul_array<bool> arrows (0, 0);
247 Drul_array<Stencil*> stencils (0,0);
248 Drul_array<Grob*> common_y (0, 0);
251 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
252 bounds[d], SCM_BOOL_F), 0.0);
253 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
254 bounds[d], SCM_BOOL_F));
255 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
256 bounds[d], SCM_BOOL_F));
257 common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
258 bounds[d], SCM_BOOL_F));
262 while (flip (&d) != LEFT);
264 Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
266 span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
267 while (flip (&d) != LEFT);
269 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
270 Offset dz_dir = dz.direction ();
271 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
281 Stencil s = stencils[d]->translated (span_points[d]);
282 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
283 bounds[d], SCM_BOOL_F);
284 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
285 bounds[d], SCM_BOOL_F);
287 if (scm_is_number (align))
288 s.align_to (Y_AXIS, scm_to_double (align));
291 todo: should use font-size.
293 if (is_number_pair (off))
294 s.translate (ly_scm2offset (off));
296 line.add_stencil (s);
299 while (flip (&d) != LEFT);
304 span_points[d] += dz_dir *
305 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
307 span_points[d] += -d * gaps[d] * dz.direction ();
309 while (flip (&d) != LEFT);
311 Offset adjust = dz.direction() * Staff_symbol_referencer::staff_space (me);
312 line.add_stencil (Line_interface::line (me,
313 span_points[LEFT] + (arrows[LEFT] ? adjust*1.4 : Offset(0,0)),
314 span_points[RIGHT] - (arrows[RIGHT] ? adjust*0.55 : Offset(0,0))));
316 line.add_stencil (Line_interface::arrows (me,
322 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
323 -me->relative_coordinate (my_common_y, Y_AXIS)));
326 return line.smobbed_copy ();
329 ADD_INTERFACE (Line_spanner,
330 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
331 "The property @code{style} can be @code{line}, "
332 "@code{dashed-line}, @code{trill}, \n"
333 "@code{dotted-line} or @code{zigzag}.\n"