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"
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_right_bound_info, (SCM));
31 DECLARE_SCHEME_CALLBACK (calc_bound_info, (SCM, Direction));
32 DECLARE_GROB_INTERFACE();
34 static Stencil line_stencil (Grob *me, Offset f, Offset t);
40 line_spanner_common_parent (Grob *me)
42 Grob *common = find_fixed_alignment_parent (me);
45 common = Staff_symbol_referencer::get_staff_symbol (me);
47 common = common->get_parent (Y_AXIS);
49 common = me->get_parent (Y_AXIS);
55 MAKE_SCHEME_CALLBACK (New_line_spanner, after_line_breaking, 1);
57 New_line_spanner::after_line_breaking (SCM g)
59 Grob *me = unsmob_grob (g);
60 Spanner *sp = dynamic_cast<Spanner *> (me);
63 We remove the line at the start of the line. For piano voice
64 indicators, it makes no sense to have them at the start of the
67 I'm not sure what the official rules for glissandi are, but
68 usually the 2nd note of the glissando is "exact", so when playing
69 from the start of the line, there is no need to glide.
71 From a typographical p.o.v. this makes sense, since the amount of
72 space left of a note at the start of a line is very small.
77 if (sp->get_bound (LEFT)->break_status_dir ()
78 && !sp->get_bound (RIGHT)->break_status_dir ())
81 Can't do suicide, since this mucks up finding the trend.
83 me->set_property ("transparent", SCM_BOOL_T);
89 New_line_spanner::line_stencil (Grob *me,
94 SCM type = me->get_property ("style");
98 if (scm_is_symbol (type)
99 && (type == ly_symbol2scm ("line")
100 || type == ly_symbol2scm ("dashed-line")
101 || type == ly_symbol2scm ("dotted-line")
102 || type == ly_symbol2scm ("zigzag")
103 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
105 line = Line_interface::line (me, from, to);
107 else if (scm_is_symbol (type)
108 && type == ly_symbol2scm ("trill"))
110 SCM alist_chain = Font_interface::text_font_alist_chain (me);
111 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
112 ly_symbol2scm ("fetaMusic")),
115 Font_metric *fm = select_font (me->layout (),
116 scm_cons (style_alist,
118 Stencil m = fm->find_by_name ("scripts.trill_element");
122 mol.add_at_edge (X_AXIS, RIGHT, m, 0);
123 while (m.extent (X_AXIS).length ()
124 && mol.extent (X_AXIS).length ()
125 + m.extent (X_AXIS).length () < dz[X_AXIS])
129 FIXME: should center element on x/y
131 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
132 mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
133 + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
135 mol.translate (from);
139 if (to_boolean (me->get_property ("arrow")))
140 line.add_stencil (Line_interface::arrows (me, from, to, false, true));
147 New_line_spanner::calc_bound_info (SCM smob, Direction dir)
149 Spanner *me = unsmob_spanner (smob);
151 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
152 commonx = me->common_refpoint (commonx, X_AXIS);
154 SCM bound_details = me->get_property ("bound-details");
158 (me->get_bound (dir)->break_status_dir ())
159 ? (dir == LEFT ? ly_symbol2scm ("left-broken")
160 : ly_symbol2scm ("right-broken"))
161 : (dir == LEFT ? ly_symbol2scm ("left")
162 : ly_symbol2scm ("right"));
164 SCM details = ly_assoc_get (sym, bound_details, SCM_BOOL_F);
165 if (details == SCM_BOOL_F)
166 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
168 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
170 Direction attach = (Direction)
171 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
172 details, SCM_BOOL_F),
175 details = scm_acons (ly_symbol2scm ("X"),
176 scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
177 .linear_combination (attach)),
182 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
186 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
189 if (me->get_bound (dir)->break_status_dir ())
192 This is hairy. For the normal case, we simply find common
193 parents, and draw a line between the bounds. When two note
194 heads are on different systems, there is no common parent
195 anymore. We have to find the piano-staff object.
198 Spanner *next_sp = me->broken_neighbor (dir);
199 Item *next_bound = next_sp->get_bound (dir);
201 if (next_bound->break_status_dir ())
203 programming_error ("no note heads for the line spanner on neighbor line?"
209 Grob *next_common_y = line_spanner_common_parent (next_bound);
210 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
212 y = next_ext.center ();
216 Grob *commony = me->get_bound (LEFT);
217 commony = me->common_refpoint (commony, Y_AXIS);
218 y = me->get_bound (dir)->extent (commony, Y_AXIS).center();
221 y += dir * extra_dy / 2;
222 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
228 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_right_bound_info, 1);
230 New_line_spanner::calc_right_bound_info (SCM smob)
232 return New_line_spanner::calc_bound_info (smob, RIGHT);
235 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_left_bound_info, 1);
237 New_line_spanner::calc_left_bound_info (SCM smob)
239 return New_line_spanner::calc_bound_info (smob, LEFT);
242 MAKE_SCHEME_CALLBACK (New_line_spanner, print, 1);
244 New_line_spanner::print (SCM smob)
246 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
248 Interval_t<Moment> moments = me->spanned_time ();
249 if (moments.length () == Moment (0,0))
251 me->set_property ("transparent", SCM_BOOL_T);
255 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
256 me->get_property ("right-bound-info"));
259 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
260 commonx = me->common_refpoint (commonx, X_AXIS);
262 Drul_array<Offset> span_points;
267 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
268 bounds[d], SCM_BOOL_F), 0.0)
269 + commonx->relative_coordinate (commonx, X_AXIS),
270 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
271 bounds[d], SCM_BOOL_F), 0.0));
275 while (flip (&d) != LEFT);
277 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
278 Drul_array<Real> gaps;
280 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
281 bounds[d], SCM_BOOL_F), 0.0);
282 while (flip (&d) != LEFT);
284 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
286 me->set_property ("transparent", SCM_BOOL_T);
291 span_points[d] += -d * gaps[d] * dz.direction ();
292 while (flip (&d) != LEFT);
294 Offset my_z (me->relative_coordinate (commonx, X_AXIS), 0);
296 span_points[LEFT] -= my_z;
297 span_points[RIGHT] -= my_z;
299 Stencil line = line_stencil (me,
304 return line.smobbed_copy ();
307 ADD_INTERFACE (New_line_spanner,
308 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
309 "The property @code{style} can be @code{line}, "
310 "@code{dashed-line}, @code{trill}, \n"
311 "@code{dotted-line} or @code{zigzag}.\n"