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 Grob *common = find_fixed_alignment_parent (me);
42 common = Staff_symbol_referencer::get_staff_symbol (me);
44 common = common->get_parent (Y_AXIS);
46 common = me->get_parent (Y_AXIS);
53 Line_spanner::calc_bound_info (SCM smob, Direction dir)
55 Spanner *me = unsmob_spanner (smob);
57 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
58 commonx = me->common_refpoint (commonx, X_AXIS);
60 SCM bound_details = me->get_property ("bound-details");
62 SCM details = SCM_BOOL_F;
63 if (details == SCM_BOOL_F)
64 details = ly_assoc_get ((dir == LEFT)
65 ? ly_symbol2scm ("left")
66 : ly_symbol2scm ("right"), bound_details, SCM_BOOL_F);
68 if (me->get_bound (dir)->break_status_dir ())
70 SCM extra = ly_assoc_get ((dir == LEFT)
71 ? ly_symbol2scm ("left-broken")
72 : ly_symbol2scm ("right-broken"), bound_details, SCM_EOL);
74 for (SCM s = extra; scm_is_pair (s); s = scm_cdr (s))
75 details = scm_cons (scm_car (s), details);
78 if (details == SCM_BOOL_F)
79 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
81 SCM text = ly_assoc_get (ly_symbol2scm ("text"), details, SCM_BOOL_F);
82 if (Text_interface::is_markup (text))
84 Output_def *layout = me->layout ();
85 SCM properties = Font_interface::text_font_alist_chain (me);
86 details = scm_acons (ly_symbol2scm ("stencil"),
87 Text_interface::interpret_markup (layout->self_scm (),
92 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
94 Direction attach = (Direction)
95 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
99 details = scm_acons (ly_symbol2scm ("X"),
100 scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
101 .linear_combination (attach)),
106 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
110 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
113 if (me->get_bound (dir)->break_status_dir ())
116 This is hairy. For the normal case, we simply find common
117 parents, and draw a line between the bounds. When two note
118 heads are on different systems, there is no common parent
119 anymore. We have to find the piano-staff object.
122 Spanner *next_sp = me->broken_neighbor (dir);
123 Item *next_bound = next_sp->get_bound (dir);
125 if (next_bound->break_status_dir ())
127 programming_error ("no note heads for the line spanner on neighbor line?"
133 Grob *next_common_y = line_spanner_common_parent (next_bound);
134 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
136 y = next_ext.center ();
140 Grob *commony = me->common_refpoint (me->get_bound (dir), Y_AXIS);
141 y = me->get_bound (dir)->extent (commony, Y_AXIS).center();
142 details = scm_acons (ly_symbol2scm ("common-Y"), commony->self_scm (), details);
145 y += dir * extra_dy / 2;
146 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
152 MAKE_SCHEME_CALLBACK (Line_spanner, calc_right_bound_info, 1);
154 Line_spanner::calc_right_bound_info (SCM smob)
156 return Line_spanner::calc_bound_info (smob, RIGHT);
159 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info, 1);
161 Line_spanner::calc_left_bound_info (SCM smob)
163 return Line_spanner::calc_bound_info (smob, LEFT);
166 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info_and_text, 1);
168 Line_spanner::calc_left_bound_info_and_text (SCM smob)
170 SCM alist = Line_spanner::calc_bound_info (smob, LEFT);
171 Spanner *me = unsmob_spanner (smob);
173 SCM text = me->get_property ("text");
174 if (Text_interface::is_markup (text)
175 && me->get_bound (LEFT)->break_status_dir () == CENTER
176 && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
178 Output_def *layout = me->layout ();
179 SCM properties = Font_interface::text_font_alist_chain (me);
180 alist = scm_acons (ly_symbol2scm ("stencil"),
181 Text_interface::interpret_markup (layout->self_scm (),
189 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
191 Line_spanner::print (SCM smob)
193 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
195 Interval_t<Moment> moments = me->spanned_time ();
197 We remove the line at the start of the line. For piano voice
198 indicators, it makes no sense to have them at the start of the
201 I'm not sure what the official rules for glissandi are, but
202 usually the 2nd note of the glissando is "exact", so when playing
203 from the start of the line, there is no need to glide.
205 From a typographical p.o.v. this makes sense, since the amount of
206 space left of a note at the start of a line is very small.
211 if (moments.length () == Moment (0,0))
214 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
215 me->get_property ("right-bound-info"));
218 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
219 commonx = me->common_refpoint (commonx, X_AXIS);
221 Drul_array<Offset> span_points;
226 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
227 bounds[d], SCM_BOOL_F), 0.0)
228 + commonx->relative_coordinate (commonx, X_AXIS),
229 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
230 bounds[d], SCM_BOOL_F), 0.0));
234 while (flip (&d) != LEFT);
236 Drul_array<Real> gaps (0, 0);
237 Drul_array<bool> arrows (0, 0);
238 Drul_array<Stencil*> stencils (0,0);
239 Drul_array<Grob*> common_y (0, 0);
242 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
243 bounds[d], SCM_BOOL_F), 0.0);
244 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
245 bounds[d], SCM_BOOL_F));
246 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
247 bounds[d], SCM_BOOL_F));
248 common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
249 bounds[d], SCM_BOOL_F));
253 while (flip (&d) != LEFT);
255 Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
257 span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
258 while (flip (&d) != LEFT);
260 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
261 Offset dz_dir = dz.direction ();
262 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
272 Stencil s = stencils[d]->translated (span_points[d]);
273 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
274 bounds[d], SCM_BOOL_F);
275 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
276 bounds[d], SCM_BOOL_F);
278 if (scm_is_number (align))
279 s.align_to (Y_AXIS, scm_to_double (align));
282 todo: should use font-size.
284 if (is_number_pair (off))
285 s.translate (ly_scm2offset (off));
287 line.add_stencil (s);
290 while (flip (&d) != LEFT);
295 span_points[d] += dz_dir *
296 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
298 span_points[d] += -d * gaps[d] * dz.direction ();
300 while (flip (&d) != LEFT);
302 line.add_stencil (Line_interface::line (me,
304 span_points[RIGHT]));
306 line.add_stencil (Line_interface::arrows (me,
312 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
313 -me->relative_coordinate (my_common_y, Y_AXIS)));
316 return line.smobbed_copy ();
319 ADD_INTERFACE (Line_spanner,
320 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
321 "The property @code{style} can be @code{line}, "
322 "@code{dashed-line}, @code{trill}, \n"
323 "@code{dotted-line} or @code{zigzag}.\n"