]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie.cc
($(outdir)/%.pdf): add DVIPS_FLAGS. This will
[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--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include <math.h>
9
10
11 #include "spanner.hh"
12 #include "lookup.hh"
13 #include "paper-def.hh"
14 #include "tie.hh"
15 #include "rhythmic-head.hh"
16 #include "bezier.hh"
17 #include "paper-column.hh"
18 #include "warn.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "directional-element-interface.hh"
21 #include "stencil.hh"
22 #include "bezier-bow.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_grob_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_grob_property ("head-pair", gh_cons (SCM_EOL, SCM_EOL));
54 }
55
56
57 Grob*
58 Tie::head (Grob*me, Direction d) 
59 {
60   SCM c = me->get_grob_property ("head-pair");
61
62   if (gh_pair_p (c))
63     return unsmob_grob (index_get_cell (c, d));
64   else
65     return 0;
66 }
67
68 Real
69 Tie::get_position (Grob*me) 
70 {
71   Direction d = head (me,LEFT) ? LEFT:RIGHT;
72   return Staff_symbol_referencer::get_position (head (me,d));
73 }
74
75
76 /*
77   Default:  Put the tie oppositie of the stem [Wanske p231]
78
79   In case of chords: Tie_column takes over
80   
81   The direction of the Tie is more complicated (See [Ross] p136 and
82   further).
83
84   (what about linebreaks? )  
85   
86 */
87 Direction
88 Tie::get_default_dir (Grob*me) 
89 {
90   Item * sl =  head (me,LEFT) ? Rhythmic_head::get_stem (head (me,LEFT)) :0;
91   Item * sr =  head (me,RIGHT) ? Rhythmic_head::get_stem (head (me,RIGHT)) :0;  
92   if (sl && sr)
93     {
94       if (get_grob_direction (sl) == UP
95           && get_grob_direction (sr) == UP)
96         return DOWN;
97     }
98   else if (sl || sr)
99     {
100       Item *s = sl ? sl : sr;
101       return - get_grob_direction (s);
102     }
103
104   
105   return UP;
106 }
107
108
109 void
110 Tie::set_direction (Grob*me)
111 {
112   if (!get_grob_direction (me))
113     {
114       if (Tie_column::has_interface (me->get_parent (Y_AXIS)))
115         Tie_column::set_directions (me->get_parent (Y_AXIS));
116       else
117         set_grob_direction (me, Tie::get_default_dir (me));
118     }
119 }
120
121 /*
122   TODO: we should also use thickness for computing the clearance
123   between head and tie. Very thick ties will now touch the note head.
124   
125   */
126 SCM
127 Tie::get_control_points (SCM smob)
128 {  
129   Spanner*me = unsmob_spanner (smob);
130   Direction headdir = CENTER; 
131   if (head (me,LEFT))
132     headdir = LEFT;
133   else if (head (me,RIGHT))
134     headdir = RIGHT;
135   else
136     {
137       programming_error ("Tie without heads.");
138       me->suicide ();
139       return SCM_EOL;
140     }
141
142   set_direction (me);
143
144   Direction dir = get_grob_direction (me);
145   
146   Real staff_space = Staff_symbol_referencer::staff_space (me);
147
148   Real x_gap_f = robust_scm2double (me->get_grob_property ("x-gap"), 0);
149
150   Grob* l = me->get_bound (LEFT);
151   Grob* r = me->get_bound (RIGHT);  
152
153   Grob* commonx = me->common_refpoint (l, X_AXIS);
154   commonx = me->common_refpoint (r, X_AXIS);
155   
156   Real left_x;
157
158   /*
159      the tie has to be long enough to be visible, but should not go
160     through key sigs. In the 1.5 series the pref.matter - note
161     distance is fixed , so this won't be a problem anymore.
162    */
163   Real lambda = 0.9;            
164   
165   if (Note_head::has_interface (l))
166     {
167       Real where = RIGHT;
168
169       /*
170         This correction is due te the shape of the black note head.
171        */
172       if (Rhythmic_head::duration_log (l) == 2)
173         where += dir* 0.2;
174       left_x = l->extent (l, X_AXIS).linear_combination (where)
175         + x_gap_f;
176     }
177   else
178     left_x = l->extent (l, X_AXIS).linear_combination (lambda);
179   
180
181   Real width;
182   if (Note_head::has_interface (l) && Note_head::has_interface (r))
183     {
184       width = 
185         + r->extent (commonx,X_AXIS)[LEFT]
186         - l->extent (commonx, X_AXIS)[RIGHT]
187         -2 * x_gap_f;
188     }
189   else
190     {
191       if (Note_head::has_interface (l))
192         width = r->relative_coordinate (commonx, X_AXIS)
193           - l->extent (commonx, X_AXIS)[RIGHT]
194           - 2 * x_gap_f;
195       else
196         width =
197           - l->extent (commonx, X_AXIS).linear_combination (lambda)  
198           + r->extent (commonx, X_AXIS)[LEFT]
199           - 2 * x_gap_f;
200     }
201   
202
203
204   SCM details = me->get_grob_property ("details");
205
206   SCM lim // groetjes aan de chirurgendochter.
207     = scm_assq (ly_symbol2scm ("height-limit"),details);
208   
209   Real h_inf = gh_scm2double (ly_cdr (lim)) *  staff_space;
210   Real r_0 = gh_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details)));
211
212   Bezier b  = slur_shape (width, h_inf, r_0);
213   
214   /*
215     I think this better, particularly for small ties. It always allows the user to move ties if
216     they seem in the wrong place
217
218     TODO: what if 2 heads have different size.
219
220   */
221
222   Real ypos = Tie::get_position (me) * staff_space/2
223     + dir * gh_scm2double (me->get_grob_property ("y-offset"));;
224
225   /*
226     Make sure we don't start on a dots
227    */
228   if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
229     {
230       Grob* dots = Rhythmic_head::get_dots (l);
231       if(fabs (staff_space * Staff_symbol_referencer::get_position (dots) /2
232                - ypos) < 0.5)
233         {
234           ypos += 0.5 * dir ;
235         }
236     }
237
238   
239   /*
240     todo: prevent ending / staffline collision.
241
242     todo: tie / stem collision
243    */
244
245   b = slur_shape (width,h_inf, r_0);
246   b.scale (1, dir);
247   b.translate (Offset (left_x, ypos));
248   
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 * gh_scm2double (me->get_grob_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
314
315   SCM controls = SCM_EOL;
316   for (int i= 4; i--;)
317     controls = gh_cons (ly_offset2scm (b.control_[i]), controls);
318   return controls;
319 }
320
321
322 MAKE_SCHEME_CALLBACK (Tie,print,1);
323 SCM
324 Tie::print (SCM smob) 
325 {
326   Grob*me = unsmob_grob (smob);
327
328   SCM cp = me->get_grob_property ("control-points");
329   if (!gh_pair_p (cp))          // list is more accurate
330     {
331       cp = get_control_points (smob);
332       me->set_grob_property ("control-points", cp);
333     }
334
335   if (!gh_pair_p (cp))
336     return Stencil ().smobbed_copy ();
337   
338   Real thick
339     = Staff_symbol_referencer::line_thickness (me)
340     *  robust_scm2double (me->get_grob_property ("thickness"), 1);
341
342   Bezier b;
343   int i = 0;
344   for (SCM s= cp; s != SCM_EOL; s = ly_cdr (s))
345     {
346       b.control_[i] = ly_scm2offset (ly_car (s));
347       i++;
348     }
349   
350    Stencil a = Lookup::slur (b, get_grob_direction (me) * thick, thick);
351    
352    return a.smobbed_copy ();
353 }
354
355
356
357 ADD_INTERFACE (Tie,"tie-interface",
358   "A tie connecting two noteheads.\n"
359 "direction = Forced direction for all ties",
360   "y-offset staffline-clearance control-points head-pair details thickness x-gap direction minimum-length");