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 if (me->get_bound (dir)->break_status_dir ())
98 Spanner *next_sp = me->broken_neighbor (dir);
99 Item *next_bound = next_sp->get_bound (dir);
101 if (next_bound->break_status_dir ())
103 programming_error ("no note heads for the line spanner on neighbor line?"
109 Grob *next_common_y = next_sp->common_refpoint (next_bound, X_AXIS);
110 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
112 y = next_ext.center ();
116 Grob *commony = me->common_refpoint (me->get_bound (dir), Y_AXIS);
117 y = me->get_bound (dir)->extent (commony, Y_AXIS).center ();
118 details = scm_acons (ly_symbol2scm ("common-Y"), commony->self_scm (), details);
121 y += dir * extra_dy / 2;
122 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
128 MAKE_SCHEME_CALLBACK (Line_spanner, calc_right_bound_info, 1);
130 Line_spanner::calc_right_bound_info (SCM smob)
132 return Line_spanner::calc_bound_info (smob, RIGHT);
135 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info, 1);
137 Line_spanner::calc_left_bound_info (SCM smob)
139 return Line_spanner::calc_bound_info (smob, LEFT);
142 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info_and_text, 1);
144 Line_spanner::calc_left_bound_info_and_text (SCM smob)
146 SCM alist = Line_spanner::calc_bound_info (smob, LEFT);
147 Spanner *me = unsmob_spanner (smob);
149 SCM text = me->get_property ("text");
150 if (Text_interface::is_markup (text)
151 && me->get_bound (LEFT)->break_status_dir () == CENTER
152 && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
154 Output_def *layout = me->layout ();
155 SCM properties = Font_interface::text_font_alist_chain (me);
156 alist = scm_acons (ly_symbol2scm ("stencil"),
157 Text_interface::interpret_markup (layout->self_scm (),
165 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
167 Line_spanner::print (SCM smob)
169 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
171 Interval_t<Moment> moments = me->spanned_time ();
173 We remove the line at the start of the line. For piano voice
174 indicators, it makes no sense to have them at the start of the
177 I'm not sure what the official rules for glissandi are, but
178 usually the 2nd note of the glissando is "exact", so when playing
179 from the start of the line, there is no need to glide.
181 From a typographical p.o.v. this makes sense, since the amount of
182 space left of a note at the start of a line is very small.
187 if (moments.length () == Moment (0,0))
190 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
191 me->get_property ("right-bound-info"));
194 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
195 commonx = me->common_refpoint (commonx, X_AXIS);
197 Drul_array<Offset> span_points;
202 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
203 bounds[d], SCM_BOOL_F), 0.0)
204 + commonx->relative_coordinate (commonx, X_AXIS),
205 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
206 bounds[d], SCM_BOOL_F), 0.0));
210 while (flip (&d) != LEFT);
212 Drul_array<Real> gaps (0, 0);
213 Drul_array<bool> arrows (0, 0);
214 Drul_array<Stencil*> stencils (0,0);
215 Drul_array<Grob*> common_y (0, 0);
218 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
219 bounds[d], SCM_BOOL_F), 0.0);
220 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
221 bounds[d], SCM_BOOL_F));
222 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
223 bounds[d], SCM_BOOL_F));
224 common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
225 bounds[d], SCM_BOOL_F));
229 while (flip (&d) != LEFT);
231 Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
233 span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
234 while (flip (&d) != LEFT);
236 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
237 Offset dz_dir = dz.direction ();
238 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
248 Stencil s = stencils[d]->translated (span_points[d]);
249 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
250 bounds[d], SCM_BOOL_F);
251 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
252 bounds[d], SCM_BOOL_F);
254 if (scm_is_number (align))
255 s.align_to (Y_AXIS, scm_to_double (align));
258 todo: should use font-size.
260 if (is_number_pair (off))
261 s.translate (ly_scm2offset (off));
263 line.add_stencil (s);
266 while (flip (&d) != LEFT);
271 span_points[d] += dz_dir *
272 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
274 span_points[d] += -d * gaps[d] * dz.direction ();
276 while (flip (&d) != LEFT);
278 line.add_stencil (Line_interface::line (me,
280 span_points[RIGHT]));
282 line.add_stencil (Line_interface::arrows (me,
288 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
289 -me->relative_coordinate (my_common_y, Y_AXIS)));
292 return line.smobbed_copy ();
295 ADD_INTERFACE (Line_spanner,
296 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
297 "The property @code{style} can be @code{line}, "
298 "@code{dashed-line}, @code{trill}, \n"
299 "@code{dotted-line} or @code{zigzag}.\n"