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