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,
32 Real thick = Staff_symbol_referencer::line_thickness (me);
33 thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer?
35 Real staff_space = Staff_symbol_referencer::staff_space (me);
37 double w = robust_scm2double (me->get_property ("zigzag-width"), 1) * staff_space;
38 double l = robust_scm2double (me->get_property ("zigzag-length"), 1) * w;
39 double h = l > w / 2 ? sqrt (l * l - w * w / 4) : 0;
41 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
45 scm_make_real (thick),
50 b.add_point (Offset (0, 0));
52 b[X_AXIS].widen (thick / 2);
53 b[Y_AXIS].widen (thick / 2);
55 return Stencil (b, list);
58 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
60 Line_spanner::after_line_breaking (SCM g)
62 Grob *me = unsmob_grob (g);
63 Spanner *sp = dynamic_cast<Spanner *> (me);
66 We remove the line at the start of the line. For piano voice
67 indicators, it makes no sense to have them at the start of the
70 I'm not sure what the official rules for glissandi are, but
71 usually the 2nd note of the glissando is "exact", so when playing
72 from the start of the line, there is no need to glide.
74 From a typographical p.o.v. this makes sense, since the amount of
75 space left of a note at the start of a line is very small.
80 if (sp->get_bound (LEFT)->break_status_dir ()
81 && !sp->get_bound (RIGHT)->break_status_dir ())
84 Can't do suicide, since this mucks up finding the trend.
86 me->set_property ("print-function", SCM_EOL);
92 Line_spanner::line_stencil (Grob *me,
97 SCM type = me->get_property ("style");
101 if (scm_is_symbol (type)
102 && (type == ly_symbol2scm ("line")
103 || type == ly_symbol2scm ("dashed-line")
104 || type == ly_symbol2scm ("dotted-line")
105 || type == ly_symbol2scm ("zigzag")
106 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
108 line = (type == ly_symbol2scm ("zigzag"))
109 ? zigzag_stencil (me, from, to)
110 : Line_interface::line (me, from, to);
112 else if (scm_is_symbol (type)
113 && type == ly_symbol2scm ("trill"))
115 SCM alist_chain = Font_interface::text_font_alist_chain (me);
116 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
117 ly_symbol2scm ("fetaMusic")),
120 Font_metric *fm = select_font (me->get_layout (),
121 scm_cons (style_alist,
123 Stencil m = fm->find_by_name ("scripts.trill_element");
127 mol.add_at_edge (X_AXIS, RIGHT, m, 0, 0);
128 while (m.extent (X_AXIS).length ()
129 && mol.extent (X_AXIS).length ()
130 + m.extent (X_AXIS).length () < dz[X_AXIS])
134 FIXME: should center element on x/y
136 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
137 mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
138 + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
140 mol.translate (from);
144 if (to_boolean (me->get_property ("arrow")))
145 line.add_stencil (Line_interface::arrows (me, from, to, false, true));
151 Find a common Y parent, which --if found-- should be the
152 fixed-distance alignment.
155 line_spanner_common_parent (Grob *me)
157 Grob *common = find_fixed_alignment_parent (me);
160 common = Staff_symbol_referencer::get_staff_symbol (me);
162 common = common->get_parent (Y_AXIS);
164 common = me->get_parent (Y_AXIS);
171 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
173 (If not, you risk that this is called from the staff-alignment
174 routine, via stencil_extent. At this point, the staves aren't
175 separated yet, so it doesn't work cross-staff.
177 (huh? crossable staves have fixed distance? --hwn)
180 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
182 Line_spanner::print (SCM smob)
184 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
186 Drul_array<Item *> bound (me->get_bound (LEFT),
187 me->get_bound (RIGHT));
189 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
191 Offset ofxy (gap, 0); /* offset from start point to start of line */
196 if (bound[RIGHT]->break_status_dir ())
198 if (bound[LEFT]->break_status_dir ())
200 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
207 This is hairy. For the normal case, we simply find common
208 parents, and draw a line between the bounds. When two note
209 heads are on different systems, there is no common parent
210 anymore. We have to find the piano-staff object.
213 int k = broken_spanner_index (me);
214 Spanner *parent_sp = dynamic_cast<Spanner *> (me->original_);
215 Spanner *next_sp = parent_sp->broken_intos_ [k + 1];
216 Item *next_bound = next_sp->get_bound (RIGHT);
218 if (next_bound->break_status_dir ())
220 programming_error ("no note heads for the line spanner on next line?"
226 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
227 commonx = me->common_refpoint (commonx, X_AXIS);
229 Grob *next_common_y = line_spanner_common_parent (next_bound);
230 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
232 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
234 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
235 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
237 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
239 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
240 this_ext.center () + yoff);
241 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
242 next_ext.center () + yoff);
245 Real len = dz.length ();
247 Offset dir = dz * (1 / len);
248 dz = (dz.length () - 2 * gap) * dir;
250 Stencil l (line_stencil (me, Offset (0, 0), dz));
252 l.translate (dir * gap + p1
253 - Offset (me->relative_coordinate (commonx, X_AXIS),
254 me->relative_coordinate (all_common_y, Y_AXIS)));
256 return l.smobbed_copy ();
260 Grob *common[] = { me, me };
261 for (int a = X_AXIS; a < NO_AXES; a++)
263 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
264 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
267 // distance from center to start of line
268 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
270 for (int a = X_AXIS; a < NO_AXES; a++)
274 = + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
275 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
277 my_off[ax] = me->relative_coordinate (common[a], ax);
278 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
281 ofxy = dxy * (off / dxy.length ());
284 Stencil line = line_stencil (me, Offset (0, 0), dxy);
286 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
287 line.translate (ofxy - my_off + his_off);
288 return line.smobbed_copy ();
292 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
293 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
294 "The property @code{style} can be @code{line}, "
295 "@code{dashed-line}, @code{trill}, \n"
296 "@code{dotted-line} or @code{zigzag}.\n"
298 "gap zigzag-width zigzag-length thickness arrow");