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 int k = broken_spanner_index (me);
226 Spanner *parent_sp = dynamic_cast<Spanner *> (me->original ());
227 Spanner *next_sp = parent_sp->broken_intos_ [k + 1];
228 Item *next_bound = next_sp->get_bound (RIGHT);
230 if (next_bound->break_status_dir ())
232 programming_error ("no note heads for the line spanner on next line?"
238 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
239 commonx = me->common_refpoint (commonx, X_AXIS);
241 Grob *next_common_y = line_spanner_common_parent (next_bound);
242 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
244 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
246 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
247 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
249 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
251 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
252 this_ext.center () + yoff);
253 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
254 next_ext.center () + yoff);
257 Real len = dz.length ();
259 Offset dir = dz * (1 / len);
260 dz = (dz.length () - 2 * gap) * dir;
262 Stencil l (line_stencil (me, Offset (0, 0), dz));
264 l.translate (dir * gap + p1
265 - Offset (me->relative_coordinate (commonx, X_AXIS),
266 me->relative_coordinate (all_common_y, Y_AXIS)));
268 return l.smobbed_copy ();
272 Grob *common[] = { me, me };
273 for (int a = X_AXIS; a < NO_AXES; a++)
275 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
276 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
279 // distance from center to start of line
280 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
282 for (int a = X_AXIS; a < NO_AXES; a++)
286 = + robust_relative_extent (bound[RIGHT], common[X_AXIS], ax).center ()
287 - robust_relative_extent (bound[LEFT], common[X_AXIS], ax).center ();
289 my_off[ax] = me->relative_coordinate (common[a], ax);
290 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
293 ofxy = dxy * (off / dxy.length ());
296 Stencil line = line_stencil (me, Offset (0, 0), dxy);
298 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
299 line.translate (ofxy - my_off + his_off);
300 return line.smobbed_copy ();
304 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
305 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
306 "The property @code{style} can be @code{line}, "
307 "@code{dashed-line}, @code{trill}, \n"
308 "@code{dotted-line} or @code{zigzag}.\n"