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 ();
36 Line_spanner::calc_bound_info (SCM smob, Direction dir)
38 Spanner *me = unsmob_spanner (smob);
40 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
41 commonx = me->common_refpoint (commonx, X_AXIS);
43 SCM bound_details = me->get_property ("bound-details");
45 SCM details = SCM_BOOL_F;
46 if (details == SCM_BOOL_F)
47 details = ly_assoc_get ((dir == LEFT)
48 ? ly_symbol2scm ("left")
49 : ly_symbol2scm ("right"), bound_details, SCM_BOOL_F);
51 if (me->get_bound (dir)->break_status_dir ())
53 SCM extra = ly_assoc_get ((dir == LEFT)
54 ? ly_symbol2scm ("left-broken")
55 : ly_symbol2scm ("right-broken"), bound_details, SCM_EOL);
57 for (SCM s = extra; scm_is_pair (s); s = scm_cdr (s))
58 details = scm_cons (scm_car (s), details);
61 if (details == SCM_BOOL_F)
62 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
64 SCM text = ly_assoc_get (ly_symbol2scm ("text"), details, SCM_BOOL_F);
65 if (Text_interface::is_markup (text))
67 Output_def *layout = me->layout ();
68 SCM properties = Font_interface::text_font_alist_chain (me);
69 details = scm_acons (ly_symbol2scm ("stencil"),
70 Text_interface::interpret_markup (layout->self_scm (),
75 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
77 Direction attach = (Direction)
78 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
82 details = scm_acons (ly_symbol2scm ("X"),
83 scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
84 .linear_combination (attach)),
89 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
93 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
96 Grob *common_y = me->common_refpoint (me->get_bound (dir), Y_AXIS);
97 if (me->get_bound (dir)->break_status_dir ())
99 Spanner *next_sp = me->broken_neighbor (dir);
100 Item *next_bound = next_sp->get_bound (dir);
102 if (next_bound->break_status_dir ())
104 programming_error ("no note heads for the line spanner on neighbor line?"
110 Grob *next_common_y = next_sp->common_refpoint (next_bound, X_AXIS);
111 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
113 /* FIXME: this is not a real solution. We want to know would be the
114 y-position of the right bound (relative to the line-spanner)
115 if it belonged to the same system
116 as the left bound. What we do instead is to calculate the
117 y-position of the right bound relative to the VerticalAlignment
118 (of the next System!) and subtract the y-position of the
119 line-spanner relative to the VerticalAlignment (of this System!).
120 This works as long as the distance between staves is approximately
121 the same before and after breaking. */
122 y = next_ext.center () - me->relative_coordinate (common_y, Y_AXIS);
126 y = me->get_bound (dir)->extent (common_y, Y_AXIS).center ();
127 details = scm_acons (ly_symbol2scm ("common-Y"), common_y->self_scm (), details);
130 y += dir * extra_dy / 2;
131 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
137 MAKE_SCHEME_CALLBACK (Line_spanner, calc_right_bound_info, 1);
139 Line_spanner::calc_right_bound_info (SCM smob)
141 return Line_spanner::calc_bound_info (smob, RIGHT);
144 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info, 1);
146 Line_spanner::calc_left_bound_info (SCM smob)
148 return Line_spanner::calc_bound_info (smob, LEFT);
151 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info_and_text, 1);
153 Line_spanner::calc_left_bound_info_and_text (SCM smob)
155 SCM alist = Line_spanner::calc_bound_info (smob, LEFT);
156 Spanner *me = unsmob_spanner (smob);
158 SCM text = me->get_property ("text");
159 if (Text_interface::is_markup (text)
160 && me->get_bound (LEFT)->break_status_dir () == CENTER
161 && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
163 Output_def *layout = me->layout ();
164 SCM properties = Font_interface::text_font_alist_chain (me);
165 alist = scm_acons (ly_symbol2scm ("stencil"),
166 Text_interface::interpret_markup (layout->self_scm (),
174 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
176 Line_spanner::print (SCM smob)
178 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
180 Interval_t<Moment> moments = me->spanned_time ();
182 We remove the line at the start of the line. For piano voice
183 indicators, it makes no sense to have them at the start of the
186 I'm not sure what the official rules for glissandi are, but
187 usually the 2nd note of the glissando is "exact", so when playing
188 from the start of the line, there is no need to glide.
190 From a typographical p.o.v. this makes sense, since the amount of
191 space left of a note at the start of a line is very small.
196 if (moments.length () == Moment (0,0))
199 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
200 me->get_property ("right-bound-info"));
203 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
204 commonx = me->common_refpoint (commonx, X_AXIS);
206 Drul_array<Offset> span_points;
211 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
212 bounds[d], SCM_BOOL_F), 0.0)
213 + commonx->relative_coordinate (commonx, X_AXIS),
214 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
215 bounds[d], SCM_BOOL_F), 0.0));
219 while (flip (&d) != LEFT);
221 Drul_array<Real> gaps (0, 0);
222 Drul_array<bool> arrows (0, 0);
223 Drul_array<Stencil*> stencils (0,0);
224 Drul_array<Grob*> common_y (0, 0);
227 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
228 bounds[d], SCM_BOOL_F), 0.0);
229 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
230 bounds[d], SCM_BOOL_F));
231 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
232 bounds[d], SCM_BOOL_F));
233 common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
234 bounds[d], SCM_BOOL_F));
238 while (flip (&d) != LEFT);
240 Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
242 span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
243 while (flip (&d) != LEFT);
245 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
246 Offset dz_dir = dz.direction ();
247 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
257 Stencil s = stencils[d]->translated (span_points[d]);
258 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
259 bounds[d], SCM_BOOL_F);
260 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
261 bounds[d], SCM_BOOL_F);
263 if (scm_is_number (align))
264 s.align_to (Y_AXIS, scm_to_double (align));
267 todo: should use font-size.
269 if (is_number_pair (off))
270 s.translate (ly_scm2offset (off));
272 line.add_stencil (s);
275 while (flip (&d) != LEFT);
280 span_points[d] += dz_dir *
281 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
283 span_points[d] += -d * gaps[d] * dz.direction ();
285 while (flip (&d) != LEFT);
287 line.add_stencil (Line_interface::line (me,
289 span_points[RIGHT]));
291 line.add_stencil (Line_interface::arrows (me,
297 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
298 -me->relative_coordinate (my_common_y, Y_AXIS)));
301 return line.smobbed_copy ();
304 ADD_INTERFACE (Line_spanner,
305 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
306 "The property @code{style} can be @code{line}, "
307 "@code{dashed-line}, @code{trill}, \n"
308 "@code{dotted-line} or @code{zigzag}.\n"