]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie.cc
* flower
[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         {
236           ypos += 0.5 * dir;
237         }
238     }
239
240   /*
241     todo: prevent ending / staffline collision.
242
243     todo: tie / stem collision
244   */
245
246   b = slur_shape (width, h_inf, r_0);
247   b.scale (1, dir);
248   b.translate (Offset (left_x, ypos));
249
250   /*
251     Avoid colliding of the horizontal part with stafflines.
252
253
254     TODO: redo this, heuristic is half-baken, and ties often look ugly
255     as a result.
256
257     TODO: doesn't work when on staff with even number of lines.
258   */
259   Array<Real> horizontal (b.solve_derivative (Offset (1, 0)));
260   if (horizontal.size ())
261     {
262       /*
263         ugh. Doesnt work for non-horizontal curves.
264       */
265       Real y = b.curve_point (horizontal[0])[Y_AXIS];
266
267       Real ry = rint (y / staff_space) * staff_space;
268       Real diff = ry - y;
269       Real newy = y;
270
271       Real clear = staff_space * scm_to_double (me->get_property ("staffline-clearance"));
272
273       if (fabs (y)
274           <= Staff_symbol_referencer::staff_radius (me) * staff_space + clear
275           && fabs (diff) < clear)
276         {
277           Real y1 = ry + clear;
278           Real y2 = ry - clear;
279
280           /*
281             ugh, we shove the 0.5 out of our sleeves.
282
283             Any way. This test is to make sure that staffline
284             collision avoidance does not result in completely flat
285             ties.
286           */
287           if (fabs (y1 - ypos) < 0.5)
288             y1 = y2;
289           else if (fabs (y2 - ypos) < 0.5)
290             y2 = y1;
291
292           newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
293
294           // newy = ry - 0.5 * staff_space * sign (diff) ;
295
296           /*
297             we don't want horizontal ties
298           */
299           if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
300             {
301               newy = newy + dir * staff_space;
302             }
303         }
304
305       Real y0 = b.control_ [0][Y_AXIS];
306       b.control_[2][Y_AXIS]
307         = b.control_[1][Y_AXIS]
308         = (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
309     }
310   else
311     programming_error ("Tie is nowhere horizontal");
312
313   SCM controls = SCM_EOL;
314   for (int i = 4; i--;)
315     controls = scm_cons (ly_offset2scm (b.control_[i]), controls);
316   return controls;
317 }
318
319 MAKE_SCHEME_CALLBACK (Tie, print, 1);
320 SCM
321 Tie::print (SCM smob)
322 {
323   Grob *me = unsmob_grob (smob);
324
325   SCM cp = me->get_property ("control-points");
326   if (!scm_is_pair (cp))                // list is more accurate
327     {
328       cp = get_control_points (smob);
329       me->set_property ("control-points", cp);
330     }
331
332   if (!scm_is_pair (cp))
333     return Stencil ().smobbed_copy ();
334
335   Real thick
336     = Staff_symbol_referencer::line_thickness (me)
337     * robust_scm2double (me->get_property ("thickness"), 1);
338
339   Bezier b;
340   int i = 0;
341   for (SCM s = cp; s != SCM_EOL; s = scm_cdr (s))
342     {
343       b.control_[i] = ly_scm2offset (scm_car (s));
344       i++;
345     }
346
347   Stencil a = Lookup::slur (b, get_grob_direction (me) * thick, thick);
348
349   return a.smobbed_copy ();
350 }
351
352 ADD_INTERFACE (Tie, "tie-interface",
353                "A tie connecting two noteheads.\n",
354                "y-offset staffline-clearance control-points head-pair details thickness x-gap direction minimum-length");