2 line-spanner.cc -- implement Line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2006 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"
23 zigzag_stencil (Grob *me,
29 Real thick = Staff_symbol_referencer::line_thickness (me);
30 thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer?
32 Real staff_space = Staff_symbol_referencer::staff_space (me);
34 Real w = robust_scm2double (me->get_property ("zigzag-width"), 1) * staff_space;
35 int count = (int) ceil (dz.length () / w);
36 w = dz.length () / count;
38 Real l = robust_scm2double (me->get_property ("zigzag-length"), 1) * w;
39 Real h = l > w / 2 ? sqrt (l * l - w * w / 4) : 0;
41 Offset rotation_factor = complex_exp (Offset (0, dz.arg ()));
44 points[0] = Offset (0, -h / 2);
45 points[1] = Offset (w / 2, h / 2);
46 points[2] = Offset (w, -h / 2);
47 for (int i = 0; i < 3; i++)
48 points[i] = complex_multiply (points[i], rotation_factor);
50 Stencil squiggle (Line_interface::make_line (thick, points[0], points[1]));
51 squiggle.add_stencil (Line_interface::make_line (thick, points[1], points[2]));
54 for (int i = 0; i < count; i++)
56 Stencil moved_squiggle (squiggle);
57 moved_squiggle.translate (from + Offset (i * w, 0) * rotation_factor);
58 total.add_stencil (moved_squiggle);
62 b.add_point (Offset (0, 0));
64 b[X_AXIS].widen (thick / 2);
65 b[Y_AXIS].widen (thick / 2);
67 return Stencil (b, total.expr ());
70 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
72 Line_spanner::after_line_breaking (SCM g)
74 Grob *me = unsmob_grob (g);
75 Spanner *sp = dynamic_cast<Spanner *> (me);
78 We remove the line at the start of the line. For piano voice
79 indicators, it makes no sense to have them at the start of the
82 I'm not sure what the official rules for glissandi are, but
83 usually the 2nd note of the glissando is "exact", so when playing
84 from the start of the line, there is no need to glide.
86 From a typographical p.o.v. this makes sense, since the amount of
87 space left of a note at the start of a line is very small.
92 if (sp->get_bound (LEFT)->break_status_dir ()
93 && !sp->get_bound (RIGHT)->break_status_dir ())
96 Can't do suicide, since this mucks up finding the trend.
98 me->set_property ("transparent", SCM_BOOL_T);
104 Line_spanner::line_stencil (Grob *me,
108 Offset dz = to -from;
109 SCM type = me->get_property ("style");
113 if (scm_is_symbol (type)
114 && (type == ly_symbol2scm ("line")
115 || type == ly_symbol2scm ("dashed-line")
116 || type == ly_symbol2scm ("dotted-line")
117 || type == ly_symbol2scm ("zigzag")
118 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
120 line = (type == ly_symbol2scm ("zigzag"))
121 ? zigzag_stencil (me, from, to)
122 : Line_interface::line (me, from, to);
124 else if (scm_is_symbol (type)
125 && type == ly_symbol2scm ("trill"))
127 SCM alist_chain = Font_interface::text_font_alist_chain (me);
128 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
129 ly_symbol2scm ("fetaMusic")),
132 Font_metric *fm = select_font (me->layout (),
133 scm_cons (style_alist,
135 Stencil m = fm->find_by_name ("scripts.trill_element");
139 mol.add_at_edge (X_AXIS, RIGHT, m, 0, 0);
140 while (m.extent (X_AXIS).length ()
141 && mol.extent (X_AXIS).length ()
142 + m.extent (X_AXIS).length () < dz[X_AXIS])
146 FIXME: should center element on x/y
148 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
149 mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
150 + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
152 mol.translate (from);
156 if (to_boolean (me->get_property ("arrow")))
157 line.add_stencil (Line_interface::arrows (me, from, to, false, true));
163 Find a common Y parent, which --if found-- should be the
164 fixed-distance alignment.
167 line_spanner_common_parent (Grob *me)
169 Grob *common = find_fixed_alignment_parent (me);
172 common = Staff_symbol_referencer::get_staff_symbol (me);
174 common = common->get_parent (Y_AXIS);
176 common = me->get_parent (Y_AXIS);
183 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
185 (If not, you risk that this is called from the staff-alignment
186 routine, via stencil_extent. At this point, the staves aren't
187 separated yet, so it doesn't work cross-staff.
189 (huh? crossable staves have fixed distance? --hwn)
192 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
194 Line_spanner::print (SCM smob)
196 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
198 Drul_array<Item *> bound (me->get_bound (LEFT),
199 me->get_bound (RIGHT));
201 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
203 Offset ofxy (gap, 0); /* offset from start point to start of line */
208 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
211 if (bound[RIGHT]->break_status_dir ())
213 if (bound[LEFT]->break_status_dir ())
215 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
222 This is hairy. For the normal case, we simply find common
223 parents, and draw a line between the bounds. When two note
224 heads are on different systems, there is no common parent
225 anymore. We have to find the piano-staff object.
228 Spanner *next_sp = me->broken_neighbor (RIGHT);
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 - extra_dy / 2);
254 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
255 next_ext.center () + yoff + extra_dy / 2);
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 = + robust_relative_extent (bound[RIGHT], common[X_AXIS], ax).center ()
288 - robust_relative_extent (bound[LEFT], 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 dxy[Y_AXIS] += extra_dy;
299 Stencil line = line_stencil (me, Offset (0, 0), dxy);
301 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
302 line.translate (ofxy - my_off + his_off + Offset (0, -extra_dy/2));
303 return line.smobbed_copy ();
307 ADD_INTERFACE (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"