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