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>
11 #include "molecule.hh"
14 #include "line-spanner.hh"
15 #include "paper-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_molecule (Grob *me,
33 Real thick = me->get_paper()->get_realvar (ly_symbol2scm ("linethickness"));
34 thick *= robust_scm2double (me->get_grob_property ("thickness"), 1.0); // todo: staff sym referencer?
36 Real staff_space = Staff_symbol_referencer::staff_space (me);
37 SCM ws = me->get_grob_property ("zigzag-width");
38 SCM ls = me->get_grob_property ("zigzag-length");
39 double w = (gh_number_p(ws) ? gh_scm2double(ws) : 1)*staff_space;
40 double l = (gh_number_p(ls) ? gh_scm2double(ls) : 1)*w;
41 double h = l>w/2 ? sqrt(l*l-w*w/4) : 0;
43 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
47 gh_double2scm (thick),
52 b.add_point (Offset (0,0));
54 b[X_AXIS].widen (thick/2);
55 b[Y_AXIS].widen (thick/2);
57 return Molecule (b, list);
60 MAKE_SCHEME_CALLBACK(Line_spanner, after_line_breaking, 1);
62 Line_spanner::after_line_breaking (SCM g)
64 Grob *me = unsmob_grob (g);
65 Spanner*sp = dynamic_cast<Spanner*> (me);
68 We remove the line at the start of the line. For piano voice
69 indicators, it makes no sense to have them at the start of the
72 I'm not sure what the official rules for glissandi are, but
73 usually the 2nd note of the glissando is "exact", so when playing
74 from the start of the line, there is no need to glide.
76 From a typographical p.o.v. this makes sense, since the amount of
77 space left of a note at the start of a line is very small.
82 if (sp->get_bound (LEFT)->break_status_dir()
83 && !sp->get_bound (RIGHT)->break_status_dir())
86 Can't do suicide, since this mucks up finding the trend.
88 me->set_grob_property ("molecule-callback", SCM_EOL);
96 Line_spanner::line_molecule (Grob *me,
100 Offset dz = to -from ;
101 SCM type = me->get_grob_property ("style");
102 if (gh_symbol_p (type)
103 && (type == ly_symbol2scm ("line")
104 || type == ly_symbol2scm ("dashed-line")
105 || type == ly_symbol2scm ("dotted-line")
106 || type == ly_symbol2scm ("zigzag")
107 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
109 return (type == ly_symbol2scm ("zigzag"))
110 ? zigzag_molecule (me, from, to)
111 : Line_interface::line (me, from, to);
113 else if (gh_symbol_p (type)
114 && type == ly_symbol2scm ("trill"))
116 SCM alist_chain = Font_interface::font_alist_chain (me);
117 SCM style_alist = scm_list_n (gh_cons (ly_symbol2scm ("font-family"),
118 ly_symbol2scm ("music")),
121 Font_metric *fm = select_font (me->get_paper (),
122 gh_cons (style_alist,
124 Molecule m = fm->find_by_name ("scripts-trill-element");
128 mol.add_at_edge (X_AXIS, RIGHT, m, 0,0);
129 while (m.extent (X_AXIS).length ()
130 && mol.extent (X_AXIS).length ()
131 + m.extent (X_AXIS).length () < dz[X_AXIS]);
134 FIXME: should center element on x/y
136 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
137 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
138 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
140 mol.translate (from);
147 Find a common Y parent, which --if found-- should be the
148 fixed-distance alignment.
151 line_spanner_common_parent (Grob *me)
153 Grob * common = find_fixed_alignment_parent (me);
156 common = Staff_symbol_referencer::get_staff_symbol (me);
158 common = common->get_parent (Y_AXIS);
160 common = me->get_parent (Y_AXIS);
167 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
169 (If not, you risk that this is called from the staff-alignment
170 routine, via molecule_extent. At this point, the staves aren't
171 separated yet, so it doesn't work cross-staff.
176 MAKE_SCHEME_CALLBACK (Line_spanner, brew_molecule, 1);
178 Line_spanner::brew_molecule (SCM smob)
180 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
182 Drul_array<Item*> bound (me->get_bound (LEFT),
183 me->get_bound (RIGHT));
186 Real gap = gh_scm2double (me->get_grob_property ("gap"));
188 Offset ofxy (gap, 0); /*offset from start point to start of line*/
194 if (bound[RIGHT]->break_status_dir())
196 if (bound[LEFT]->break_status_dir ())
198 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
205 This is hairy. For the normal case, we simply find common
206 parents, and draw a line between the bounds. When two note
207 heads are on different lines, there is no common parent
208 anymore. We have to find the piano-staff object.
211 int k = broken_spanner_index (me);
212 Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
213 Spanner *next_sp = parent_sp->broken_intos_ [k+1];
214 Item *next_bound = next_sp->get_bound (RIGHT);
216 if (next_bound->break_status_dir ())
218 programming_error ("no note heads for the line spanner on next line?"
224 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
225 commonx = me->common_refpoint (commonx, X_AXIS);
227 Grob *next_common_y = line_spanner_common_parent (next_bound);
228 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
230 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
232 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
233 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
235 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
237 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
238 this_ext.center () + yoff);
239 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
240 next_ext.center () + yoff);
243 Real len = dz.length ();
245 Offset dir = dz *(1/ len);
246 dz = (dz.length () - 2*gap) *dir;
249 Molecule l (line_molecule (me, Offset(0, 0), dz));
251 l.translate (dir * gap + p1
252 - Offset (me->relative_coordinate (commonx, X_AXIS),
253 me->relative_coordinate (all_common_y, Y_AXIS)));
255 return l.smobbed_copy ();
259 Grob *common[] = { me, me };
260 for (int a = X_AXIS; a < NO_AXES; a++)
262 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
263 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
266 // distance from center to start of line
267 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
269 for (int a = X_AXIS; a < NO_AXES; a++)
273 + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
274 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
276 my_off[ax] =me->relative_coordinate (common[a], ax);
277 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
281 ofxy = dxy * (off/dxy.length ());
284 Molecule line = line_molecule (me, Offset (0,0),dxy);
286 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()/2, X_AXIS);
287 line.translate (ofxy - my_off + his_off);
288 return line.smobbed_copy ();
293 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
294 "Generic line drawn between two objects, eg. for use with glissandi.\n"
295 "gap is measured in staff-spaces.\n"
296 "The property 'type is one of: line, dashed-line, trill, dotted-line or zigzag.\n"
298 "gap zigzag-width zigzag-length thickness");