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