]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie.cc
(parse_symbol_list): Bugfix.
[lilypond.git] / lily / tie.cc
1 /*
2   tie.cc -- implement Tie
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "tie.hh"
10
11 #include <math.h>
12
13 #include "spanner.hh"
14 #include "lookup.hh"
15 #include "output-def.hh"
16 #include "rhythmic-head.hh"
17 #include "bezier.hh"
18 #include "paper-column.hh"
19 #include "warn.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "directional-element-interface.hh"
22 #include "bezier.hh"
23 #include "stem.hh"
24 #include "note-head.hh"
25 #include "tie-column.hh"
26
27 /*
28   tie: Connect two noteheads.
29
30   What if we have
31
32   c4 ~ \clef bass ; c4 or
33
34   c4 \staffchange c4
35
36   do we have non-horizontal ties then?
37 */
38
39 void
40 Tie::set_head (Grob *me, Direction d, Grob *h)
41 {
42   assert (!head (me, d));
43   index_set_cell (me->get_property ("head-pair"), d, h->self_scm ());
44
45   dynamic_cast<Spanner *> (me)->set_bound (d, h);
46   me->add_dependency (h);
47 }
48
49 void
50 Tie::set_interface (Grob *me)
51 {
52   me->set_property ("head-pair", scm_cons (SCM_EOL, SCM_EOL));
53 }
54
55 Grob *
56 Tie::head (Grob *me, Direction d)
57 {
58   SCM c = me->get_property ("head-pair");
59
60   if (scm_is_pair (c))
61     return unsmob_grob (index_get_cell (c, d));
62   else
63     return 0;
64 }
65
66 int
67 Tie::get_column_rank (Grob *me, Direction d)
68 {
69   Spanner *span = dynamic_cast<Spanner *> (me);
70   Grob *h = head (me, d);
71   if (!h)
72     h = span->get_bound (d);
73
74   Grob *col = dynamic_cast<Item *> (h)->get_column ();
75   return Paper_column::get_rank (col);
76 }
77
78 Real
79 Tie::get_position (Grob *me)
80 {
81   Direction d = head (me, LEFT) ? LEFT : RIGHT;
82   return Staff_symbol_referencer::get_position (head (me, d));
83 }
84
85 /*
86   Default:  Put the tie oppositie of the stem [Wanske p231]
87
88   In case of chords: Tie_column takes over
89
90   The direction of the Tie is more complicated (See [Ross] p136 and
91   further).
92
93   (what about linebreaks? )
94 */
95 Direction
96 Tie::get_default_dir (Grob *me)
97 {
98   Item *sl = head (me, LEFT) ? Rhythmic_head::get_stem (head (me, LEFT)) : 0;
99   Item *sr = head (me, RIGHT) ? Rhythmic_head::get_stem (head (me, RIGHT)) : 0;
100   if (sl && sr)
101     {
102       if (get_grob_direction (sl) == UP
103           && get_grob_direction (sr) == UP)
104         return DOWN;
105     }
106   else if (sl || sr)
107     {
108       Item *s = sl ? sl : sr;
109       return -get_grob_direction (s);
110     }
111
112   return UP;
113 }
114
115 void
116 Tie::set_direction (Grob *me)
117 {
118   if (!get_grob_direction (me))
119     {
120       if (Tie_column::has_interface (me->get_parent (Y_AXIS)))
121         Tie_column::set_directions (me->get_parent (Y_AXIS));
122       else
123         set_grob_direction (me, Tie::get_default_dir (me));
124     }
125 }
126
127 /*
128   TODO: we should also use thickness for computing the clearance
129   between head and tie. Very thick ties will now touch the note head.
130 */
131 SCM
132 Tie::get_control_points (SCM smob)
133 {
134   Spanner *me = unsmob_spanner (smob);
135   Direction headdir = CENTER;
136   if (head (me, LEFT))
137     headdir = LEFT;
138   else if (head (me, RIGHT))
139     headdir = RIGHT;
140   else
141     {
142       programming_error ("tie without heads");
143       me->suicide ();
144       return SCM_EOL;
145     }
146
147   set_direction (me);
148
149   Direction dir = get_grob_direction (me);
150
151   Real staff_space = Staff_symbol_referencer::staff_space (me);
152
153   Real x_gap_f = robust_scm2double (me->get_property ("x-gap"), 0);
154
155   Grob *l = me->get_bound (LEFT);
156   Grob *r = me->get_bound (RIGHT);
157
158   Grob *commonx = me->common_refpoint (l, X_AXIS);
159   commonx = me->common_refpoint (r, X_AXIS);
160
161   Real left_x;
162
163   /*
164     the tie has to be long enough to be visible, but should not go
165     through key sigs. In the 1.5 series the pref.matter - note
166     distance is fixed , so this won't be a problem anymore.
167   */
168   Real lambda = 0.9;
169
170   if (Note_head::has_interface (l))
171     {
172       Real where = RIGHT;
173
174       /*
175         This correction is due te the shape of the black note head.
176       */
177       if (Rhythmic_head::duration_log (l) == 2)
178         where += dir * 0.2;
179       left_x = l->extent (l, X_AXIS).linear_combination (where)
180         + x_gap_f;
181     }
182   else
183     left_x = l->extent (l, X_AXIS).linear_combination (lambda);
184
185   Real width;
186   if (Note_head::has_interface (l) && Note_head::has_interface (r))
187     {
188       width
189         = + r->extent (commonx, X_AXIS)[LEFT]
190         - l->extent (commonx, X_AXIS)[RIGHT]
191         -2 * x_gap_f;
192     }
193   else
194     {
195       if (Note_head::has_interface (l))
196         width = r->relative_coordinate (commonx, X_AXIS)
197           - l->extent (commonx, X_AXIS)[RIGHT]
198           - 2 * x_gap_f;
199       else
200         width
201           = -l->extent (commonx, X_AXIS).linear_combination (lambda)
202           + r->extent (commonx, X_AXIS)[LEFT]
203           - 2 * x_gap_f;
204     }
205
206   SCM details = me->get_property ("details");
207
208   SCM lim // groetjes aan de chirurgendochter.
209     = scm_assq (ly_symbol2scm ("height-limit"), details);
210
211   Real h_inf = scm_to_double (scm_cdr (lim)) * staff_space;
212   Real r_0 = scm_to_double (scm_cdr (scm_assq (ly_symbol2scm ("ratio"), details)));
213
214   Bezier b = slur_shape (width, h_inf, r_0);
215
216   /*
217     I think this better, particularly for small ties. It always allows the user to move ties if
218     they seem in the wrong place
219
220     TODO: what if 2 heads have different size.
221
222   */
223
224   Real ypos = Tie::get_position (me) * staff_space / 2
225     + dir * scm_to_double (me->get_property ("y-offset"));;
226
227   /*
228     Make sure we don't start on a dots
229   */
230   if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
231     {
232       Grob *dots = Rhythmic_head::get_dots (l);
233       if (fabs (staff_space * Staff_symbol_referencer::get_position (dots) / 2
234                 - ypos) < 0.5)
235         ypos += 0.5 * dir;
236     }
237
238   /*
239     todo: prevent ending / staffline collision.
240
241     todo: tie / stem collision
242   */
243
244   b = slur_shape (width, h_inf, r_0);
245   b.scale (1, dir);
246   b.translate (Offset (left_x, ypos));
247
248   /*
249     Avoid colliding of the horizontal part with stafflines.
250
251
252     TODO: redo this, heuristic is half-baken, and ties often look ugly
253     as a result.
254
255     TODO: doesn't work when on staff with even number of lines.
256   */
257   Array<Real> horizontal (b.solve_derivative (Offset (1, 0)));
258   if (horizontal.size ())
259     {
260       /*
261         ugh. Doesnt work for non-horizontal curves.
262       */
263       Real y = b.curve_point (horizontal[0])[Y_AXIS];
264
265       Real ry = rint (y / staff_space) * staff_space;
266       Real diff = ry - y;
267       Real newy = y;
268
269       Real clear = staff_space * scm_to_double (me->get_property ("staffline-clearance"));
270
271       if (fabs (y)
272           <= Staff_symbol_referencer::staff_radius (me) * staff_space + clear
273           && fabs (diff) < clear)
274         {
275           Real y1 = ry + clear;
276           Real y2 = ry - clear;
277
278           /*
279             ugh, we shove the 0.5 out of our sleeves.
280
281             Any way. This test is to make sure that staffline
282             collision avoidance does not result in completely flat
283             ties.
284           */
285           if (fabs (y1 - ypos) < 0.5)
286             y1 = y2;
287           else if (fabs (y2 - ypos) < 0.5)
288             y2 = y1;
289
290           newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
291
292           // newy = ry - 0.5 * staff_space * sign (diff) ;
293
294           /*
295             we don't want horizontal ties
296           */
297           if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
298             newy = newy + dir * staff_space;
299         }
300
301       Real y0 = b.control_ [0][Y_AXIS];
302       b.control_[2][Y_AXIS]
303         = b.control_[1][Y_AXIS]
304         = (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
305     }
306   else
307     programming_error ("tie is nowhere horizontal");
308
309   SCM controls = SCM_EOL;
310   for (int i = 4; i--;)
311     controls = scm_cons (ly_offset2scm (b.control_[i]), controls);
312   return controls;
313 }
314
315 MAKE_SCHEME_CALLBACK (Tie, print, 1);
316 SCM
317 Tie::print (SCM smob)
318 {
319   Grob *me = unsmob_grob (smob);
320
321   SCM cp = me->get_property ("control-points");
322   if (!scm_is_pair (cp))                // list is more accurate
323     {
324       cp = get_control_points (smob);
325       me->set_property ("control-points", cp);
326     }
327
328   if (!scm_is_pair (cp))
329     return Stencil ().smobbed_copy ();
330
331   Real staff_thick = Staff_symbol_referencer::line_thickness (me);
332   Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
333   Real thick = base_thick * staff_thick;
334
335   Bezier b;
336   int i = 0;
337   for (SCM s = cp; s != SCM_EOL; s = scm_cdr (s))
338     {
339       b.control_[i] = ly_scm2offset (scm_car (s));
340       i++;
341     }
342
343   Stencil a;
344
345   SCM p = me->get_property ("dash-period");
346   SCM f = me->get_property ("dash-fraction");
347   if (scm_is_number (p) && scm_is_number (f))
348     a = Lookup::dashed_slur (b,
349                              thick,
350                              robust_scm2double (p, 1.0),
351                              robust_scm2double (f, 0));
352   else
353     a = Lookup::slur (b,
354                       get_grob_direction (me) * staff_thick,
355                       thick);
356
357   return a.smobbed_copy ();
358 }
359
360 ADD_INTERFACE (Tie,
361                "tie-interface",
362                "A tie connecting two noteheads.\n",
363
364                "y-offset dash-period dash-fraction "
365                "staffline-clearance control-points head-pair "
366                "details thickness x-gap direction minimum-length");