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"
11 #include "paper-column.hh"
12 #include "staff-symbol-referencer.hh"
13 #include "font-interface.hh"
15 #include "align-interface.hh"
17 #include "line-interface.hh"
20 #include "lily-proto.hh"
21 #include "grob-interface.hh"
22 #include "text-interface.hh"
24 class New_line_spanner
27 DECLARE_SCHEME_CALLBACK (print, (SCM));
28 DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
30 DECLARE_SCHEME_CALLBACK (calc_left_bound_info, (SCM));
31 DECLARE_SCHEME_CALLBACK (calc_left_bound_info_and_text, (SCM));
32 DECLARE_SCHEME_CALLBACK (calc_right_bound_info, (SCM));
33 DECLARE_SCHEME_CALLBACK (calc_bound_info, (SCM, Direction));
34 DECLARE_GROB_INTERFACE();
36 static Grob *common_y (Grob*);
40 New_line_spanner::common_y (Grob*me_grob)
42 Spanner *me = dynamic_cast<Spanner*> (me_grob);
47 if (me->get_bound (d)->break_status_dir () == CENTER)
48 commony = me->get_bound (d)->common_refpoint (commony, Y_AXIS);
49 while (flip (&d) != LEFT);
55 line_spanner_common_parent (Grob *me)
57 Grob *common = find_fixed_alignment_parent (me);
60 common = Staff_symbol_referencer::get_staff_symbol (me);
62 common = common->get_parent (Y_AXIS);
64 common = me->get_parent (Y_AXIS);
71 New_line_spanner::calc_bound_info (SCM smob, Direction dir)
73 Spanner *me = unsmob_spanner (smob);
75 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
76 commonx = me->common_refpoint (commonx, X_AXIS);
78 SCM bound_details = me->get_property ("bound-details");
80 SCM details = SCM_BOOL_F;
81 if (details == SCM_BOOL_F)
82 details = ly_assoc_get ((dir == LEFT)
83 ? ly_symbol2scm ("left")
84 : ly_symbol2scm ("right"), bound_details, SCM_BOOL_F);
86 if (me->get_bound (dir)->break_status_dir ())
88 SCM extra = ly_assoc_get ((dir == LEFT)
89 ? ly_symbol2scm ("left-broken")
90 : ly_symbol2scm ("right-broken"), bound_details, SCM_EOL);
92 for (SCM s = extra; scm_is_pair (s); s = scm_cdr (s))
93 details = scm_cons (scm_car (s), details);
96 if (details == SCM_BOOL_F)
97 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
99 SCM text = ly_assoc_get (ly_symbol2scm ("text"), details, SCM_BOOL_F);
100 if (Text_interface::is_markup (text))
102 Output_def *layout = me->layout ();
103 SCM properties = Font_interface::text_font_alist_chain (me);
104 details = scm_acons (ly_symbol2scm ("stencil"),
105 Text_interface::interpret_markup (layout->self_scm (),
110 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
112 Direction attach = (Direction)
113 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
114 details, SCM_BOOL_F),
117 details = scm_acons (ly_symbol2scm ("X"),
118 scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
119 .linear_combination (attach)),
124 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
128 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
131 if (me->get_bound (dir)->break_status_dir ())
134 This is hairy. For the normal case, we simply find common
135 parents, and draw a line between the bounds. When two note
136 heads are on different systems, there is no common parent
137 anymore. We have to find the piano-staff object.
140 Spanner *next_sp = me->broken_neighbor (dir);
141 Item *next_bound = next_sp->get_bound (dir);
143 if (next_bound->break_status_dir ())
145 programming_error ("no note heads for the line spanner on neighbor line?"
151 Grob *next_common_y = line_spanner_common_parent (next_bound);
152 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
154 y = next_ext.center ();
158 Grob *commony = common_y (me);
159 y = me->get_bound (dir)->extent (commony, Y_AXIS).center();
162 y += dir * extra_dy / 2;
163 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
169 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_right_bound_info, 1);
171 New_line_spanner::calc_right_bound_info (SCM smob)
173 return New_line_spanner::calc_bound_info (smob, RIGHT);
176 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_left_bound_info, 1);
178 New_line_spanner::calc_left_bound_info (SCM smob)
180 return New_line_spanner::calc_bound_info (smob, LEFT);
183 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_left_bound_info_and_text, 1);
185 New_line_spanner::calc_left_bound_info_and_text (SCM smob)
187 SCM alist = New_line_spanner::calc_bound_info (smob, LEFT);
188 Spanner *me = unsmob_spanner (smob);
190 SCM text = me->get_property ("text");
191 if (Text_interface::is_markup (text)
192 && me->get_bound (LEFT)->break_status_dir () == CENTER
193 && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
195 Output_def *layout = me->layout ();
196 SCM properties = Font_interface::text_font_alist_chain (me);
197 alist = scm_acons (ly_symbol2scm ("stencil"),
198 Text_interface::interpret_markup (layout->self_scm (),
206 MAKE_SCHEME_CALLBACK (New_line_spanner, print, 1);
208 New_line_spanner::print (SCM smob)
210 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
212 Interval_t<Moment> moments = me->spanned_time ();
214 We remove the line at the start of the line. For piano voice
215 indicators, it makes no sense to have them at the start of the
218 I'm not sure what the official rules for glissandi are, but
219 usually the 2nd note of the glissando is "exact", so when playing
220 from the start of the line, there is no need to glide.
222 From a typographical p.o.v. this makes sense, since the amount of
223 space left of a note at the start of a line is very small.
228 if (moments.length () == Moment (0,0))
231 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
232 me->get_property ("right-bound-info"));
235 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
236 commonx = me->common_refpoint (commonx, X_AXIS);
238 Drul_array<Offset> span_points;
243 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
244 bounds[d], SCM_BOOL_F), 0.0)
245 + commonx->relative_coordinate (commonx, X_AXIS),
246 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
247 bounds[d], SCM_BOOL_F), 0.0));
251 while (flip (&d) != LEFT);
253 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
254 Offset dz_dir = dz.direction ();
255 Drul_array<Real> gaps (0, 0);
256 Drul_array<bool> arrows (0, 0);
257 Drul_array<Stencil*> stencils (0,0);
260 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
261 bounds[d], SCM_BOOL_F), 0.0);
262 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
263 bounds[d], SCM_BOOL_F));
264 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
265 bounds[d], SCM_BOOL_F));
268 while (flip (&d) != LEFT);
270 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
280 Stencil s = stencils[d]->translated (span_points[d]);
281 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
282 bounds[d], SCM_BOOL_F);
283 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
284 bounds[d], SCM_BOOL_F);
286 if (scm_is_number (align))
287 s.align_to (Y_AXIS, scm_to_double (align));
290 todo: should use font-size.
292 if (is_number_pair (off))
293 s.translate (ly_scm2offset (off));
295 line.add_stencil (s);
298 while (flip (&d) != LEFT);
303 span_points[d] += dz_dir *
304 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
307 span_points[d] += -d * gaps[d] * dz.direction ();
309 while (flip (&d) != LEFT);
311 line.add_stencil (Line_interface::line (me,
313 span_points[RIGHT]));
315 line.add_stencil (Line_interface::arrows (me,
320 Grob *commony = common_y (me);
322 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
323 -me->relative_coordinate (commony, Y_AXIS)));
326 return line.smobbed_copy ();
329 ADD_INTERFACE (New_line_spanner,
330 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
331 "The property @code{style} can be @code{line}, "
332 "@code{dashed-line}, @code{trill}, \n"
333 "@code{dotted-line} or @code{zigzag}.\n"