]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-spanner.cc
Update.
[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   return SCM_EOL;
89 }
90
91 Stencil
92 Line_spanner::line_stencil (Grob *me,
93                             Offset from,
94                             Offset to)
95 {
96   Offset dz = to -from;
97   SCM type = me->get_property ("style");
98   if (scm_is_symbol (type)
99       && (type == ly_symbol2scm ("line")
100           || type == ly_symbol2scm ("dashed-line")
101           || type == ly_symbol2scm ("dotted-line")
102           || type == ly_symbol2scm ("zigzag")
103           || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
104     {
105       return (type == ly_symbol2scm ("zigzag"))
106         ? zigzag_stencil (me, from, to)
107         : Line_interface::line (me, from, to);
108     }
109   else if (scm_is_symbol (type)
110            && type == ly_symbol2scm ("trill"))
111     {
112       SCM alist_chain = Font_interface::text_font_alist_chain (me);
113       SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
114                                               ly_symbol2scm ("fetaMusic")),
115                                     SCM_UNDEFINED);
116
117       Font_metric *fm = select_font (me->get_layout (),
118                                      scm_cons (style_alist,
119                                                alist_chain));
120       Stencil m = fm->find_by_name ("scripts.trill_element");
121       Stencil mol;
122
123       do
124         mol.add_at_edge (X_AXIS, RIGHT, m, 0, 0);
125       while (m.extent (X_AXIS).length ()
126              && mol.extent (X_AXIS).length ()
127              + m.extent (X_AXIS).length () < dz[X_AXIS])
128         ;
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       ofxy = dxy * (off / dxy.length ());
275       dxy -= 2*ofxy;
276
277       Stencil line = line_stencil (me, Offset (0, 0), dxy);
278
279       line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
280       line.translate (ofxy - my_off + his_off);
281       return line.smobbed_copy ();
282     }
283 }
284
285 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
286                "Generic line drawn between two objects, e.g. for use with glissandi.\n"
287                "The property @code{style} can be @code{line}, "
288                "@code{dashed-line}, @code{trill}, \n"
289                "@code{dotted-line} or @code{zigzag}.\n"
290                "\n",
291                "gap zigzag-width zigzag-length thickness");
292