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 TODO: convert all Molecule functions to taking arguments of the form
27 Offset FROM, Offset TO.
32 Introduce line-interface that allows dots/dashes/etc. to be set as
33 grob-properties. Make arbitrary paths.
39 zigzag_molecule (Grob *me, Real thick,
47 Real staff_space = Staff_symbol_referencer::staff_space (me);
48 SCM ws = me->get_grob_property ("zigzag-width");
49 SCM ls = me->get_grob_property ("zigzag-length");
50 double w = (gh_number_p(ws) ? gh_scm2double(ws) : 1)*staff_space;
51 double l = (gh_number_p(ls) ? gh_scm2double(ls) : 1)*w;
52 double h = l>w/2 ? sqrt(l*l-w*w/4) : 0;
54 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
58 gh_double2scm (thick),
63 b.add_point (Offset (0,0));
65 b[X_AXIS].widen (thick/2);
66 b[Y_AXIS].widen (thick/2);
68 return Molecule (b, list);
71 MAKE_SCHEME_CALLBACK(Line_spanner, after_line_breaking, 1);
73 Line_spanner::after_line_breaking (SCM g)
75 Grob *me = unsmob_grob (g);
76 Spanner*sp = dynamic_cast<Spanner*> (me);
79 We remove the line at the start of the line. For piano voice
80 indicators, it makes no sense to have them at the start of the
83 I'm not sure what the official rules for glissandi are, but
84 usually the 2nd note of the glissando is "exact", so when playing
85 from the start of the line, there is no need to glide.
87 From a typographical p.o.v. this makes sense, since the amount of
88 space left of a note at the start of a line is very small.
93 if (sp->get_bound (LEFT)->break_status_dir()
94 && !sp->get_bound (RIGHT)->break_status_dir())
97 Can't do suicide, since this mucks up finding the trend.
99 me->set_grob_property ("molecule-callback", SCM_EOL);
107 Line_spanner::line_molecule (Grob *me, Real thick,
111 Offset dz = to -from ;
112 SCM type = me->get_grob_property ("style");
113 if (gh_symbol_p (type)
114 && (type == ly_symbol2scm ("line")
115 || type == ly_symbol2scm ("dashed-line")
116 || type == ly_symbol2scm ("dotted-line")
117 || type == ly_symbol2scm ("zigzag")
118 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
120 return (type == ly_symbol2scm ("zigzag"))
121 ? zigzag_molecule (me, thick, from, to)
122 : Line_interface::dashed_line (me, thick, from, to);
124 else if (gh_symbol_p (type)
125 && type == ly_symbol2scm ("trill"))
127 SCM alist_chain = Font_interface::font_alist_chain (me);
128 SCM style_alist = scm_list_n (gh_cons (ly_symbol2scm ("font-family"),
129 ly_symbol2scm ("music")),
132 Font_metric *fm = select_font (me->get_paper (),
133 gh_cons (style_alist,
135 Molecule m = fm->find_by_name ("scripts-trill-element");
139 mol.add_at_edge (X_AXIS, RIGHT, m, 0,0);
140 while (m.extent (X_AXIS).length ()
141 && mol.extent (X_AXIS).length ()
142 + m.extent (X_AXIS).length () < dz[X_AXIS]);
145 FIXME: should center element on x/y
147 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
148 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
149 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
151 mol.translate (from);
158 Find a common Y parent, which --if found-- should be the
159 fixed-distance alignment.
162 line_spanner_common_parent (Grob *me)
164 Grob * common = find_fixed_alignment_parent (me);
167 common = Staff_symbol_referencer::get_staff_symbol (me);
169 common = common->get_parent (Y_AXIS);
171 common = me->get_parent (Y_AXIS);
178 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
180 (If not, you risk that this is called from the staff-alignment
181 routine, via molecule_extent. At this point, the staves aren't
182 separated yet, so it doesn't work cross-staff.
187 MAKE_SCHEME_CALLBACK (Line_spanner, brew_molecule, 1);
189 Line_spanner::brew_molecule (SCM smob)
191 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
193 Drul_array<Item*> bound (me->get_bound (LEFT),
194 me->get_bound (RIGHT));
197 Real gap = gh_scm2double (me->get_grob_property ("gap"));
199 Offset ofxy (gap, 0); /*offset from start point to start of line*/
204 Real thick = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
206 SCM s = me->get_grob_property ("thickness");
208 thick *= gh_scm2double (s);
210 if (bound[RIGHT]->break_status_dir())
212 if (bound[LEFT]->break_status_dir ())
214 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
221 This is hairy. For the normal case, we simply find common
222 parents, and draw a line between the bounds. When two note
223 heads are on different lines, there is no common parent
224 anymore. We have to find the piano-staff object.
227 int k = broken_spanner_index (me);
228 Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
229 Spanner *next_sp = parent_sp->broken_intos_ [k+1];
230 Item *next_bound = next_sp->get_bound (RIGHT);
232 if (next_bound->break_status_dir ())
234 programming_error ("no note heads for the line spanner on next line?"
240 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
241 commonx = me->common_refpoint (commonx, X_AXIS);
243 Grob *next_common_y = line_spanner_common_parent (next_bound);
244 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
246 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
248 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
249 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
251 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
253 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
254 this_ext.center () + yoff);
255 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
256 next_ext.center () + yoff);
259 Real len = dz.length ();
261 Offset dir = dz *(1/ len);
262 dz = (dz.length () - 2*gap) *dir;
265 Molecule l (line_molecule (me, thick, Offset(0, 0), dz));
267 l.translate (dir * gap + p1
268 - Offset (me->relative_coordinate (commonx, X_AXIS),
269 me->relative_coordinate (all_common_y, Y_AXIS)));
271 return l.smobbed_copy ();
275 Grob *common[] = { me, me };
276 for (int a = X_AXIS; a < NO_AXES; a++)
278 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
279 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
282 // distance from center to start of line
283 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
285 for (int a = X_AXIS; a < NO_AXES; a++)
289 + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
290 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
292 my_off[ax] =me->relative_coordinate (common[a], ax);
293 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
297 ofxy = dxy * (off/dxy.length ());
300 Molecule line = line_molecule (me, thick, Offset (0,0),dxy);
302 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()/2, X_AXIS);
303 line.translate (ofxy - my_off + his_off);
304 return line.smobbed_copy ();
309 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
310 "Generic line drawn between two objects, eg. for use with glissandi.\n"
311 "gap is measured in staff-spaces.\n"
312 "The property 'type is one of: line, dashed-line, trill, dotted-line or zigzag.\n"
314 "gap zigzag-width zigzag-length thickness");