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>
9 #include "line-spanner.hh"
13 #include "output-def.hh"
14 #include "paper-column.hh"
15 #include "staff-symbol-referencer.hh"
16 #include "font-interface.hh"
18 #include "align-interface.hh"
20 #include "line-interface.hh"
22 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
24 Line_spanner::after_line_breaking (SCM g)
26 Grob *me = unsmob_grob (g);
27 Spanner *sp = dynamic_cast<Spanner *> (me);
30 We remove the line at the start of the line. For piano voice
31 indicators, it makes no sense to have them at the start of the
34 I'm not sure what the official rules for glissandi are, but
35 usually the 2nd note of the glissando is "exact", so when playing
36 from the start of the line, there is no need to glide.
38 From a typographical p.o.v. this makes sense, since the amount of
39 space left of a note at the start of a line is very small.
44 if (sp->get_bound (LEFT)->break_status_dir ()
45 && !sp->get_bound (RIGHT)->break_status_dir ())
48 Can't do suicide, since this mucks up finding the trend.
50 me->set_property ("transparent", SCM_BOOL_T);
56 Line_spanner::line_stencil (Grob *me,
61 SCM type = me->get_property ("style");
65 if (scm_is_symbol (type)
66 && (type == ly_symbol2scm ("line")
67 || type == ly_symbol2scm ("dashed-line")
68 || type == ly_symbol2scm ("dotted-line")
69 || type == ly_symbol2scm ("zigzag")
70 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
72 line = Line_interface::line (me, from, to);
74 else if (scm_is_symbol (type)
75 && type == ly_symbol2scm ("trill"))
77 SCM alist_chain = Font_interface::text_font_alist_chain (me);
78 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
79 ly_symbol2scm ("fetaMusic")),
82 Font_metric *fm = select_font (me->layout (),
83 scm_cons (style_alist,
85 Stencil m = fm->find_by_name ("scripts.trill_element");
89 mol.add_at_edge (X_AXIS, RIGHT, m, 0);
90 while (m.extent (X_AXIS).length ()
91 && mol.extent (X_AXIS).length ()
92 + m.extent (X_AXIS).length () < dz[X_AXIS])
96 FIXME: should center element on x/y
98 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
99 mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
100 + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
102 mol.translate (from);
106 if (to_boolean (me->get_property ("arrow")))
107 line.add_stencil (Line_interface::arrows (me, from, to, false, true));
113 Find a common Y parent, which --if found-- should be the
114 fixed-distance alignment.
117 line_spanner_common_parent (Grob *me)
119 Grob *common = find_fixed_alignment_parent (me);
122 common = Staff_symbol_referencer::get_staff_symbol (me);
124 common = common->get_parent (Y_AXIS);
126 common = me->get_parent (Y_AXIS);
133 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
135 (If not, you risk that this is called from the staff-alignment
136 routine, via stencil_extent. At this point, the staves aren't
137 separated yet, so it doesn't work cross-staff.
139 (huh? crossable staves have fixed distance? --hwn)
142 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
144 Line_spanner::print (SCM smob)
146 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
148 Drul_array<Item *> bound (me->get_bound (LEFT),
149 me->get_bound (RIGHT));
151 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
153 Offset ofxy (gap, 0); /* offset from start point to start of line */
158 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
161 if (bound[RIGHT]->break_status_dir ())
163 if (bound[LEFT]->break_status_dir ())
165 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
172 This is hairy. For the normal case, we simply find common
173 parents, and draw a line between the bounds. When two note
174 heads are on different systems, there is no common parent
175 anymore. We have to find the piano-staff object.
178 Spanner *next_sp = me->broken_neighbor (RIGHT);
179 Item *next_bound = next_sp->get_bound (RIGHT);
181 if (next_bound->break_status_dir ())
183 programming_error ("no note heads for the line spanner on next line?"
189 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
190 commonx = me->common_refpoint (commonx, X_AXIS);
192 Grob *next_common_y = line_spanner_common_parent (next_bound);
193 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
195 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
197 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
198 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
200 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
202 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
203 this_ext.center () + yoff - extra_dy / 2);
204 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
205 next_ext.center () + yoff + extra_dy / 2);
208 Real len = dz.length ();
210 Offset dir = dz * (1 / len);
211 dz = (dz.length () - 2 * gap) * dir;
213 Stencil l (line_stencil (me, Offset (0, 0), dz));
215 l.translate (dir * gap + p1
216 - Offset (me->relative_coordinate (commonx, X_AXIS),
217 me->relative_coordinate (all_common_y, Y_AXIS)));
219 return l.smobbed_copy ();
223 Grob *common[] = { me, me };
224 for (int a = X_AXIS; a < NO_AXES; a++)
226 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
227 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
230 // distance from center to start of line
231 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
233 for (int a = X_AXIS; a < NO_AXES; a++)
237 = + robust_relative_extent (bound[RIGHT], common[X_AXIS], ax).center ()
238 - robust_relative_extent (bound[LEFT], common[X_AXIS], ax).center ();
240 my_off[ax] = me->relative_coordinate (common[a], ax);
241 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
244 ofxy = dxy * (off / dxy.length ()) ;
247 dxy[Y_AXIS] += extra_dy;
249 Stencil line = line_stencil (me, Offset (0, 0), dxy);
251 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
252 line.translate (ofxy - my_off + his_off + Offset (0, -extra_dy/2));
253 return line.smobbed_copy ();
257 ADD_INTERFACE (Line_spanner,
258 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
259 "The property @code{style} can be @code{line}, "
260 "@code{dashed-line}, @code{trill}, \n"
261 "@code{dotted-line} or @code{zigzag}.\n"