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"
24 slightishly clumsy interface?
26 Make a Scheme expression for a line going from (0,0) to (dx,dy).
30 line_atom (Grob *me, Real thick, Real dx, Real dy)
32 SCM type = me->get_grob_property ("style");
33 Real staff_space = Staff_symbol_referencer::staff_space (me);
35 // maybe these should be in line-thickness?
36 Real length = staff_space;
37 SCM s = me->get_grob_property ("dash-length");
39 length = gh_scm2double (s) * staff_space;
41 Real period = 2 * length + thick;
42 s = me->get_grob_property ("dash-period");
44 period = gh_scm2double (s) * staff_space;
46 if (type == ly_symbol2scm ("dotted-line"))
49 if (type == ly_symbol2scm ("line"))
50 length = period + thick;
52 Real on = length - thick;
53 Real off = period - on;
55 SCM list = scm_list_n (ly_symbol2scm ("dashed-line"),
56 gh_double2scm (thick),
67 zigzag_atom (Grob *me, Real thick, Real dx, Real dy)
69 Real staff_space = Staff_symbol_referencer::staff_space (me);
70 SCM ws = me->get_grob_property ("zigzag-width");
71 SCM ls = me->get_grob_property ("zigzag-length");
72 double w = (gh_number_p(ws) ? gh_scm2double(ws) : 1)*staff_space;
73 double l = (gh_number_p(ls) ? gh_scm2double(ls) : 1)*w;
74 double h = l>w/2 ? sqrt(l*l-w*w/4) : 0;
76 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
80 gh_double2scm (thick),
88 MAKE_SCHEME_CALLBACK(Line_spanner, after_line_breaking, 1);
90 Line_spanner::after_line_breaking (SCM g)
92 Grob *me = unsmob_grob (g);
93 Spanner*sp = dynamic_cast<Spanner*> (me);
96 We remove the line at the start of the line. For piano voice
97 indicators, it makes no sense to have them at the start of the
100 I'm not sure what the official rules for glissandi are, but
101 usually the 2nd note of the glissando is "exact", so when playing
102 from the start of the line, there is no need to glide.
104 From a typographical p.o.v. this makes sense, since the amount of
105 space left of a note at the start of a line is very small.
110 if (sp->get_bound (LEFT)->break_status_dir()
111 && !sp->get_bound (RIGHT)->break_status_dir())
114 Can't do suicide, since this mucks up finding the trend.
116 me->set_grob_property ("molecule-callback", SCM_EOL);
124 Line_spanner::line_molecule (Grob *me, Real thick, Real dx, Real dy)
127 SCM type = me->get_grob_property ("style");
128 if (gh_symbol_p (type)
129 && (type == ly_symbol2scm ("line")
130 || type == ly_symbol2scm ("dashed-line")
131 || type == ly_symbol2scm ("dotted-line")
132 || (type == ly_symbol2scm ("trill") && dy != 0)))
134 Box b (Interval (-0.5* thick + (0 <? dx) ,0.5* thick+ (0 >? dx)),
135 Interval (- 0.5* thick + (0<? dy), 0.5*thick + (0 >? dy)));
136 mol = Molecule (b, line_atom (me, thick, dx, dy));
138 else if (gh_symbol_p (type)
139 && type == ly_symbol2scm ("zigzag"))
142 Box b (Interval (-0.5* thick + (0 <? dx) ,0.5* thick+ (0 >? dx)),
143 Interval (- 0.5* thick + (0<? dy), 0.5*thick + (0 >? dy)));
144 mol = Molecule (b, zigzag_atom (me, thick, dx, dy));
147 else if (gh_symbol_p (type)
148 && type == ly_symbol2scm ("trill"))
150 SCM alist_chain = Font_interface::font_alist_chain (me);
151 SCM style_alist = scm_list_n (gh_cons (ly_symbol2scm ("font-family"),
152 ly_symbol2scm ("music")),
155 Font_metric *fm = Font_interface::get_font (me,
156 gh_cons (style_alist,
158 Molecule m = fm->find_by_name ("scripts-trill-element");
160 mol.add_at_edge (X_AXIS, RIGHT, m, 0,0);
161 while (m.extent (X_AXIS).length ()
162 && mol.extent (X_AXIS).length ()
163 + m.extent (X_AXIS).length () < dx);
166 FIXME: should center element on x/y
168 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
169 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
170 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
176 Find a common Y parent, which --if found-- should be the
177 fixed-distance alignment.
180 line_spanner_common_parent (Grob *me)
182 Grob * common = find_fixed_alignment_parent (me);
185 common = Staff_symbol_referencer::get_staff_symbol (me);
187 common = common->get_parent (Y_AXIS);
189 common = me->get_parent (Y_AXIS);
196 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
198 (If not, you risk that this is called from the staff-alignment
199 routine, via molecule_extent. At this point, the staves aren't
200 separated yet, so it doesn't work cross-staff.
205 MAKE_SCHEME_CALLBACK (Line_spanner, brew_molecule, 1);
207 Line_spanner::brew_molecule (SCM smob)
209 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
211 Drul_array<Item*> bound (me->get_bound (LEFT),
212 me->get_bound (RIGHT));
215 Real gap = gh_scm2double (me->get_grob_property ("gap"));
217 Offset ofxy (gap, 0); /*offset from start point to start of line*/
222 Real thick = me->get_paper ()->get_var ("linethickness");
224 SCM s = me->get_grob_property ("thickness");
226 thick *= gh_scm2double (s);
228 if (bound[RIGHT]->break_status_dir())
230 if (bound[LEFT]->break_status_dir ())
232 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
239 This is hairy. For the normal case, we simply find common
240 parents, and draw a line between the bounds. When two note
241 heads are on different lines, there is no common parent
242 anymore. We have to find the piano-staff object.
245 int k = broken_spanner_index (me);
246 Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
247 Spanner *next_sp = parent_sp->broken_intos_ [k+1];
248 Item *next_bound = next_sp->get_bound (RIGHT);
250 if (next_bound->break_status_dir ())
252 programming_error ("no note heads for the line spanner on next line?"
258 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
259 commonx = me->common_refpoint (commonx, X_AXIS);
261 Grob *next_common_y = line_spanner_common_parent (next_bound);
262 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
264 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
266 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
267 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
269 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
271 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
272 this_ext.center () + yoff);
273 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
274 next_ext.center () + yoff);
277 Real len = dz.length ();
279 Offset dir = dz *(1/ len);
280 dz = (dz.length () - 2*gap) *dir;
283 Molecule l (line_molecule (me, thick, dz[X_AXIS],
286 l.translate (dir * gap + p1
287 - Offset (me->relative_coordinate (commonx, X_AXIS),
288 me->relative_coordinate (all_common_y, Y_AXIS)));
290 return l.smobbed_copy ();
294 Grob *common[] = { me, me };
295 for (int a = X_AXIS; a < NO_AXES; a++)
297 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
298 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
301 // distance from center to start of line
302 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
304 for (int a = X_AXIS; a < NO_AXES; a++)
308 + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
309 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
311 my_off[ax] =me->relative_coordinate (common[a], ax);
312 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
316 ofxy = dxy * (off/dxy.length ());
319 Molecule line = line_molecule (me, thick, dxy[X_AXIS], dxy[Y_AXIS]);
320 line.translate_axis (bound[LEFT]->extent (bound[LEFT],
321 X_AXIS).length ()/2, X_AXIS);
322 line.translate (ofxy - my_off + his_off);
323 return line.smobbed_copy ();
328 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
329 "Generic line drawn between two objects, eg. for use with glissandi.\n"
330 "gap is measured in staff-spaces.\n"
331 "The property 'type is one of: line, dashed-line, trill, dotted-line or zigzag.\n"
333 "gap dash-period dash-length zigzag-width zigzag-length thickness style");