]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie.cc
0ae13646c3bcf604ac51fd18b054884c3d526c00
[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--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "lookup.hh"
10 #include "paper-def.hh"
11 #include "tie.hh"
12 #include "note-head.hh"
13 #include "bezier.hh"
14 #include "paper-column.hh"
15 #include "debug.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "directional-element-interface.hh"
18 #include "molecule.hh"
19 #include "bezier-bow.hh"
20
21 void
22 Tie::set_head (Direction d, Item * head_l)
23 {
24   assert (!head (d));
25   index_set_cell (get_elt_property ("heads"), d, head_l->self_scm_);
26   
27   set_bounds (d, head_l);
28   add_dependency (head_l);
29 }
30
31 Tie::Tie()
32 {
33   set_elt_property ("heads", gh_cons (SCM_EOL, SCM_EOL));
34   dy_f_drul_[LEFT] = dy_f_drul_[RIGHT] = 0.0;
35   dx_f_drul_[LEFT] = dx_f_drul_[RIGHT] = 0.0;
36
37 }
38
39 Note_head* 
40 Tie::head (Direction d) const
41 {
42   SCM c = get_elt_property ("heads");
43   c = index_cell (c, d);
44
45   return dynamic_cast<Note_head*> (unsmob_element (c));  
46 }
47
48
49 /*
50   ugh: direction of the Tie is more complicated.  See [Ross] p136 and further
51  */
52 Direction
53 Tie::get_default_dir () const
54 {
55   Real p1 = Staff_symbol_referencer_interface (head (LEFT)).position_f () ;
56   Real p2 = Staff_symbol_referencer_interface (head (RIGHT)).position_f () ;  
57   
58   int m = int (p1  + p2);
59
60   /*
61     If dir is not determined: inverse of stem: down
62     (see stem::get_default_dir ())
63    */
64   Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
65   return (m == 0) ? other_dir (neutral_dir) : (m < 0) ? DOWN : UP;
66 }
67
68 void
69 Tie::do_add_processing()
70 {
71   if (!(head (LEFT) && head (RIGHT)))
72     warning (_ ("lonely tie"));
73
74   Direction d = LEFT;
75   Drul_array<Note_head *> new_head_drul;
76   new_head_drul[LEFT] = head(LEFT);
77   new_head_drul[RIGHT] = head(RIGHT);  
78   do {
79     if (!head (d))
80       new_head_drul[d] = head((Direction)-d);
81   } while (flip(&d) != LEFT);
82
83   index_set_cell (get_elt_property ("heads"), LEFT, new_head_drul[LEFT]->self_scm_ );
84   index_set_cell (get_elt_property ("heads"), RIGHT, new_head_drul[LEFT]->self_scm_ );
85
86 }
87
88 void
89 Tie::do_post_processing()
90 {
91   if (!head (LEFT) && !head (RIGHT))
92     {
93       programming_error ("Tie without heads.");
94       set_elt_property ("transparent", SCM_BOOL_T);
95       set_empty (X_AXIS);
96       set_empty (Y_AXIS);
97       return;
98     }
99
100   Real staff_space = paper_l ()->get_var ("interline");
101   Real half_staff_space = staff_space / 2;
102   Real x_gap_f = paper_l ()->get_var ("tie_x_gap");
103   Real y_gap_f = paper_l ()->get_var ("tie_y_gap");
104
105   /* 
106    Slur and tie placement [OSU]
107
108    Ties:
109
110        * x = inner vertical tangent - d * gap
111
112    */
113
114
115   /*
116     OSU: not different for outer notes, so why all this code?
117     ie,  can we drop this, or should it be made switchable.
118    */
119 #if 0
120   Direction d = LEFT;
121   do
122     {
123       Real head_width_f = head (d)
124         ? head (d)->extent (X_AXIS).length ()
125         : 0;
126       /*
127         side attached to outer (upper or lower) notehead of chord
128       */
129       if (head (d)
130           /*
131
132                 a~a~a;
133
134             to second tie, middle notehead seems not extremal
135
136             Getting scared a bit by score-element's comment:
137             // is this a good idea?
138           */
139           // FIXME extremal deprecated
140           && (head (d)->get_elt_property ("extremal")
141               != SCM_UNDEFINED))
142         {
143         if (d == LEFT)
144             dx_f_drul_[d] += head_width_f;
145           dx_f_drul_[d] += -d * x_gap_f;
146         }
147       /*
148         side attached to inner notehead
149       */
150       else
151         {
152           dx_f_drul_[d] += -d * head_width_f;
153         }
154     } while (flip (&d) != LEFT);
155
156 #else
157
158   if (head (LEFT))
159     dx_f_drul_[LEFT] = head (LEFT)->extent (X_AXIS).length ();
160   else
161     dx_f_drul_[LEFT] = get_broken_left_end_align ();
162   dx_f_drul_[LEFT] += x_gap_f;
163   dx_f_drul_[RIGHT] -= x_gap_f;
164
165 #endif
166
167   /* 
168    Slur and tie placement [OSU]  -- check this
169
170    Ties:
171
172        * y = dx <  5ss: horizontal tangent
173          y = dx >= 5ss: y next interline - d * 0.25 ss
174
175          which probably means that OSU assumes that
176
177             dy <= 5 dx
178
179          for smal slurs
180    */
181
182
183   Real ypos = head (LEFT)
184     ? Staff_symbol_referencer_interface (head (LEFT)).position_f ()
185     : Staff_symbol_referencer_interface (head (RIGHT)).position_f () ;  
186
187   Real y_f = half_staff_space * ypos; 
188   int ypos_i = int (ypos);
189  
190   Real dx_f = extent (X_AXIS).length () + dx_f_drul_[RIGHT] - dx_f_drul_[LEFT];
191   Direction dir = directional_element (this).get();
192   if (dx_f < paper_l ()->get_var ("tie_staffspace_length"))
193     {
194       if (abs (ypos_i) % 2)
195         y_f += dir * half_staff_space;
196       y_f += dir * y_gap_f;
197     }
198   else
199     {
200       if (! (abs (ypos_i) % 2))
201         y_f += dir * half_staff_space;
202       y_f += dir * half_staff_space;
203       y_f -= dir * y_gap_f;
204     }
205   
206   dy_f_drul_[LEFT] = dy_f_drul_[RIGHT] = y_f;
207 }
208
209
210
211 Array<Rod>
212 Tie::get_rods () const
213 {
214   Array<Rod> a;
215   Rod r;
216   r.item_l_drul_ = spanned_drul_;
217   r.distance_f_ = paper_l ()->get_var ("tie_x_minimum");
218   a.push (r);
219   return a;
220 }
221
222
223
224
225 Molecule*
226 Tie::do_brew_molecule_p () const
227 {
228   Real thick = paper_l ()->get_var ("slur_thickness");
229   Bezier one = get_curve ();
230
231   Molecule a;
232   SCM d =  get_elt_property ("dashed");
233   if (gh_number_p (d))
234     a = lookup_l ()->dashed_slur (one, thick, gh_scm2int (d));
235   else
236     a = lookup_l ()->slur (one, directional_element (this).get () * thick, thick);
237   
238   return new Molecule (a); 
239 }
240
241
242
243 Bezier
244 Tie::get_curve () const
245 {
246   Bezier_bow b (get_encompass_offset_arr (), directional_element (this).get ());
247
248   b.ratio_ = paper_l ()->get_var ("slur_ratio");
249   b.height_limit_ = paper_l ()->get_var ("slur_height_limit");
250   b.rc_factor_ = paper_l ()->get_var ("slur_rc_factor");
251
252   b.calculate ();
253   return b.get_curve ();
254 }
255
256 #if 0
257
258 /*
259   TODO: FIXME.
260  */
261
262 /*
263   Clipping
264
265   This function tries to address two issues:
266     * the tangents of the slur should always point inwards 
267       in the actual slur, i.e.  *after rotating back*.
268
269     * slurs shouldn't be too high 
270       let's try : h <= 1.2 b && h <= 3 staffheight?
271
272   We could calculate the tangent of the bezier curve from
273   both ends going inward, and clip the slur at the point
274   where the tangent (after rotation) points up (or inward
275   with a certain maximum angle).
276   
277   However, we assume that real clipping is not the best
278   answer.  We expect that moving the outer control point up 
279   if the slur becomes too high will result in a nicer slur 
280   after recalculation.
281
282   Knowing that the tangent is the line through the first
283   two control points, we'll clip (move the outer control
284   point upwards) too if the tangent points outwards.
285  */
286
287 bool
288 Bezier_Tie::calc_clipping ()
289 {
290   Real clip_height = paper_l_->get_var ("slur_clip_height");
291   Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
292   Real clip_angle = paper_l_->get_var ("slur_clip_angle");
293
294   Real b = curve_.control_[3][X_AXIS] - curve_.control_[0][X_AXIS];
295   Real clip_h = clip_ratio * b <? clip_height;
296   Real begin_h = curve_.control_[1][Y_AXIS] - curve_.control_[0][Y_AXIS];
297   Real end_h = curve_.control_[2][Y_AXIS] - curve_.control_[3][Y_AXIS];
298   Real begin_dy = 0 >? begin_h - clip_h;
299   Real end_dy = 0 >? end_h - clip_h;
300   
301   Real pi = M_PI;
302   Real begin_alpha = (curve_.control_[1] - curve_.control_[0]).arg () + dir_ * alpha_;
303   Real end_alpha = pi -  (curve_.control_[2] - curve_.control_[3]).arg () - dir_  * alpha_;
304
305   Real max_alpha = clip_angle / 90 * pi / 2;
306   if ((begin_dy < 0) && (end_dy < 0)
307     && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
308     return false;
309
310   transform_back ();
311
312   if ((begin_dy > 0) || (end_dy > 0))
313     {
314       Real dy = (begin_dy + end_dy) / 4;
315       dy *= cos (alpha_);
316       encompass_[0][Y_AXIS] += dir_ * dy;
317       encompass_.top ()[Y_AXIS] += dir_ * dy;
318     }
319   else
320     {
321       //ugh
322       Real c = 0.4;
323       if (begin_alpha >= max_alpha)
324         begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
325       if (end_alpha >= max_alpha)
326         end_dy = 0 >? c * end_alpha / max_alpha * end_h;
327
328       encompass_[0][Y_AXIS] += dir_ * begin_dy;
329       encompass_.top ()[Y_AXIS] += dir_ * end_dy;
330
331       Offset delta = encompass_.top () - encompass_[0];
332       alpha_ = delta.arg ();
333     }
334
335   to_canonic_form ();
336
337   return true;
338 }
339 #endif
340
341
342
343 Array<Offset>
344 Tie::get_encompass_offset_arr () const
345 {
346   Array<Offset> offset_arr;
347   offset_arr.push (Offset (dx_f_drul_[LEFT], dy_f_drul_[LEFT]));
348   offset_arr.push (Offset (spanner_length () + dx_f_drul_[RIGHT],
349                            dy_f_drul_[RIGHT]));
350                       
351   return offset_arr;
352 }
353
354