]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-spanner.cc
* lily/text-spanner.cc: add bound-padding.
[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
99   Stencil line;
100   
101   if (scm_is_symbol (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)))
107     {
108       line = (type == ly_symbol2scm ("zigzag"))
109         ? zigzag_stencil (me, from, to)
110         : Line_interface::line (me, from, to);
111     }
112   else if (scm_is_symbol (type)
113            && type == ly_symbol2scm ("trill"))
114     {
115       SCM alist_chain = Font_interface::text_font_alist_chain (me);
116       SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
117                                               ly_symbol2scm ("fetaMusic")),
118                                     SCM_UNDEFINED);
119
120       Font_metric *fm = select_font (me->get_layout (),
121                                      scm_cons (style_alist,
122                                                alist_chain));
123       Stencil m = fm->find_by_name ("scripts.trill_element");
124       Stencil mol;
125
126       do
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])
131         ;
132
133       /*
134         FIXME: should center element on x/y
135       */
136       mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
137       mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
138                              + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
139
140       mol.translate (from);
141       line = mol;
142     }
143
144   if (to_boolean (me->get_property ("arrow")))
145     line.add_stencil (Line_interface::arrows (me, from, to, false, true));
146   
147   return line;
148 }
149
150 /*
151   Find a common Y parent, which --if found-- should be the
152   fixed-distance alignment.
153 */
154 Grob *
155 line_spanner_common_parent (Grob *me)
156 {
157   Grob *common = find_fixed_alignment_parent (me);
158   if (!common)
159     {
160       common = Staff_symbol_referencer::get_staff_symbol (me);
161       if (common)
162         common = common->get_parent (Y_AXIS);
163       else
164         common = me->get_parent (Y_AXIS);
165     }
166
167   return common;
168 }
169
170 /*
171   Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
172
173   (If not, you risk that this is called from the staff-alignment
174   routine, via stencil_extent. At this point, the staves aren't
175   separated yet, so it doesn't work cross-staff.
176
177   (huh? crossable staves have fixed distance? --hwn)
178 */
179
180 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
181 SCM
182 Line_spanner::print (SCM smob)
183 {
184   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
185
186   Drul_array<Item *> bound (me->get_bound (LEFT),
187                             me->get_bound (RIGHT));
188
189   Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
190
191   Offset ofxy (gap, 0); /* offset from start point to start of line */
192   Offset dxy;
193   Offset my_off;
194   Offset his_off;
195
196   if (bound[RIGHT]->break_status_dir ())
197     {
198       if (bound[LEFT]->break_status_dir ())
199         {
200           programming_error ("line-spanner with two broken ends. Farewell sweet world.");
201
202           me->suicide ();
203           return SCM_EOL;
204         }
205
206       /*
207         This is hairy. For the normal case, we simply find common
208         parents, and draw a line between the bounds. When two note
209         heads are on different systems, there is no common parent
210         anymore. We have to find the piano-staff object.
211       */
212
213       int k = broken_spanner_index (me);
214       Spanner *parent_sp = dynamic_cast<Spanner *> (me->original_);
215       Spanner *next_sp = parent_sp->broken_intos_ [k + 1];
216       Item *next_bound = next_sp->get_bound (RIGHT);
217
218       if (next_bound->break_status_dir ())
219         {
220           programming_error ("no note heads for the line spanner on next line?"
221                              " Confused.");
222           me->suicide ();
223           return SCM_EOL;
224         }
225
226       Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
227       commonx = me->common_refpoint (commonx, X_AXIS);
228
229       Grob *next_common_y = line_spanner_common_parent (next_bound);
230       Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
231
232       Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
233
234       Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
235       Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
236
237       Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
238
239       Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
240                  this_ext.center () + yoff);
241       Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
242                  next_ext.center () + yoff);
243
244       Offset dz (p2 -p1);
245       Real len = dz.length ();
246
247       Offset dir = dz * (1 / len);
248       dz = (dz.length () - 2 * gap) * dir;
249
250       Stencil l (line_stencil (me, Offset (0, 0), dz));
251
252       l.translate (dir * gap + p1
253                    - Offset (me->relative_coordinate (commonx, X_AXIS),
254                              me->relative_coordinate (all_common_y, Y_AXIS)));
255
256       return l.smobbed_copy ();
257     }
258   else
259     {
260       Grob *common[] = { me, me };
261       for (int a = X_AXIS; a < NO_AXES; a++)
262         {
263           common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
264           common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
265         }
266
267       // distance from center to start of line      
268       Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
269
270       for (int a = X_AXIS; a < NO_AXES; a++)
271         {
272           Axis ax = (Axis)a;
273           dxy[ax]
274             = + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
275             - bound[LEFT]->extent (common[X_AXIS], ax).center ();
276
277           my_off[ax] = me->relative_coordinate (common[a], ax);
278           his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
279         }
280
281       ofxy = dxy * (off / dxy.length ());
282       dxy -= 2*ofxy;
283
284       Stencil line = line_stencil (me, Offset (0, 0), dxy);
285
286       line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
287       line.translate (ofxy - my_off + his_off);
288       return line.smobbed_copy ();
289     }
290 }
291
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",
298                "gap zigzag-width zigzag-length thickness arrow");
299