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>
10 #include "output-def.hh"
12 #include "staff-symbol-referencer.hh"
13 #include "font-interface.hh"
15 #include "align-interface.hh"
16 #include "line-interface.hh"
19 #include "lily-proto.hh"
20 #include "grob-interface.hh"
21 #include "text-interface.hh"
26 DECLARE_SCHEME_CALLBACK (print, (SCM));
27 DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
28 DECLARE_SCHEME_CALLBACK (calc_left_bound_info, (SCM));
29 DECLARE_SCHEME_CALLBACK (calc_left_bound_info_and_text, (SCM));
30 DECLARE_SCHEME_CALLBACK (calc_right_bound_info, (SCM));
31 DECLARE_SCHEME_CALLBACK (calc_bound_info, (SCM, Direction));
32 DECLARE_GROB_INTERFACE ();
37 line_spanner_common_parent (Grob *me)
39 /* FIXME: what is the right thing to do here, now that PianoStaves don't
40 have fixed spacing? */
41 Grob *common = 0; //find_fixed_alignment_parent (me);
44 common = Staff_symbol_referencer::get_staff_symbol (me);
46 common = common->get_parent (Y_AXIS);
48 common = me->get_parent (Y_AXIS);
55 Line_spanner::calc_bound_info (SCM smob, Direction dir)
57 Spanner *me = unsmob_spanner (smob);
59 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
60 commonx = me->common_refpoint (commonx, X_AXIS);
62 SCM bound_details = me->get_property ("bound-details");
64 SCM details = SCM_BOOL_F;
65 if (details == SCM_BOOL_F)
66 details = ly_assoc_get ((dir == LEFT)
67 ? ly_symbol2scm ("left")
68 : ly_symbol2scm ("right"), bound_details, SCM_BOOL_F);
70 if (me->get_bound (dir)->break_status_dir ())
72 SCM extra = ly_assoc_get ((dir == LEFT)
73 ? ly_symbol2scm ("left-broken")
74 : ly_symbol2scm ("right-broken"), bound_details, SCM_EOL);
76 for (SCM s = extra; scm_is_pair (s); s = scm_cdr (s))
77 details = scm_cons (scm_car (s), details);
80 if (details == SCM_BOOL_F)
81 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
83 SCM text = ly_assoc_get (ly_symbol2scm ("text"), details, SCM_BOOL_F);
84 if (Text_interface::is_markup (text))
86 Output_def *layout = me->layout ();
87 SCM properties = Font_interface::text_font_alist_chain (me);
88 details = scm_acons (ly_symbol2scm ("stencil"),
89 Text_interface::interpret_markup (layout->self_scm (),
94 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
96 Direction attach = (Direction)
97 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
101 details = scm_acons (ly_symbol2scm ("X"),
102 scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
103 .linear_combination (attach)),
108 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
112 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
115 if (me->get_bound (dir)->break_status_dir ())
118 This is hairy. For the normal case, we simply find common
119 parents, and draw a line between the bounds. When two note
120 heads are on different systems, there is no common parent
121 anymore. We have to find the piano-staff object.
124 Spanner *next_sp = me->broken_neighbor (dir);
125 Item *next_bound = next_sp->get_bound (dir);
127 if (next_bound->break_status_dir ())
129 programming_error ("no note heads for the line spanner on neighbor line?"
135 Grob *next_common_y = line_spanner_common_parent (next_bound);
136 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
138 y = next_ext.center ();
142 Grob *commony = me->common_refpoint (me->get_bound (dir), Y_AXIS);
143 y = me->get_bound (dir)->extent (commony, Y_AXIS).center ();
144 details = scm_acons (ly_symbol2scm ("common-Y"), commony->self_scm (), details);
147 y += dir * extra_dy / 2;
148 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
154 MAKE_SCHEME_CALLBACK (Line_spanner, calc_right_bound_info, 1);
156 Line_spanner::calc_right_bound_info (SCM smob)
158 return Line_spanner::calc_bound_info (smob, RIGHT);
161 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info, 1);
163 Line_spanner::calc_left_bound_info (SCM smob)
165 return Line_spanner::calc_bound_info (smob, LEFT);
168 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info_and_text, 1);
170 Line_spanner::calc_left_bound_info_and_text (SCM smob)
172 SCM alist = Line_spanner::calc_bound_info (smob, LEFT);
173 Spanner *me = unsmob_spanner (smob);
175 SCM text = me->get_property ("text");
176 if (Text_interface::is_markup (text)
177 && me->get_bound (LEFT)->break_status_dir () == CENTER
178 && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
180 Output_def *layout = me->layout ();
181 SCM properties = Font_interface::text_font_alist_chain (me);
182 alist = scm_acons (ly_symbol2scm ("stencil"),
183 Text_interface::interpret_markup (layout->self_scm (),
191 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
193 Line_spanner::print (SCM smob)
195 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
197 Interval_t<Moment> moments = me->spanned_time ();
199 We remove the line at the start of the line. For piano voice
200 indicators, it makes no sense to have them at the start of the
203 I'm not sure what the official rules for glissandi are, but
204 usually the 2nd note of the glissando is "exact", so when playing
205 from the start of the line, there is no need to glide.
207 From a typographical p.o.v. this makes sense, since the amount of
208 space left of a note at the start of a line is very small.
213 if (moments.length () == Moment (0,0))
216 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
217 me->get_property ("right-bound-info"));
220 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
221 commonx = me->common_refpoint (commonx, X_AXIS);
223 Drul_array<Offset> span_points;
228 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
229 bounds[d], SCM_BOOL_F), 0.0)
230 + commonx->relative_coordinate (commonx, X_AXIS),
231 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
232 bounds[d], SCM_BOOL_F), 0.0));
236 while (flip (&d) != LEFT);
238 Drul_array<Real> gaps (0, 0);
239 Drul_array<bool> arrows (0, 0);
240 Drul_array<Stencil*> stencils (0,0);
241 Drul_array<Grob*> common_y (0, 0);
244 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
245 bounds[d], SCM_BOOL_F), 0.0);
246 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
247 bounds[d], SCM_BOOL_F));
248 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
249 bounds[d], SCM_BOOL_F));
250 common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
251 bounds[d], SCM_BOOL_F));
255 while (flip (&d) != LEFT);
257 Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
259 span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
260 while (flip (&d) != LEFT);
262 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
263 Offset dz_dir = dz.direction ();
264 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
274 Stencil s = stencils[d]->translated (span_points[d]);
275 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
276 bounds[d], SCM_BOOL_F);
277 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
278 bounds[d], SCM_BOOL_F);
280 if (scm_is_number (align))
281 s.align_to (Y_AXIS, scm_to_double (align));
284 todo: should use font-size.
286 if (is_number_pair (off))
287 s.translate (ly_scm2offset (off));
289 line.add_stencil (s);
292 while (flip (&d) != LEFT);
297 span_points[d] += dz_dir *
298 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
300 span_points[d] += -d * gaps[d] * dz.direction ();
302 while (flip (&d) != LEFT);
304 line.add_stencil (Line_interface::line (me,
306 span_points[RIGHT]));
308 line.add_stencil (Line_interface::arrows (me,
314 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
315 -me->relative_coordinate (my_common_y, Y_AXIS)));
318 return line.smobbed_copy ();
321 ADD_INTERFACE (Line_spanner,
322 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
323 "The property @code{style} can be @code{line}, "
324 "@code{dashed-line}, @code{trill}, \n"
325 "@code{dotted-line} or @code{zigzag}.\n"