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);
94 Line_spanner::line_stencil (Grob *me,
98 Offset dz = to -from ;
99 SCM type = me->get_property ("style");
100 if (scm_is_symbol (type)
101 && (type == ly_symbol2scm ("line")
102 || type == ly_symbol2scm ("dashed-line")
103 || type == ly_symbol2scm ("dotted-line")
104 || type == ly_symbol2scm ("zigzag")
105 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
107 return (type == ly_symbol2scm ("zigzag"))
108 ? zigzag_stencil (me, from, to)
109 : Line_interface::line (me, from, to);
111 else if (scm_is_symbol (type)
112 && type == ly_symbol2scm ("trill"))
114 SCM alist_chain = Font_interface::text_font_alist_chain (me);
115 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
116 ly_symbol2scm ("fetaMusic")),
119 Font_metric *fm = select_font (me->get_layout (),
120 scm_cons (style_alist,
122 Stencil m = fm->find_by_name ("scripts.trill_element");
126 mol.add_at_edge (X_AXIS, RIGHT, m, 0, 0);
127 while (m.extent (X_AXIS).length ()
128 && mol.extent (X_AXIS).length ()
129 + m.extent (X_AXIS).length () < dz[X_AXIS]);
132 FIXME: should center element on x/y
134 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
135 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
136 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
138 mol.translate (from);
145 Find a common Y parent, which --if found-- should be the
146 fixed-distance alignment.
149 line_spanner_common_parent (Grob *me)
151 Grob * common = find_fixed_alignment_parent (me);
154 common = Staff_symbol_referencer::get_staff_symbol (me);
156 common = common->get_parent (Y_AXIS);
158 common = me->get_parent (Y_AXIS);
165 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
167 (If not, you risk that this is called from the staff-alignment
168 routine, via stencil_extent. At this point, the staves aren't
169 separated yet, so it doesn't work cross-staff.
171 (huh? crossable staves have fixed distance? --hwn)
177 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
179 Line_spanner::print (SCM smob)
181 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
183 Drul_array<Item*> bound (me->get_bound (LEFT),
184 me->get_bound (RIGHT));
187 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
189 Offset ofxy (gap, 0); /*offset from start point to start of line*/
195 if (bound[RIGHT]->break_status_dir ())
197 if (bound[LEFT]->break_status_dir ())
199 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
206 This is hairy. For the normal case, we simply find common
207 parents, and draw a line between the bounds. When two note
208 heads are on different systems, there is no common parent
209 anymore. We have to find the piano-staff object.
212 int k = broken_spanner_index (me);
213 Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
214 Spanner *next_sp = parent_sp->broken_intos_ [k+1];
215 Item *next_bound = next_sp->get_bound (RIGHT);
217 if (next_bound->break_status_dir ())
219 programming_error ("no note heads for the line spanner on next line?"
225 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
226 commonx = me->common_refpoint (commonx, X_AXIS);
228 Grob *next_common_y = line_spanner_common_parent (next_bound);
229 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
231 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
233 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
234 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
236 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
238 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
239 this_ext.center () + yoff);
240 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
241 next_ext.center () + yoff);
244 Real len = dz.length ();
246 Offset dir = dz *(1/ len);
247 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);
282 ofxy = dxy * (off/dxy.length ());
285 Stencil line = line_stencil (me, Offset (0, 0), dxy);
287 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()/2, X_AXIS);
288 line.translate (ofxy - my_off + his_off);
289 return line.smobbed_copy ();
294 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
295 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
296 "The property @code{style} can be @code{line}, "
297 "@code{dashed-line}, @code{trill}, \n"
298 "@code{dotted-line} or @code{zigzag}.\n"
301 "gap zigzag-width zigzag-length thickness");