2 line-spanner.cc -- implement Line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2005 Jan Nieuwenhuizen <janneke@gnu.org>
9 #include "line-spanner.hh"
14 #include "output-def.hh"
15 #include "paper-column.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "font-interface.hh"
19 #include "align-interface.hh"
21 #include "line-interface.hh"
24 zigzag_stencil (Grob *me,
32 Real thick = Staff_symbol_referencer::line_thickness (me);
33 thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer?
35 Real staff_space = Staff_symbol_referencer::staff_space (me);
37 double w = robust_scm2double (me->get_property ("zigzag-width"), 1)*staff_space;
38 double l = robust_scm2double (me->get_property ("zigzag-length"), 1)* w;
39 double h = l > w / 2 ? sqrt (l*l - w*w / 4) : 0;
41 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
45 scm_make_real (thick),
50 b.add_point (Offset (0, 0));
52 b[X_AXIS].widen (thick / 2);
53 b[Y_AXIS].widen (thick / 2);
55 return Stencil (b, list);
58 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
60 Line_spanner::after_line_breaking (SCM g)
62 Grob *me = unsmob_grob (g);
63 Spanner *sp = dynamic_cast<Spanner *> (me);
66 We remove the line at the start of the line. For piano voice
67 indicators, it makes no sense to have them at the start of the
70 I'm not sure what the official rules for glissandi are, but
71 usually the 2nd note of the glissando is "exact", so when playing
72 from the start of the line, there is no need to glide.
74 From a typographical p.o.v. this makes sense, since the amount of
75 space left of a note at the start of a line is very small.
80 if (sp->get_bound (LEFT)->break_status_dir ()
81 && !sp->get_bound (RIGHT)->break_status_dir ())
84 Can't do suicide, since this mucks up finding the trend.
86 me->set_property ("print-function", SCM_EOL);
93 Line_spanner::line_stencil (Grob *me,
98 SCM type = me->get_property ("style");
99 if (scm_is_symbol (type)
100 && (type == ly_symbol2scm ("line")
101 || type == ly_symbol2scm ("dashed-line")
102 || type == ly_symbol2scm ("dotted-line")
103 || type == ly_symbol2scm ("zigzag")
104 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
106 return (type == ly_symbol2scm ("zigzag"))
107 ? zigzag_stencil (me, from, to)
108 : Line_interface::line (me, from, to);
110 else if (scm_is_symbol (type)
111 && type == ly_symbol2scm ("trill"))
113 SCM alist_chain = Font_interface::text_font_alist_chain (me);
114 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
115 ly_symbol2scm ("fetaMusic")),
118 Font_metric *fm = select_font (me->get_layout (),
119 scm_cons (style_alist,
121 Stencil m = fm->find_by_name ("scripts.trill_element");
125 mol.add_at_edge (X_AXIS, RIGHT, m, 0, 0);
126 while (m.extent (X_AXIS).length ()
127 && mol.extent (X_AXIS).length ()
128 + m.extent (X_AXIS).length () < dz[X_AXIS]);
131 FIXME: should center element on x/y
133 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
134 mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
135 + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
137 mol.translate (from);
144 Find a common Y parent, which --if found-- should be the
145 fixed-distance alignment.
148 line_spanner_common_parent (Grob *me)
150 Grob *common = find_fixed_alignment_parent (me);
153 common = Staff_symbol_referencer::get_staff_symbol (me);
155 common = common->get_parent (Y_AXIS);
157 common = me->get_parent (Y_AXIS);
164 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
166 (If not, you risk that this is called from the staff-alignment
167 routine, via stencil_extent. At this point, the staves aren't
168 separated yet, so it doesn't work cross-staff.
170 (huh? crossable staves have fixed distance? --hwn)
173 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
175 Line_spanner::print (SCM smob)
177 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
179 Drul_array<Item *> bound (me->get_bound (LEFT),
180 me->get_bound (RIGHT));
182 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
184 Offset ofxy (gap, 0); /*offset from start point to start of line*/
189 if (bound[RIGHT]->break_status_dir ())
191 if (bound[LEFT]->break_status_dir ())
193 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
200 This is hairy. For the normal case, we simply find common
201 parents, and draw a line between the bounds. When two note
202 heads are on different systems, there is no common parent
203 anymore. We have to find the piano-staff object.
206 int k = broken_spanner_index (me);
207 Spanner *parent_sp = dynamic_cast<Spanner *> (me->original_);
208 Spanner *next_sp = parent_sp->broken_intos_ [k + 1];
209 Item *next_bound = next_sp->get_bound (RIGHT);
211 if (next_bound->break_status_dir ())
213 programming_error ("no note heads for the line spanner on next line?"
219 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
220 commonx = me->common_refpoint (commonx, X_AXIS);
222 Grob *next_common_y = line_spanner_common_parent (next_bound);
223 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
225 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
227 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
228 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
230 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
232 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
233 this_ext.center () + yoff);
234 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
235 next_ext.center () + yoff);
238 Real len = dz.length ();
240 Offset dir = dz * (1/ len);
241 dz = (dz.length () - 2*gap) *dir;
243 Stencil l (line_stencil (me, Offset (0, 0), dz));
245 l.translate (dir * gap + p1
246 - Offset (me->relative_coordinate (commonx, X_AXIS),
247 me->relative_coordinate (all_common_y, Y_AXIS)));
249 return l.smobbed_copy ();
253 Grob *common[] = { me, me };
254 for (int a = X_AXIS; a < NO_AXES; a++)
256 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
257 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
260 // distance from center to start of line
261 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3) / 4);
263 for (int a = X_AXIS; a < NO_AXES; a++)
267 = + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
268 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
270 my_off[ax] = me->relative_coordinate (common[a], ax);
271 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
275 ofxy = dxy * (off / dxy.length ());
278 Stencil line = line_stencil (me, Offset (0, 0), dxy);
280 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
281 line.translate (ofxy - my_off + his_off);
282 return line.smobbed_copy ();
286 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
287 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
288 "The property @code{style} can be @code{line}, "
289 "@code{dashed-line}, @code{trill}, \n"
290 "@code{dotted-line} or @code{zigzag}.\n"
292 "gap zigzag-width zigzag-length thickness");