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 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
238 bounds[d], SCM_BOOL_F), 0.0));
242 while (flip (&d) != LEFT);
244 Drul_array<Real> gaps (0, 0);
245 Drul_array<bool> arrows (0, 0);
246 Drul_array<Stencil*> stencils (0,0);
247 Drul_array<Grob*> common_y (0, 0);
250 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
251 bounds[d], SCM_BOOL_F), 0.0);
252 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
253 bounds[d], SCM_BOOL_F));
254 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
255 bounds[d], SCM_BOOL_F));
256 common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
257 bounds[d], SCM_BOOL_F));
261 while (flip (&d) != LEFT);
263 Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
265 span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
266 while (flip (&d) != LEFT);
268 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
269 Offset dz_dir = dz.direction ();
270 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
278 span_points[d] += -d * gaps[d] * dz.direction ();
282 Stencil s = stencils[d]->translated (span_points[d]);
283 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
284 bounds[d], SCM_BOOL_F);
285 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
286 bounds[d], SCM_BOOL_F);
288 if (scm_is_number (align))
289 s.align_to (Y_AXIS, scm_to_double (align));
292 todo: should use font-size.
294 if (is_number_pair (off))
295 s.translate (ly_scm2offset (off));
297 line.add_stencil (s);
300 while (flip (&d) != LEFT);
305 span_points[d] += dz_dir *
306 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
308 while (flip (&d) != LEFT);
310 Offset adjust = dz.direction() * Staff_symbol_referencer::staff_space (me);
312 Offset line_left = span_points[LEFT] + (arrows[LEFT] ? adjust*1.4 : Offset (0, 0));
313 Offset line_right = span_points[RIGHT] - (arrows[RIGHT] ? adjust*0.55 : Offset (0, 0));
314 if (line_right[X_AXIS] > line_left[X_AXIS])
316 line.add_stencil (Line_interface::line (me, line_left, line_right));
318 line.add_stencil (Line_interface::arrows (me,
325 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
326 -me->relative_coordinate (my_common_y, Y_AXIS)));
329 return line.smobbed_copy ();
332 ADD_INTERFACE (Line_spanner,
333 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
334 "The property @code{style} can be @code{line}, "
335 "@code{dashed-line}, @code{trill}, \n"
336 "@code{dotted-line} or @code{zigzag}.\n"