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"
15 #include "output-def.hh"
16 #include "paper-column.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "font-interface.hh"
20 #include "align-interface.hh"
22 #include "line-interface.hh"
25 zigzag_stencil (Grob *me,
31 Real thick = Staff_symbol_referencer::line_thickness (me);
32 thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer?
34 Real staff_space = Staff_symbol_referencer::staff_space (me);
36 Real w = robust_scm2double (me->get_property ("zigzag-width"), 1) * staff_space;
37 int count = (int) ceil (dz.length () / w);
38 w = dz.length () / count;
40 Real l = robust_scm2double (me->get_property ("zigzag-length"), 1) * w;
41 Real h = l > w / 2 ? sqrt (l * l - w * w / 4) : 0;
43 Offset rotation_factor = complex_exp (Offset (0, dz.arg ()));
46 points[0] = Offset (0, -h / 2);
47 points[1] = Offset (w / 2, h / 2);
48 points[2] = Offset (w, -h / 2);
49 for (int i = 0; i < 3; i++)
50 points[i] = complex_multiply (points[i], rotation_factor);
52 Stencil squiggle (Line_interface::make_line (thick, points[0], points[1]));
53 squiggle.add_stencil (Line_interface::make_line (thick, points[1], points[2]));
56 for (int i = 0; i < count; i++)
58 Stencil moved_squiggle (squiggle);
59 moved_squiggle.translate (from + Offset (i * w, 0) * rotation_factor);
60 total.add_stencil (moved_squiggle);
64 b.add_point (Offset (0, 0));
66 b[X_AXIS].widen (thick / 2);
67 b[Y_AXIS].widen (thick / 2);
69 return Stencil (b, total.expr ());
72 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
74 Line_spanner::after_line_breaking (SCM g)
76 Grob *me = unsmob_grob (g);
77 Spanner *sp = dynamic_cast<Spanner *> (me);
80 We remove the line at the start of the line. For piano voice
81 indicators, it makes no sense to have them at the start of the
84 I'm not sure what the official rules for glissandi are, but
85 usually the 2nd note of the glissando is "exact", so when playing
86 from the start of the line, there is no need to glide.
88 From a typographical p.o.v. this makes sense, since the amount of
89 space left of a note at the start of a line is very small.
94 if (sp->get_bound (LEFT)->break_status_dir ()
95 && !sp->get_bound (RIGHT)->break_status_dir ())
98 Can't do suicide, since this mucks up finding the trend.
100 me->set_property ("print-function", SCM_EOL);
106 Line_spanner::line_stencil (Grob *me,
110 Offset dz = to -from;
111 SCM type = me->get_property ("style");
115 if (scm_is_symbol (type)
116 && (type == ly_symbol2scm ("line")
117 || type == ly_symbol2scm ("dashed-line")
118 || type == ly_symbol2scm ("dotted-line")
119 || type == ly_symbol2scm ("zigzag")
120 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
122 line = (type == ly_symbol2scm ("zigzag"))
123 ? zigzag_stencil (me, from, to)
124 : Line_interface::line (me, from, to);
126 else if (scm_is_symbol (type)
127 && type == ly_symbol2scm ("trill"))
129 SCM alist_chain = Font_interface::text_font_alist_chain (me);
130 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
131 ly_symbol2scm ("fetaMusic")),
134 Font_metric *fm = select_font (me->get_layout (),
135 scm_cons (style_alist,
137 Stencil m = fm->find_by_name ("scripts.trill_element");
141 mol.add_at_edge (X_AXIS, RIGHT, m, 0, 0);
142 while (m.extent (X_AXIS).length ()
143 && mol.extent (X_AXIS).length ()
144 + m.extent (X_AXIS).length () < dz[X_AXIS])
148 FIXME: should center element on x/y
150 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
151 mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
152 + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
154 mol.translate (from);
158 if (to_boolean (me->get_property ("arrow")))
159 line.add_stencil (Line_interface::arrows (me, from, to, false, true));
165 Find a common Y parent, which --if found-- should be the
166 fixed-distance alignment.
169 line_spanner_common_parent (Grob *me)
171 Grob *common = find_fixed_alignment_parent (me);
174 common = Staff_symbol_referencer::get_staff_symbol (me);
176 common = common->get_parent (Y_AXIS);
178 common = me->get_parent (Y_AXIS);
185 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
187 (If not, you risk that this is called from the staff-alignment
188 routine, via stencil_extent. At this point, the staves aren't
189 separated yet, so it doesn't work cross-staff.
191 (huh? crossable staves have fixed distance? --hwn)
194 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
196 Line_spanner::print (SCM smob)
198 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
200 Drul_array<Item *> bound (me->get_bound (LEFT),
201 me->get_bound (RIGHT));
203 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
205 Offset ofxy (gap, 0); /* offset from start point to start of line */
210 if (bound[RIGHT]->break_status_dir ())
212 if (bound[LEFT]->break_status_dir ())
214 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
221 This is hairy. For the normal case, we simply find common
222 parents, and draw a line between the bounds. When two note
223 heads are on different systems, there is no common parent
224 anymore. We have to find the piano-staff object.
227 int k = broken_spanner_index (me);
228 Spanner *parent_sp = dynamic_cast<Spanner *> (me->original_);
229 Spanner *next_sp = parent_sp->broken_intos_ [k + 1];
230 Item *next_bound = next_sp->get_bound (RIGHT);
232 if (next_bound->break_status_dir ())
234 programming_error ("no note heads for the line spanner on next line?"
240 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
241 commonx = me->common_refpoint (commonx, X_AXIS);
243 Grob *next_common_y = line_spanner_common_parent (next_bound);
244 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
246 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
248 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
249 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
251 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
253 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
254 this_ext.center () + yoff);
255 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
256 next_ext.center () + yoff);
259 Real len = dz.length ();
261 Offset dir = dz * (1 / len);
262 dz = (dz.length () - 2 * gap) * dir;
264 Stencil l (line_stencil (me, Offset (0, 0), dz));
266 l.translate (dir * gap + p1
267 - Offset (me->relative_coordinate (commonx, X_AXIS),
268 me->relative_coordinate (all_common_y, Y_AXIS)));
270 return l.smobbed_copy ();
274 Grob *common[] = { me, me };
275 for (int a = X_AXIS; a < NO_AXES; a++)
277 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
278 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
281 // distance from center to start of line
282 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
284 for (int a = X_AXIS; a < NO_AXES; a++)
288 = + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
289 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
291 my_off[ax] = me->relative_coordinate (common[a], ax);
292 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
295 ofxy = dxy * (off / dxy.length ());
298 Stencil line = line_stencil (me, Offset (0, 0), dxy);
300 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
301 line.translate (ofxy - my_off + his_off);
302 return line.smobbed_copy ();
306 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
307 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
308 "The property @code{style} can be @code{line}, "
309 "@code{dashed-line}, @code{trill}, \n"
310 "@code{dotted-line} or @code{zigzag}.\n"
312 "gap zigzag-width zigzag-length thickness arrow");