(all-grob-descriptions): remove gap from
[lilypond.git] / lily / line-spanner.cc
blobf0d9c1dbd11f24af6eb970bc0fc2daa33c49b3d7
1 /*
2 line-spanner.cc -- implement Line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2004 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
9 #include <math.h>
11 #include "stencil.hh"
12 #include "item.hh"
13 #include "spanner.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"
19 #include "warn.hh"
20 #include "align-interface.hh"
21 #include "lookup.hh"
22 #include "line-interface.hh"
24 Stencil
25 zigzag_stencil (Grob *me,
26 Offset from,
27 Offset to)
29 Offset dz = to -from;
30 Real dx = dz[X_AXIS];
31 Real dy = dz[Y_AXIS];
33 Real thick = Staff_symbol_referencer::line_thickness (me);
34 thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer?
36 Real staff_space = Staff_symbol_referencer::staff_space (me);
38 double w = robust_scm2double (me->get_property ("zigzag-width"), 1)*staff_space;
39 double l = robust_scm2double ( me->get_property ("zigzag-length"), 1)* w;
40 double h = l>w/2 ? sqrt (l*l-w*w/4) : 0;
42 SCM list = scm_list_n (ly_symbol2scm ("zigzag-line"),
43 gh_bool2scm (true),
44 gh_double2scm (w),
45 gh_double2scm (h),
46 gh_double2scm (thick),
47 gh_double2scm (dx),
48 gh_double2scm (dy),
49 SCM_UNDEFINED);
50 Box b;
51 b.add_point (Offset (0,0));
52 b.add_point (dz);
53 b[X_AXIS].widen (thick/2);
54 b[Y_AXIS].widen (thick/2);
56 return Stencil (b, list);
59 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
60 SCM
61 Line_spanner::after_line_breaking (SCM g)
63 Grob *me = unsmob_grob (g);
64 Spanner*sp = dynamic_cast<Spanner*> (me);
67 We remove the line at the start of the line. For piano voice
68 indicators, it makes no sense to have them at the start of the
69 line.
71 I'm not sure what the official rules for glissandi are, but
72 usually the 2nd note of the glissando is "exact", so when playing
73 from the start of the line, there is no need to glide.
75 From a typographical p.o.v. this makes sense, since the amount of
76 space left of a note at the start of a line is very small.
78 --hwn.
81 if (sp->get_bound (LEFT)->break_status_dir ()
82 && !sp->get_bound (RIGHT)->break_status_dir ())
85 Can't do suicide, since this mucks up finding the trend.
87 me->set_property ("print-function", SCM_EOL);
90 return SCM_EOL;
94 Stencil
95 Line_spanner::line_stencil (Grob *me,
96 Offset from,
97 Offset to)
99 Offset dz = to -from ;
100 SCM type = me->get_property ("style");
101 if (gh_symbol_p (type)
102 && (type == ly_symbol2scm ("line")
103 || type == ly_symbol2scm ("dashed-line")
104 || type == ly_symbol2scm ("dotted-line")
105 || type == ly_symbol2scm ("zigzag")
106 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
108 return (type == ly_symbol2scm ("zigzag"))
109 ? zigzag_stencil (me, from, to)
110 : Line_interface::line (me, from, to);
112 else if (gh_symbol_p (type)
113 && type == ly_symbol2scm ("trill"))
115 SCM alist_chain = Font_interface::text_font_alist_chain (me);
116 SCM style_alist = scm_list_n (gh_cons (ly_symbol2scm ("font-encoding"),
117 ly_symbol2scm ("music")),
118 SCM_UNDEFINED);
120 Font_metric *fm = select_font (me->get_paper (),
121 gh_cons (style_alist,
122 alist_chain));
123 Stencil m = fm->find_by_name ("scripts-trill-element");
124 Stencil mol;
127 mol.add_at_edge (X_AXIS, RIGHT, m, 0,0);
128 while (m.extent (X_AXIS).length ()
129 && mol.extent (X_AXIS).length ()
130 + m.extent (X_AXIS).length () < dz[X_AXIS]);
133 FIXME: should center element on x/y
135 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
136 mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
137 + mol.extent (Y_AXIS).length ())/2, Y_AXIS);
139 mol.translate (from);
140 return mol;
142 return Stencil ();
146 Find a common Y parent, which --if found-- should be the
147 fixed-distance alignment.
149 Grob *
150 line_spanner_common_parent (Grob *me)
152 Grob * common = find_fixed_alignment_parent (me);
153 if (!common)
155 common = Staff_symbol_referencer::get_staff_symbol (me);
156 if (common)
157 common = common->get_parent (Y_AXIS);
158 else
159 common = me->get_parent (Y_AXIS);
162 return common;
166 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
168 (If not, you risk that this is called from the staff-alignment
169 routine, via stencil_extent. At this point, the staves aren't
170 separated yet, so it doesn't work cross-staff.
175 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
177 Line_spanner::print (SCM smob)
179 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
181 Drul_array<Item*> bound (me->get_bound (LEFT),
182 me->get_bound (RIGHT));
185 Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
187 Offset ofxy (gap, 0); /*offset from start point to start of line*/
188 Offset dxy ;
189 Offset my_off;
190 Offset his_off;
193 if (bound[RIGHT]->break_status_dir ())
195 if (bound[LEFT]->break_status_dir ())
197 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
199 me->suicide ();
200 return SCM_EOL;
204 This is hairy. For the normal case, we simply find common
205 parents, and draw a line between the bounds. When two note
206 heads are on different lines, there is no common parent
207 anymore. We have to find the piano-staff object.
210 int k = broken_spanner_index (me);
211 Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
212 Spanner *next_sp = parent_sp->broken_intos_ [k+1];
213 Item *next_bound = next_sp->get_bound (RIGHT);
215 if (next_bound->break_status_dir ())
217 programming_error ("no note heads for the line spanner on next line?"
218 " Confused.");
219 me->suicide ();
220 return SCM_EOL;
223 Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
224 commonx = me->common_refpoint (commonx, X_AXIS);
226 Grob *next_common_y = line_spanner_common_parent (next_bound);
227 Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
229 Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
231 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
232 Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
234 Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
236 Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
237 this_ext.center () + yoff);
238 Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
239 next_ext.center () + yoff);
241 Offset dz (p2 -p1);
242 Real len = dz.length ();
244 Offset dir = dz *(1/ len);
245 dz = (dz.length () - 2*gap) *dir;
248 Stencil l (line_stencil (me, Offset (0, 0), dz));
250 l.translate (dir * gap + p1
251 - Offset (me->relative_coordinate (commonx, X_AXIS),
252 me->relative_coordinate (all_common_y, Y_AXIS)));
254 return l.smobbed_copy ();
256 else
258 Grob *common[] = { me, me };
259 for (int a = X_AXIS; a < NO_AXES; a++)
261 common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
262 common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
265 // distance from center to start of line
266 Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
268 for (int a = X_AXIS; a < NO_AXES; a++)
270 Axis ax = (Axis)a;
271 dxy[ax] =
272 + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
273 - bound[LEFT]->extent (common[X_AXIS], ax).center ();
275 my_off[ax] =me->relative_coordinate (common[a], ax);
276 his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
280 ofxy = dxy * (off/dxy.length ());
281 dxy -= 2*ofxy;
283 Stencil line = line_stencil (me, Offset (0,0),dxy);
285 line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()/2, X_AXIS);
286 line.translate (ofxy - my_off + his_off);
287 return line.smobbed_copy ();
292 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
293 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
294 "The property @code{style} can be @code{line}, "
295 "@code{dashed-line}, @code{trill}, \n"
296 "@code{dotted-line} or @code{zigzag}.\n"
297 "\n"
299 "gap zigzag-width zigzag-length thickness");