2 line-spanner.cc -- implement Line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2003 Jan Nieuwenhuizen <janneke@gnu.org>
12 #include "line-spanner.hh"
13 #include "paper-def.hh"
14 #include "paper-column.hh"
15 #include "staff-symbol-referencer.hh"
16 #include "font-interface.hh"
18 #include "align-interface.hh"
23 TODO: convert all Molecule functions to taking arguments of the form
25 Offset FROM, Offset TO.
30 Introduce line-interface that allows dots/dashes/etc. to be set as
31 grob-properties. Make arbitrary paths.
36 slightishly clumsy interface?
38 Make a Scheme expression for a line going from (0,0) to (dx,dy).
42 line_atom (Grob *me, Real thick, Real dx, Real dy)
44 SCM type = me->get_grob_property ("style");
45 Real staff_space = Staff_symbol_referencer::staff_space (me);
47 // maybe these should be in line-thickness?
48 Real length = staff_space;
49 SCM s = me->get_grob_property ("dash-length");
51 length = gh_scm2double (s) * staff_space;
53 Real period = 2 * length + thick;
54 s = me->get_grob_property ("dash-period");
56 period = gh_scm2double (s) * staff_space;
58 if (type == ly_symbol2scm ("dotted-line"))
61 if (type == ly_symbol2scm ("line"))
62 length = period + thick;
64 Real on = length - thick;
65 Real off = period - on;
67 SCM list = scm_list_n (ly_symbol2scm ("dashed-line"),
68 gh_double2scm (thick),
79 zigzag_atom (Grob *me, Real thick, Real dx, Real dy)
81 Real staff_space = Staff_symbol_referencer::staff_space (me);
82 SCM ws = me->get_grob_property ("zigzag-width");
83 SCM ls = me->get_grob_property ("zigzag-length");
84 double w = (gh_number_p(ws) ? gh_scm2double(ws) : 1)*staff_space;
85 double l = (gh_number_p(ls) ? gh_scm2double(ls) : 1)*w;
86 double h = l>w/2 ? sqrt(l*l-w*w/4) : 0;
88 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
92 gh_double2scm (thick),
100 MAKE_SCHEME_CALLBACK(Line_spanner, after_line_breaking, 1);
102 Line_spanner::after_line_breaking (SCM g)
104 Grob *me = unsmob_grob (g);
105 Spanner*sp = dynamic_cast<Spanner*> (me);
108 We remove the line at the start of the line. For piano voice
109 indicators, it makes no sense to have them at the start of the
112 I'm not sure what the official rules for glissandi are, but
113 usually the 2nd note of the glissando is "exact", so when playing
114 from the start of the line, there is no need to glide.
116 From a typographical p.o.v. this makes sense, since the amount of
117 space left of a note at the start of a line is very small.
122 if (sp->get_bound (LEFT)->break_status_dir()
123 && !sp->get_bound (RIGHT)->break_status_dir())
126 Can't do suicide, since this mucks up finding the trend.
128 me->set_grob_property ("molecule-callback", SCM_EOL);
136 Line_spanner::line_molecule (Grob *me, Real thick,
140 Offset dz = to -from ;
142 SCM type = me->get_grob_property ("style");
143 if (gh_symbol_p (type)
144 && (type == ly_symbol2scm ("line")
145 || type == ly_symbol2scm ("dashed-line")
146 || type == ly_symbol2scm ("dotted-line")
147 || type == ly_symbol2scm ("zigzag")
148 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
151 b.add_point (Offset (0,0));
153 b[X_AXIS].widen (thick/2);
154 b[Y_AXIS].widen (thick/2);
156 SCM atom = (type == ly_symbol2scm ("zigzag"))
157 ? zigzag_atom (me, thick, dz[X_AXIS], dz[Y_AXIS])
158 : line_atom (me, thick, dz[X_AXIS], dz[Y_AXIS]);
160 mol = Molecule (b, atom);
161 mol.translate (from);
163 else if (gh_symbol_p (type)
164 && type == ly_symbol2scm ("trill"))
166 SCM alist_chain = Font_interface::font_alist_chain (me);
167 SCM style_alist = scm_list_n (gh_cons (ly_symbol2scm ("font-family"),
168 ly_symbol2scm ("music")),
171 Font_metric *fm = Font_interface::get_font (me,
172 gh_cons (style_alist,
174 Molecule m = fm->find_by_name ("scripts-trill-element");
176 mol.add_at_edge (X_AXIS, RIGHT, m, 0,0);
177 while (m.extent (X_AXIS).length ()
178 && mol.extent (X_AXIS).length ()
179 + m.extent (X_AXIS).length () < dz[X_AXIS]);
182 FIXME: should center element on x/y
184 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
185 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
186 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
188 mol.translate (from);
195 Find a common Y parent, which --if found-- should be the
196 fixed-distance alignment.
199 line_spanner_common_parent (Grob *me)
201 Grob * common = find_fixed_alignment_parent (me);
204 common = Staff_symbol_referencer::get_staff_symbol (me);
206 common = common->get_parent (Y_AXIS);
208 common = me->get_parent (Y_AXIS);
215 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
217 (If not, you risk that this is called from the staff-alignment
218 routine, via molecule_extent. At this point, the staves aren't
219 separated yet, so it doesn't work cross-staff.
224 MAKE_SCHEME_CALLBACK (Line_spanner, brew_molecule, 1);
226 Line_spanner::brew_molecule (SCM smob)
228 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
230 Drul_array<Item*> bound (me->get_bound (LEFT),
231 me->get_bound (RIGHT));
234 Real gap = gh_scm2double (me->get_grob_property ("gap"));
236 Offset ofxy (gap, 0); /*offset from start point to start of line*/
241 Real thick = me->get_paper ()->get_var ("linethickness");
243 SCM s = me->get_grob_property ("thickness");
245 thick *= gh_scm2double (s);
247 if (bound[RIGHT]->break_status_dir())
249 if (bound[LEFT]->break_status_dir ())
251 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
258 This is hairy. For the normal case, we simply find common
259 parents, and draw a line between the bounds. When two note
260 heads are on different lines, there is no common parent
261 anymore. We have to find the piano-staff object.
264 int k = broken_spanner_index (me);
265 Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
266 Spanner *next_sp = parent_sp->broken_intos_ [k+1];
267 Item *next_bound = next_sp->get_bound (RIGHT);
269 if (next_bound->break_status_dir ())
271 programming_error ("no note heads for the line spanner on next line?"
277 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
278 commonx = me->common_refpoint (commonx, X_AXIS);
280 Grob *next_common_y = line_spanner_common_parent (next_bound);
281 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
283 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
285 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
286 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
288 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
290 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
291 this_ext.center () + yoff);
292 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
293 next_ext.center () + yoff);
296 Real len = dz.length ();
298 Offset dir = dz *(1/ len);
299 dz = (dz.length () - 2*gap) *dir;
302 Molecule l (line_molecule (me, thick, Offset(0, 0), dz));
304 l.translate (dir * gap + p1
305 - Offset (me->relative_coordinate (commonx, X_AXIS),
306 me->relative_coordinate (all_common_y, Y_AXIS)));
308 return l.smobbed_copy ();
312 Grob *common[] = { me, me };
313 for (int a = X_AXIS; a < NO_AXES; a++)
315 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
316 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
319 // distance from center to start of line
320 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
322 for (int a = X_AXIS; a < NO_AXES; a++)
326 + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
327 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
329 my_off[ax] =me->relative_coordinate (common[a], ax);
330 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
334 ofxy = dxy * (off/dxy.length ());
337 Molecule line = line_molecule (me, thick, Offset (0,0),dxy);
339 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()/2, X_AXIS);
340 line.translate (ofxy - my_off + his_off);
341 return line.smobbed_copy ();
346 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
347 "Generic line drawn between two objects, eg. for use with glissandi.\n"
348 "gap is measured in staff-spaces.\n"
349 "The property 'type is one of: line, dashed-line, trill, dotted-line or zigzag.\n"
351 "gap dash-period dash-length zigzag-width zigzag-length thickness style");