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