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 if (bound[RIGHT]->break_status_dir ())
210 if (bound[LEFT]->break_status_dir ())
212 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
219 This is hairy. For the normal case, we simply find common
220 parents, and draw a line between the bounds. When two note
221 heads are on different systems, there is no common parent
222 anymore. We have to find the piano-staff object.
225 Spanner *next_sp = me->broken_neighbor (RIGHT);
226 Item *next_bound = next_sp->get_bound (RIGHT);
228 if (next_bound->break_status_dir ())
230 programming_error ("no note heads for the line spanner on next line?"
236 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
237 commonx = me->common_refpoint (commonx, X_AXIS);
239 Grob *next_common_y = line_spanner_common_parent (next_bound);
240 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
242 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
244 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
245 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
247 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
249 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
250 this_ext.center () + yoff);
251 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
252 next_ext.center () + yoff);
255 Real len = dz.length ();
257 Offset dir = dz * (1 / len);
258 dz = (dz.length () - 2 * gap) * dir;
260 Stencil l (line_stencil (me, Offset (0, 0), dz));
262 l.translate (dir * gap + p1
263 - Offset (me->relative_coordinate (commonx, X_AXIS),
264 me->relative_coordinate (all_common_y, Y_AXIS)));
266 return l.smobbed_copy ();
270 Grob *common[] = { me, me };
271 for (int a = X_AXIS; a < NO_AXES; a++)
273 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
274 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
277 // distance from center to start of line
278 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
280 for (int a = X_AXIS; a < NO_AXES; a++)
284 = + robust_relative_extent (bound[RIGHT], common[X_AXIS], ax).center ()
285 - robust_relative_extent (bound[LEFT], common[X_AXIS], ax).center ();
287 my_off[ax] = me->relative_coordinate (common[a], ax);
288 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
291 ofxy = dxy * (off / dxy.length ());
294 Stencil line = line_stencil (me, Offset (0, 0), dxy);
296 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
297 line.translate (ofxy - my_off + his_off);
298 return line.smobbed_copy ();
302 ADD_INTERFACE (Line_spanner,
303 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
304 "The property @code{style} can be @code{line}, "
305 "@code{dashed-line}, @code{trill}, \n"
306 "@code{dotted-line} or @code{zigzag}.\n"