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 ("type");
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 ("type");
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_chain = scm_list_n (gh_cons (ly_symbol2scm ("font-family"),
152 ly_symbol2scm ("music")),
155 Font_metric *fm = Font_interface::get_font (me,
156 scm_list_n (style_chain,
159 Molecule m = fm->find_by_name ("scripts-trill-element");
161 mol.add_at_edge (X_AXIS, RIGHT, m, 0,0);
162 while (m.extent (X_AXIS).length ()
163 && mol.extent (X_AXIS).length ()
164 + m.extent (X_AXIS).length () < dx);
167 FIXME: should center element on x/y
169 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
170 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
171 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
177 Find a common Y parent, which --if found-- should be the
178 fixed-distance alignment.
181 line_spanner_common_parent (Grob *me)
183 Grob * common = find_fixed_alignment_parent (me);
186 common = Staff_symbol_referencer::get_staff_symbol (me);
188 common = common->get_parent (Y_AXIS);
190 common = me->get_parent (Y_AXIS);
197 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
199 (If not, you risk that this is called from the staff-alignment
200 routine, via molecule_extent. At this point, the staves aren't
201 separated yet, so it doesn't work cross-staff.
206 MAKE_SCHEME_CALLBACK (Line_spanner, brew_molecule, 1);
208 Line_spanner::brew_molecule (SCM smob)
210 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
212 Drul_array<Item*> bound (me->get_bound (LEFT),
213 me->get_bound (RIGHT));
216 Real gap = gh_scm2double (me->get_grob_property ("gap"));
218 Offset ofxy (gap, 0); /*offset from start point to start of line*/
223 Real thick = me->get_paper ()->get_var ("linethickness");
225 SCM s = me->get_grob_property ("thickness");
227 thick *= gh_scm2double (s);
229 if (bound[RIGHT]->break_status_dir())
231 if (bound[LEFT]->break_status_dir ())
233 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
240 This is hairy. For the normal case, we simply find common
241 parents, and draw a line between the bounds. When two note
242 heads are on different lines, there is no common parent
243 anymore. We have to find the piano-staff object.
246 int k = broken_spanner_index (me);
247 Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
248 Spanner *next_sp = parent_sp->broken_intos_ [k+1];
249 Item *next_bound = next_sp->get_bound (RIGHT);
251 if (next_bound->break_status_dir ())
253 programming_error ("no note heads for the line spanner on next line?"
259 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
260 commonx = me->common_refpoint (commonx, X_AXIS);
262 Grob *next_common_y = line_spanner_common_parent (next_bound);
263 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
265 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
267 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
268 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
270 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
272 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
273 this_ext.center () + yoff);
274 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
275 next_ext.center () + yoff);
278 Real len = dz.length ();
280 Offset dir = dz *(1/ len);
281 dz = (dz.length () - 2*gap) *dir;
284 Molecule l (line_molecule (me, thick, dz[X_AXIS],
287 l.translate (dir * gap + p1
288 - Offset (me->relative_coordinate (commonx, X_AXIS),
289 me->relative_coordinate (all_common_y, Y_AXIS)));
291 return l.smobbed_copy ();
295 Grob *common[] = { me, me };
296 for (int a = X_AXIS; a < NO_AXES; a++)
298 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
299 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
302 // distance from center to start of line
303 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
305 for (int a = X_AXIS; a < NO_AXES; a++)
309 + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
310 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
312 my_off[ax] =me->relative_coordinate (common[a], ax);
313 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
317 ofxy = dxy * (off/dxy.length ());
320 Molecule line = line_molecule (me, thick, dxy[X_AXIS], dxy[Y_AXIS]);
321 line.translate_axis (bound[LEFT]->extent (bound[LEFT],
322 X_AXIS).length ()/2, X_AXIS);
323 line.translate (ofxy - my_off + his_off);
324 return line.smobbed_copy ();
329 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
330 "Generic line drawn between two objects, eg. for use with glissandi.\n"
331 "gap is measured in staff-spaces.\n"
332 "The property 'type is one of: line, dashed-line, trill, dotted-line or zigzag.\n"
334 "gap dash-period dash-length zigzag-width zigzag-length thickness type");