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