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,
30 Real thick = Staff_symbol_referencer::line_thickness (me);
31 thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer?
33 Real staff_space = Staff_symbol_referencer::staff_space (me);
35 Real w = robust_scm2double (me->get_property ("zigzag-width"), 1) * staff_space;
36 int count = (int) ceil (dz.length () / w);
37 w = dz.length () / count;
39 Real l = robust_scm2double (me->get_property ("zigzag-length"), 1) * w;
40 Real h = l > w / 2 ? sqrt (l * l - w * w / 4) : 0;
42 Offset rotation_factor = complex_exp (Offset (0, dz.arg ()));
45 points[0] = Offset (0, -h / 2);
46 points[1] = Offset (w / 2, h / 2);
47 points[2] = Offset (w, -h / 2);
48 for (int i = 0; i < 3; i++)
49 points[i] = complex_multiply (points[i], rotation_factor);
51 Stencil squiggle (Line_interface::make_line (thick, points[0], points[1]));
52 squiggle.add_stencil (Line_interface::make_line (thick, points[1], points[2]));
55 for (int i = 0; i < count; i++)
57 Stencil moved_squiggle (squiggle);
58 moved_squiggle.translate (from + Offset (i * w, 0) * rotation_factor);
59 total.add_stencil (moved_squiggle);
63 b.add_point (Offset (0, 0));
65 b[X_AXIS].widen (thick / 2);
66 b[Y_AXIS].widen (thick / 2);
68 return Stencil (b, total.expr ());
71 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
73 Line_spanner::after_line_breaking (SCM g)
75 Grob *me = unsmob_grob (g);
76 Spanner *sp = dynamic_cast<Spanner *> (me);
79 We remove the line at the start of the line. For piano voice
80 indicators, it makes no sense to have them at the start of the
83 I'm not sure what the official rules for glissandi are, but
84 usually the 2nd note of the glissando is "exact", so when playing
85 from the start of the line, there is no need to glide.
87 From a typographical p.o.v. this makes sense, since the amount of
88 space left of a note at the start of a line is very small.
93 if (sp->get_bound (LEFT)->break_status_dir ()
94 && !sp->get_bound (RIGHT)->break_status_dir ())
97 Can't do suicide, since this mucks up finding the trend.
99 me->set_property ("print-function", SCM_EOL);
105 Line_spanner::line_stencil (Grob *me,
109 Offset dz = to -from;
110 SCM type = me->get_property ("style");
114 if (scm_is_symbol (type)
115 && (type == ly_symbol2scm ("line")
116 || type == ly_symbol2scm ("dashed-line")
117 || type == ly_symbol2scm ("dotted-line")
118 || type == ly_symbol2scm ("zigzag")
119 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
121 line = (type == ly_symbol2scm ("zigzag"))
122 ? zigzag_stencil (me, from, to)
123 : Line_interface::line (me, from, to);
125 else if (scm_is_symbol (type)
126 && type == ly_symbol2scm ("trill"))
128 SCM alist_chain = Font_interface::text_font_alist_chain (me);
129 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
130 ly_symbol2scm ("fetaMusic")),
133 Font_metric *fm = select_font (me->get_layout (),
134 scm_cons (style_alist,
136 Stencil m = fm->find_by_name ("scripts.trill_element");
140 mol.add_at_edge (X_AXIS, RIGHT, m, 0, 0);
141 while (m.extent (X_AXIS).length ()
142 && mol.extent (X_AXIS).length ()
143 + m.extent (X_AXIS).length () < dz[X_AXIS])
147 FIXME: should center element on x/y
149 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
150 mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
151 + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
153 mol.translate (from);
157 if (to_boolean (me->get_property ("arrow")))
158 line.add_stencil (Line_interface::arrows (me, from, to, false, true));
164 Find a common Y parent, which --if found-- should be the
165 fixed-distance alignment.
168 line_spanner_common_parent (Grob *me)
170 Grob *common = find_fixed_alignment_parent (me);
173 common = Staff_symbol_referencer::get_staff_symbol (me);
175 common = common->get_parent (Y_AXIS);
177 common = me->get_parent (Y_AXIS);
184 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
186 (If not, you risk that this is called from the staff-alignment
187 routine, via stencil_extent. At this point, the staves aren't
188 separated yet, so it doesn't work cross-staff.
190 (huh? crossable staves have fixed distance? --hwn)
193 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
195 Line_spanner::print (SCM smob)
197 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
199 Drul_array<Item *> bound (me->get_bound (LEFT),
200 me->get_bound (RIGHT));
202 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
204 Offset ofxy (gap, 0); /* offset from start point to start of line */
209 if (bound[RIGHT]->break_status_dir ())
211 if (bound[LEFT]->break_status_dir ())
213 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
220 This is hairy. For the normal case, we simply find common
221 parents, and draw a line between the bounds. When two note
222 heads are on different systems, there is no common parent
223 anymore. We have to find the piano-staff object.
226 int k = broken_spanner_index (me);
227 Spanner *parent_sp = dynamic_cast<Spanner *> (me->original_);
228 Spanner *next_sp = parent_sp->broken_intos_ [k + 1];
229 Item *next_bound = next_sp->get_bound (RIGHT);
231 if (next_bound->break_status_dir ())
233 programming_error ("no note heads for the line spanner on next line?"
239 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
240 commonx = me->common_refpoint (commonx, X_AXIS);
242 Grob *next_common_y = line_spanner_common_parent (next_bound);
243 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
245 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
247 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
248 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
250 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
252 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
253 this_ext.center () + yoff);
254 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
255 next_ext.center () + yoff);
258 Real len = dz.length ();
260 Offset dir = dz * (1 / len);
261 dz = (dz.length () - 2 * gap) * dir;
263 Stencil l (line_stencil (me, Offset (0, 0), dz));
265 l.translate (dir * gap + p1
266 - Offset (me->relative_coordinate (commonx, X_AXIS),
267 me->relative_coordinate (all_common_y, Y_AXIS)));
269 return l.smobbed_copy ();
273 Grob *common[] = { me, me };
274 for (int a = X_AXIS; a < NO_AXES; a++)
276 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
277 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
280 // distance from center to start of line
281 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
283 for (int a = X_AXIS; a < NO_AXES; a++)
287 = + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
288 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
290 my_off[ax] = me->relative_coordinate (common[a], ax);
291 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
294 ofxy = dxy * (off / dxy.length ());
297 Stencil line = line_stencil (me, Offset (0, 0), dxy);
299 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
300 line.translate (ofxy - my_off + his_off);
301 return line.smobbed_copy ();
305 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
306 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
307 "The property @code{style} can be @code{line}, "
308 "@code{dashed-line}, @code{trill}, \n"
309 "@code{dotted-line} or @code{zigzag}.\n"
311 "gap zigzag-width zigzag-length thickness arrow");