]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-spanner.cc
(parse_symbol_list): Bugfix.
[lilypond.git] / lily / line-spanner.cc
1 /*
2   line-spanner.cc -- implement Line_spanner
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2000--2005 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "line-spanner.hh"
10
11 #include <math.h>
12
13 #include "spanner.hh"
14 #include "output-def.hh"
15 #include "paper-column.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "font-interface.hh"
18 #include "warn.hh"
19 #include "align-interface.hh"
20 #include "lookup.hh"
21 #include "line-interface.hh"
22
23 Stencil
24 zigzag_stencil (Grob *me,
25                 Offset from,
26                 Offset to)
27 {
28   Offset dz = to -from;
29
30   Real thick = Staff_symbol_referencer::line_thickness (me);
31   thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer? 
32
33   Real staff_space = Staff_symbol_referencer::staff_space (me);
34
35   Real w = robust_scm2double (me->get_property ("zigzag-width"), 1) * staff_space;
36   int count = (int) ceil (dz.length () / w);
37   w = dz.length () / count;
38
39   Real l = robust_scm2double (me->get_property ("zigzag-length"), 1) * w;
40   Real h = l > w / 2 ? sqrt (l * l - w * w / 4) : 0;
41
42   Offset rotation_factor = complex_exp (Offset (0, dz.arg ()));
43
44   Offset points[3];
45   points[0] = Offset (0, -h / 2);
46   points[1] = Offset (w / 2, h / 2);
47   points[2] = Offset (w, -h / 2);
48   for (int i = 0; i < 3; i++)
49     points[i] = complex_multiply (points[i], rotation_factor);
50
51   Stencil squiggle (Line_interface::make_line (thick, points[0], points[1]));
52   squiggle.add_stencil (Line_interface::make_line (thick, points[1], points[2]));
53
54   Stencil total;
55   for (int i = 0; i < count; i++)
56     {
57       Stencil moved_squiggle (squiggle);
58       moved_squiggle.translate (from + Offset (i * w, 0) * rotation_factor);
59       total.add_stencil (moved_squiggle);
60     }
61
62   Box b;
63   b.add_point (Offset (0, 0));
64   b.add_point (dz);
65   b[X_AXIS].widen (thick / 2);
66   b[Y_AXIS].widen (thick / 2);
67
68   return Stencil (b, total.expr ());
69 }
70
71 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
72 SCM
73 Line_spanner::after_line_breaking (SCM g)
74 {
75   Grob *me = unsmob_grob (g);
76   Spanner *sp = dynamic_cast<Spanner *> (me);
77
78   /*
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
81     line.
82
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.
86
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.
89
90     --hwn.
91
92   */
93   if (sp->get_bound (LEFT)->break_status_dir ()
94       && !sp->get_bound (RIGHT)->break_status_dir ())
95     {
96       /*
97         Can't do suicide, since this mucks up finding the trend.
98       */
99       me->set_property ("print-function", SCM_EOL);
100     }
101   return SCM_EOL;
102 }
103
104 Stencil
105 Line_spanner::line_stencil (Grob *me,
106                             Offset from,
107                             Offset to)
108 {
109   Offset dz = to -from;
110   SCM type = me->get_property ("style");
111
112   Stencil line;
113
114   if (scm_is_symbol (type)
115       && (type == ly_symbol2scm ("line")
116           || type == ly_symbol2scm ("dashed-line")
117           || type == ly_symbol2scm ("dotted-line")
118           || type == ly_symbol2scm ("zigzag")
119           || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
120     {
121       line = (type == ly_symbol2scm ("zigzag"))
122         ? zigzag_stencil (me, from, to)
123         : Line_interface::line (me, from, to);
124     }
125   else if (scm_is_symbol (type)
126            && type == ly_symbol2scm ("trill"))
127     {
128       SCM alist_chain = Font_interface::text_font_alist_chain (me);
129       SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
130                                               ly_symbol2scm ("fetaMusic")),
131                                     SCM_UNDEFINED);
132
133       Font_metric *fm = select_font (me->get_layout (),
134                                      scm_cons (style_alist,
135                                                alist_chain));
136       Stencil m = fm->find_by_name ("scripts.trill_element");
137       Stencil mol;
138
139       do
140         mol.add_at_edge (X_AXIS, RIGHT, m, 0, 0);
141       while (m.extent (X_AXIS).length ()
142              && mol.extent (X_AXIS).length ()
143              + m.extent (X_AXIS).length () < dz[X_AXIS])
144         ;
145
146       /*
147         FIXME: should center element on x/y
148       */
149       mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
150       mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
151                              + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
152
153       mol.translate (from);
154       line = mol;
155     }
156
157   if (to_boolean (me->get_property ("arrow")))
158     line.add_stencil (Line_interface::arrows (me, from, to, false, true));
159
160   return line;
161 }
162
163 /*
164   Find a common Y parent, which --if found-- should be the
165   fixed-distance alignment.
166 */
167 Grob *
168 line_spanner_common_parent (Grob *me)
169 {
170   Grob *common = find_fixed_alignment_parent (me);
171   if (!common)
172     {
173       common = Staff_symbol_referencer::get_staff_symbol (me);
174       if (common)
175         common = common->get_parent (Y_AXIS);
176       else
177         common = me->get_parent (Y_AXIS);
178     }
179
180   return common;
181 }
182
183 /*
184   Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
185
186   (If not, you risk that this is called from the staff-alignment
187   routine, via stencil_extent. At this point, the staves aren't
188   separated yet, so it doesn't work cross-staff.
189
190   (huh? crossable staves have fixed distance? --hwn)
191 */
192
193 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
194 SCM
195 Line_spanner::print (SCM smob)
196 {
197   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
198
199   Drul_array<Item *> bound (me->get_bound (LEFT),
200                             me->get_bound (RIGHT));
201
202   Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
203
204   Offset ofxy (gap, 0); /* offset from start point to start of line */
205   Offset dxy;
206   Offset my_off;
207   Offset his_off;
208
209   if (bound[RIGHT]->break_status_dir ())
210     {
211       if (bound[LEFT]->break_status_dir ())
212         {
213           programming_error ("line-spanner with two broken ends. Farewell sweet world.");
214
215           me->suicide ();
216           return SCM_EOL;
217         }
218
219       /*
220         This is hairy. For the normal case, we simply find common
221         parents, and draw a line between the bounds. When two note
222         heads are on different systems, there is no common parent
223         anymore. We have to find the piano-staff object.
224       */
225
226       int k = broken_spanner_index (me);
227       Spanner *parent_sp = dynamic_cast<Spanner *> (me->original_);
228       Spanner *next_sp = parent_sp->broken_intos_ [k + 1];
229       Item *next_bound = next_sp->get_bound (RIGHT);
230
231       if (next_bound->break_status_dir ())
232         {
233           programming_error ("no note heads for the line spanner on next line?"
234                              " Confused.");
235           me->suicide ();
236           return SCM_EOL;
237         }
238
239       Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
240       commonx = me->common_refpoint (commonx, X_AXIS);
241
242       Grob *next_common_y = line_spanner_common_parent (next_bound);
243       Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
244
245       Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
246
247       Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
248       Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
249
250       Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
251
252       Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
253                  this_ext.center () + yoff);
254       Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
255                  next_ext.center () + yoff);
256
257       Offset dz (p2 -p1);
258       Real len = dz.length ();
259
260       Offset dir = dz * (1 / len);
261       dz = (dz.length () - 2 * gap) * dir;
262
263       Stencil l (line_stencil (me, Offset (0, 0), dz));
264
265       l.translate (dir * gap + p1
266                    - Offset (me->relative_coordinate (commonx, X_AXIS),
267                              me->relative_coordinate (all_common_y, Y_AXIS)));
268
269       return l.smobbed_copy ();
270     }
271   else
272     {
273       Grob *common[] = { me, me };
274       for (int a = X_AXIS; a < NO_AXES; a++)
275         {
276           common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
277           common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
278         }
279
280       // distance from center to start of line      
281       Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
282
283       for (int a = X_AXIS; a < NO_AXES; a++)
284         {
285           Axis ax = (Axis)a;
286           dxy[ax]
287             = + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
288             - bound[LEFT]->extent (common[X_AXIS], ax).center ();
289
290           my_off[ax] = me->relative_coordinate (common[a], ax);
291           his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
292         }
293
294       ofxy = dxy * (off / dxy.length ());
295       dxy -= 2*ofxy;
296
297       Stencil line = line_stencil (me, Offset (0, 0), dxy);
298
299       line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
300       line.translate (ofxy - my_off + his_off);
301       return line.smobbed_copy ();
302     }
303 }
304
305 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
306                "Generic line drawn between two objects, e.g. for use with glissandi.\n"
307                "The property @code{style} can be @code{line}, "
308                "@code{dashed-line}, @code{trill}, \n"
309                "@code{dotted-line} or @code{zigzag}.\n"
310                "\n",
311                "gap zigzag-width zigzag-length thickness arrow");
312