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 TODO: convert all Molecule functions to taking arguments of the form
26 Offset FROM, Offset TO.
31 Introduce line-interface that allows dots/dashes/etc. to be set as
32 grob-properties. Make arbitrary paths.
36 dashed_line_molecule (Grob *me, Real thick, Offset from, Offset to)
38 SCM type = me->get_grob_property ("style");
39 if (type == ly_symbol2scm ("dotted-line")
40 || type == ly_symbol2scm ("dashed-line"))
42 Real fraction = (type == ly_symbol2scm ("dotted-line")) ? 0.0 : 0.4;
45 SCM s = me->get_grob_property ("dash-fraction");
47 fraction = gh_scm2double (s);
48 fraction = (fraction >? 0) <? 1.0;
50 Real period = Staff_symbol_referencer::staff_space (me);
51 s = me->get_grob_property ("dash-period");
53 period *= gh_scm2double (s);
55 return Lookup::dashed_line (thick, from, to, period, fraction);
59 return Lookup::line (thick, from, to);
64 zigzag_molecule (Grob *me, Real thick,
72 Real staff_space = Staff_symbol_referencer::staff_space (me);
73 SCM ws = me->get_grob_property ("zigzag-width");
74 SCM ls = me->get_grob_property ("zigzag-length");
75 double w = (gh_number_p(ws) ? gh_scm2double(ws) : 1)*staff_space;
76 double l = (gh_number_p(ls) ? gh_scm2double(ls) : 1)*w;
77 double h = l>w/2 ? sqrt(l*l-w*w/4) : 0;
79 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
83 gh_double2scm (thick),
88 b.add_point (Offset (0,0));
90 b[X_AXIS].widen (thick/2);
91 b[Y_AXIS].widen (thick/2);
93 return Molecule (b, list);
96 MAKE_SCHEME_CALLBACK(Line_spanner, after_line_breaking, 1);
98 Line_spanner::after_line_breaking (SCM g)
100 Grob *me = unsmob_grob (g);
101 Spanner*sp = dynamic_cast<Spanner*> (me);
104 We remove the line at the start of the line. For piano voice
105 indicators, it makes no sense to have them at the start of the
108 I'm not sure what the official rules for glissandi are, but
109 usually the 2nd note of the glissando is "exact", so when playing
110 from the start of the line, there is no need to glide.
112 From a typographical p.o.v. this makes sense, since the amount of
113 space left of a note at the start of a line is very small.
118 if (sp->get_bound (LEFT)->break_status_dir()
119 && !sp->get_bound (RIGHT)->break_status_dir())
122 Can't do suicide, since this mucks up finding the trend.
124 me->set_grob_property ("molecule-callback", SCM_EOL);
132 Line_spanner::line_molecule (Grob *me, Real thick,
136 Offset dz = to -from ;
137 SCM type = me->get_grob_property ("style");
138 if (gh_symbol_p (type)
139 && (type == ly_symbol2scm ("line")
140 || type == ly_symbol2scm ("dashed-line")
141 || type == ly_symbol2scm ("dotted-line")
142 || type == ly_symbol2scm ("zigzag")
143 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
145 return (type == ly_symbol2scm ("zigzag"))
146 ? zigzag_molecule (me, thick, from, to)
147 : dashed_line_molecule (me, thick, from, to);
149 else if (gh_symbol_p (type)
150 && type == ly_symbol2scm ("trill"))
152 SCM alist_chain = Font_interface::font_alist_chain (me);
153 SCM style_alist = scm_list_n (gh_cons (ly_symbol2scm ("font-family"),
154 ly_symbol2scm ("music")),
157 Font_metric *fm = select_font (me->get_paper (),
158 gh_cons (style_alist,
160 Molecule m = fm->find_by_name ("scripts-trill-element");
164 mol.add_at_edge (X_AXIS, RIGHT, m, 0,0);
165 while (m.extent (X_AXIS).length ()
166 && mol.extent (X_AXIS).length ()
167 + m.extent (X_AXIS).length () < dz[X_AXIS]);
170 FIXME: should center element on x/y
172 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
173 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
174 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
176 mol.translate (from);
183 Find a common Y parent, which --if found-- should be the
184 fixed-distance alignment.
187 line_spanner_common_parent (Grob *me)
189 Grob * common = find_fixed_alignment_parent (me);
192 common = Staff_symbol_referencer::get_staff_symbol (me);
194 common = common->get_parent (Y_AXIS);
196 common = me->get_parent (Y_AXIS);
203 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
205 (If not, you risk that this is called from the staff-alignment
206 routine, via molecule_extent. At this point, the staves aren't
207 separated yet, so it doesn't work cross-staff.
212 MAKE_SCHEME_CALLBACK (Line_spanner, brew_molecule, 1);
214 Line_spanner::brew_molecule (SCM smob)
216 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
218 Drul_array<Item*> bound (me->get_bound (LEFT),
219 me->get_bound (RIGHT));
222 Real gap = gh_scm2double (me->get_grob_property ("gap"));
224 Offset ofxy (gap, 0); /*offset from start point to start of line*/
229 Real thick = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
231 SCM s = me->get_grob_property ("thickness");
233 thick *= gh_scm2double (s);
235 if (bound[RIGHT]->break_status_dir())
237 if (bound[LEFT]->break_status_dir ())
239 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
246 This is hairy. For the normal case, we simply find common
247 parents, and draw a line between the bounds. When two note
248 heads are on different lines, there is no common parent
249 anymore. We have to find the piano-staff object.
252 int k = broken_spanner_index (me);
253 Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
254 Spanner *next_sp = parent_sp->broken_intos_ [k+1];
255 Item *next_bound = next_sp->get_bound (RIGHT);
257 if (next_bound->break_status_dir ())
259 programming_error ("no note heads for the line spanner on next line?"
265 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
266 commonx = me->common_refpoint (commonx, X_AXIS);
268 Grob *next_common_y = line_spanner_common_parent (next_bound);
269 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
271 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
273 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
274 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
276 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
278 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
279 this_ext.center () + yoff);
280 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
281 next_ext.center () + yoff);
284 Real len = dz.length ();
286 Offset dir = dz *(1/ len);
287 dz = (dz.length () - 2*gap) *dir;
290 Molecule l (line_molecule (me, thick, Offset(0, 0), dz));
292 l.translate (dir * gap + p1
293 - Offset (me->relative_coordinate (commonx, X_AXIS),
294 me->relative_coordinate (all_common_y, Y_AXIS)));
296 return l.smobbed_copy ();
300 Grob *common[] = { me, me };
301 for (int a = X_AXIS; a < NO_AXES; a++)
303 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
304 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
307 // distance from center to start of line
308 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
310 for (int a = X_AXIS; a < NO_AXES; a++)
314 + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
315 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
317 my_off[ax] =me->relative_coordinate (common[a], ax);
318 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
322 ofxy = dxy * (off/dxy.length ());
325 Molecule line = line_molecule (me, thick, Offset (0,0),dxy);
327 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()/2, X_AXIS);
328 line.translate (ofxy - my_off + his_off);
329 return line.smobbed_copy ();
334 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
335 "Generic line drawn between two objects, eg. for use with glissandi.\n"
336 "gap is measured in staff-spaces.\n"
337 "The property 'type is one of: line, dashed-line, trill, dotted-line or zigzag.\n"
339 "gap dash-period dash-fraction zigzag-width zigzag-length thickness style");