2 line-spanner.cc -- implement Line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2004 Jan Nieuwenhuizen <janneke@gnu.org>
14 #include "line-spanner.hh"
15 #include "output-def.hh"
16 #include "paper-column.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "font-interface.hh"
20 #include "align-interface.hh"
22 #include "line-interface.hh"
25 zigzag_stencil (Grob *me,
33 Real thick = Staff_symbol_referencer::line_thickness (me);
34 thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer?
36 Real staff_space = Staff_symbol_referencer::staff_space (me);
38 double w = robust_scm2double (me->get_property ("zigzag-width"), 1)*staff_space;
39 double l = robust_scm2double ( me->get_property ("zigzag-length"), 1)* w;
40 double h = l>w/2 ? sqrt (l*l-w*w/4) : 0;
42 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
46 scm_make_real (thick),
51 b.add_point (Offset (0,0));
53 b[X_AXIS].widen (thick/2);
54 b[Y_AXIS].widen (thick/2);
56 return Stencil (b, list);
59 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
61 Line_spanner::after_line_breaking (SCM g)
63 Grob *me = unsmob_grob (g);
64 Spanner*sp = dynamic_cast<Spanner*> (me);
67 We remove the line at the start of the line. For piano voice
68 indicators, it makes no sense to have them at the start of the
71 I'm not sure what the official rules for glissandi are, but
72 usually the 2nd note of the glissando is "exact", so when playing
73 from the start of the line, there is no need to glide.
75 From a typographical p.o.v. this makes sense, since the amount of
76 space left of a note at the start of a line is very small.
81 if (sp->get_bound (LEFT)->break_status_dir ()
82 && !sp->get_bound (RIGHT)->break_status_dir ())
85 Can't do suicide, since this mucks up finding the trend.
87 me->set_property ("print-function", SCM_EOL);
95 Line_spanner::line_stencil (Grob *me,
99 Offset dz = to -from ;
100 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 return (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]);
133 FIXME: should center element on x/y
135 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
136 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
137 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
139 mol.translate (from);
146 Find a common Y parent, which --if found-- should be the
147 fixed-distance alignment.
150 line_spanner_common_parent (Grob *me)
152 Grob * common = find_fixed_alignment_parent (me);
155 common = Staff_symbol_referencer::get_staff_symbol (me);
157 common = common->get_parent (Y_AXIS);
159 common = me->get_parent (Y_AXIS);
166 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
168 (If not, you risk that this is called from the staff-alignment
169 routine, via stencil_extent. At this point, the staves aren't
170 separated yet, so it doesn't work cross-staff.
172 (huh? crossable staves have fixed distance? --hwn)
178 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
180 Line_spanner::print (SCM smob)
182 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
184 Drul_array<Item*> bound (me->get_bound (LEFT),
185 me->get_bound (RIGHT));
188 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
190 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;
251 Stencil l (line_stencil (me, Offset (0, 0), dz));
253 l.translate (dir * gap + p1
254 - Offset (me->relative_coordinate (commonx, X_AXIS),
255 me->relative_coordinate (all_common_y, Y_AXIS)));
257 return l.smobbed_copy ();
261 Grob *common[] = { me, me };
262 for (int a = X_AXIS; a < NO_AXES; a++)
264 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
265 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
268 // distance from center to start of line
269 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
271 for (int a = X_AXIS; a < NO_AXES; a++)
275 + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
276 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
278 my_off[ax] =me->relative_coordinate (common[a], ax);
279 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
283 ofxy = dxy * (off/dxy.length ());
286 Stencil line = line_stencil (me, Offset (0,0),dxy);
288 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()/2, X_AXIS);
289 line.translate (ofxy - my_off + his_off);
290 return line.smobbed_copy ();
295 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
296 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
297 "The property @code{style} can be @code{line}, "
298 "@code{dashed-line}, @code{trill}, \n"
299 "@code{dotted-line} or @code{zigzag}.\n"
302 "gap zigzag-width zigzag-length thickness");