2 line-spanner.cc -- implement New_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"
23 class New_line_spanner
26 DECLARE_SCHEME_CALLBACK (print, (SCM));
27 DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
29 DECLARE_SCHEME_CALLBACK (calc_left_bound_info, (SCM));
30 DECLARE_SCHEME_CALLBACK (calc_left_bound_info_and_text, (SCM));
31 DECLARE_SCHEME_CALLBACK (calc_right_bound_info, (SCM));
32 DECLARE_SCHEME_CALLBACK (calc_bound_info, (SCM, Direction));
33 DECLARE_GROB_INTERFACE();
38 line_spanner_common_parent (Grob *me)
40 Grob *common = find_fixed_alignment_parent (me);
43 common = Staff_symbol_referencer::get_staff_symbol (me);
45 common = common->get_parent (Y_AXIS);
47 common = me->get_parent (Y_AXIS);
54 New_line_spanner::calc_bound_info (SCM smob, Direction dir)
56 Spanner *me = unsmob_spanner (smob);
58 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
59 commonx = me->common_refpoint (commonx, X_AXIS);
61 SCM bound_details = me->get_property ("bound-details");
63 SCM details = SCM_BOOL_F;
64 if (details == SCM_BOOL_F)
65 details = ly_assoc_get ((dir == LEFT)
66 ? ly_symbol2scm ("left")
67 : ly_symbol2scm ("right"), bound_details, SCM_BOOL_F);
69 if (me->get_bound (dir)->break_status_dir ())
71 SCM extra = ly_assoc_get ((dir == LEFT)
72 ? ly_symbol2scm ("left-broken")
73 : ly_symbol2scm ("right-broken"), bound_details, SCM_EOL);
75 for (SCM s = extra; scm_is_pair (s); s = scm_cdr (s))
76 details = scm_cons (scm_car (s), details);
79 if (details == SCM_BOOL_F)
80 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
82 SCM text = ly_assoc_get (ly_symbol2scm ("text"), details, SCM_BOOL_F);
83 if (Text_interface::is_markup (text))
85 Output_def *layout = me->layout ();
86 SCM properties = Font_interface::text_font_alist_chain (me);
87 details = scm_acons (ly_symbol2scm ("stencil"),
88 Text_interface::interpret_markup (layout->self_scm (),
93 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
95 Direction attach = (Direction)
96 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
100 details = scm_acons (ly_symbol2scm ("X"),
101 scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
102 .linear_combination (attach)),
107 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
111 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
114 if (me->get_bound (dir)->break_status_dir ())
117 This is hairy. For the normal case, we simply find common
118 parents, and draw a line between the bounds. When two note
119 heads are on different systems, there is no common parent
120 anymore. We have to find the piano-staff object.
123 Spanner *next_sp = me->broken_neighbor (dir);
124 Item *next_bound = next_sp->get_bound (dir);
126 if (next_bound->break_status_dir ())
128 programming_error ("no note heads for the line spanner on neighbor line?"
134 Grob *next_common_y = line_spanner_common_parent (next_bound);
135 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
137 y = next_ext.center ();
141 Grob *commony = me->common_refpoint (me->get_bound (dir), Y_AXIS);
142 y = me->get_bound (dir)->extent (commony, Y_AXIS).center();
143 details = scm_acons (ly_symbol2scm ("common-Y"), commony->self_scm (), details);
146 y += dir * extra_dy / 2;
147 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
153 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_right_bound_info, 1);
155 New_line_spanner::calc_right_bound_info (SCM smob)
157 return New_line_spanner::calc_bound_info (smob, RIGHT);
160 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_left_bound_info, 1);
162 New_line_spanner::calc_left_bound_info (SCM smob)
164 return New_line_spanner::calc_bound_info (smob, LEFT);
167 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_left_bound_info_and_text, 1);
169 New_line_spanner::calc_left_bound_info_and_text (SCM smob)
171 SCM alist = New_line_spanner::calc_bound_info (smob, LEFT);
172 Spanner *me = unsmob_spanner (smob);
174 SCM text = me->get_property ("text");
175 if (Text_interface::is_markup (text)
176 && me->get_bound (LEFT)->break_status_dir () == CENTER
177 && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
179 Output_def *layout = me->layout ();
180 SCM properties = Font_interface::text_font_alist_chain (me);
181 alist = scm_acons (ly_symbol2scm ("stencil"),
182 Text_interface::interpret_markup (layout->self_scm (),
190 MAKE_SCHEME_CALLBACK (New_line_spanner, print, 1);
192 New_line_spanner::print (SCM smob)
194 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
196 Interval_t<Moment> moments = me->spanned_time ();
198 We remove the line at the start of the line. For piano voice
199 indicators, it makes no sense to have them at the start of the
202 I'm not sure what the official rules for glissandi are, but
203 usually the 2nd note of the glissando is "exact", so when playing
204 from the start of the line, there is no need to glide.
206 From a typographical p.o.v. this makes sense, since the amount of
207 space left of a note at the start of a line is very small.
212 if (moments.length () == Moment (0,0))
215 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
216 me->get_property ("right-bound-info"));
219 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
220 commonx = me->common_refpoint (commonx, X_AXIS);
222 Drul_array<Offset> span_points;
227 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
228 bounds[d], SCM_BOOL_F), 0.0)
229 + commonx->relative_coordinate (commonx, X_AXIS),
230 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
231 bounds[d], SCM_BOOL_F), 0.0));
235 while (flip (&d) != LEFT);
237 Drul_array<Real> gaps (0, 0);
238 Drul_array<bool> arrows (0, 0);
239 Drul_array<Stencil*> stencils (0,0);
240 Drul_array<Grob*> common_y (0, 0);
243 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
244 bounds[d], SCM_BOOL_F), 0.0);
245 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
246 bounds[d], SCM_BOOL_F));
247 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
248 bounds[d], SCM_BOOL_F));
249 common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
250 bounds[d], SCM_BOOL_F));
254 while (flip (&d) != LEFT);
256 Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
258 span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
259 while (flip (&d) != LEFT);
261 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
262 Offset dz_dir = dz.direction ();
263 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
273 Stencil s = stencils[d]->translated (span_points[d]);
274 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
275 bounds[d], SCM_BOOL_F);
276 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
277 bounds[d], SCM_BOOL_F);
279 if (scm_is_number (align))
280 s.align_to (Y_AXIS, scm_to_double (align));
283 todo: should use font-size.
285 if (is_number_pair (off))
286 s.translate (ly_scm2offset (off));
288 line.add_stencil (s);
291 while (flip (&d) != LEFT);
296 span_points[d] += dz_dir *
297 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
299 span_points[d] += -d * gaps[d] * dz.direction ();
301 while (flip (&d) != LEFT);
303 line.add_stencil (Line_interface::line (me,
305 span_points[RIGHT]));
307 line.add_stencil (Line_interface::arrows (me,
313 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
314 -me->relative_coordinate (my_common_y, Y_AXIS)));
317 return line.smobbed_copy ();
320 ADD_INTERFACE (New_line_spanner,
321 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
322 "The property @code{style} can be @code{line}, "
323 "@code{dashed-line}, @code{trill}, \n"
324 "@code{dotted-line} or @code{zigzag}.\n"