]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie.cc
release: 1.3.69
[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 #include <math.h>
9
10 #include "lookup.hh"
11 #include "paper-def.hh"
12 #include "tie.hh"
13 #include "rhythmic-head.hh"
14 #include "bezier.hh"
15 #include "paper-column.hh"
16 #include "debug.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "directional-element-interface.hh"
19 #include "molecule.hh"
20 #include "bezier-bow.hh"
21 #include "stem.hh"
22
23 void
24 Tie::set_head (Score_element*me,Direction d, Item * head_l)
25 {
26   assert (!head (me,d));
27   index_set_cell (me->get_elt_property ("heads"), d, head_l->self_scm_);
28   
29   dynamic_cast<Spanner*> (me)->set_bound (d, head_l);
30   me->add_dependency (head_l);
31 }
32
33 Tie::Tie(SCM s)
34   : Spanner (s)
35 {
36   dy_f_drul_[LEFT] = dy_f_drul_[RIGHT] = 0.0;
37   dx_f_drul_[LEFT] = dx_f_drul_[RIGHT] = 0.0;
38 }
39 void
40 Tie::set_interface (Score_element*me)
41 {
42   me->set_elt_property ("heads", gh_cons (SCM_EOL, SCM_EOL));
43   me->set_interface (ly_symbol2scm ("tie-interface"));
44 }
45 bool
46 Tie::has_interface (Score_element*me)
47 {
48   return me->has_interface (ly_symbol2scm ("tie-interface"));
49 }
50
51 Score_element*
52 Tie::head (Score_element*me, Direction d) 
53 {
54   SCM c = me->get_elt_property ("heads");
55   c = index_cell (c, d);
56
57   return unsmob_element (c);
58 }
59
60 Real
61 Tie::position_f (Score_element*me) 
62 {
63   Direction d = head (me,LEFT) ? LEFT:RIGHT;
64   return Staff_symbol_referencer::position_f (head (me,d));
65 }
66
67
68 /*
69   ugh: direction of the Tie is more complicated.  See [Ross] p136 and further
70  */
71 Direction
72 Tie::get_default_dir (Score_element*me) 
73 {
74   Item * sl =  head(me,LEFT) ? Rhythmic_head::stem_l (head (me,LEFT)) :0;
75   Item * sr =  head(me,RIGHT) ? Rhythmic_head::stem_l (head (me,RIGHT)) :0;  
76
77   if (sl && Directional_element_interface (sl).get () == UP
78       && sr && Directional_element_interface (sr).get () == UP)
79     return DOWN;
80   else
81     return UP;
82 }
83
84
85
86 MAKE_SCHEME_CALLBACK(Tie,after_line_breaking);
87 SCM
88 Tie::after_line_breaking (SCM smob)
89 {
90   Tie*me = dynamic_cast<Tie*> (unsmob_element (smob));
91   
92   if (!head (me,LEFT) && !head (me,RIGHT))
93     {
94       programming_error ("Tie without heads.");
95       me->suicide ();
96       return SCM_UNDEFINED;
97     }
98
99   if (!Directional_element_interface (me).get ())
100     Directional_element_interface (me).set (Tie::get_default_dir (me));
101   
102   Real staff_space = Staff_symbol_referencer::staff_space (me);
103   Real half_space = staff_space / 2;
104   Real x_gap_f = me->paper_l ()->get_var ("tie_x_gap");
105   Real y_gap_f = me->paper_l ()->get_var ("tie_y_gap");
106
107   /* 
108    Slur and tie placement [OSU]
109
110    Ties:
111
112        * x = inner vertical tangent - d * gap
113
114    */
115
116
117   /*
118     OSU: not different for outer notes, so why all me code?
119     ie,  can we drop me, or should it be made switchable.
120    */
121   if (head (me,LEFT))
122     me->dx_f_drul_[LEFT] = Tie::head (me,LEFT)->extent (X_AXIS).length ();
123   else
124     me->dx_f_drul_[LEFT] = dynamic_cast<Spanner*>(me)->get_broken_left_end_align ();
125   me->dx_f_drul_[LEFT] += x_gap_f;
126   me->dx_f_drul_[RIGHT] -= x_gap_f;
127
128   /* 
129    Slur and tie placement [OSU]  -- check me
130
131    Ties:
132
133        * y = dx <  5ss: horizontal tangent
134          y = dx >= 5ss: y next interline - d * 0.25 ss
135
136          which probably means that OSU assumes that
137
138             dy <= 5 dx
139
140          for smal slurs
141    */
142
143
144   Real ypos = Tie::position_f (me);
145
146   Real y_f = half_space * ypos; 
147   int ypos_i = int (ypos);
148  
149   Real dx_f = me->extent (X_AXIS).length () + me->dx_f_drul_[RIGHT] - me->dx_f_drul_[LEFT];
150   Direction dir = Directional_element_interface (me).get();
151   if (dx_f < me->paper_l ()->get_var ("tie_staffspace_length"))
152     {
153       if (abs (ypos_i) % 2)
154         y_f += dir * half_space;
155       y_f += dir * y_gap_f;
156     }
157   else
158     {
159       if (! (abs (ypos_i) % 2))
160         y_f += dir * half_space;
161       y_f += dir * half_space;
162       y_f -= dir * y_gap_f;
163     }
164   
165   me->dy_f_drul_[LEFT] = me->dy_f_drul_[RIGHT] = y_f;
166
167   return SCM_UNDEFINED;
168 }
169
170
171
172 Array<Rod>
173 Tie::get_rods () const
174 {
175   Array<Rod> a;
176   Rod r;
177
178   r.item_l_drul_ [LEFT]=get_bound (LEFT);
179   r.item_l_drul_ [RIGHT]=get_bound (RIGHT);  
180   
181   r.distance_f_ = paper_l ()->get_var ("tie_x_minimum");
182   a.push (r);
183   return a;
184 }
185
186 MAKE_SCHEME_CALLBACK(Tie,brew_molecule);
187
188 SCM
189 Tie::brew_molecule (SCM smob) 
190 {
191   Score_element*me = unsmob_element (smob);
192   Real thick = me->paper_l ()->get_var ("tie_thickness");
193   Bezier one = dynamic_cast<Tie*> (me)->get_curve ();
194
195   Molecule a;
196   SCM d =  me->get_elt_property ("dashed");
197   if (gh_number_p (d))
198     a = me->lookup_l ()->dashed_slur (one, thick, gh_scm2int (d));
199   else
200     a = me->lookup_l ()->slur (one, Directional_element_interface (me).get () * thick, thick);
201   
202   return a.create_scheme(); 
203 }
204
205
206
207 Bezier
208 Tie::get_curve () const
209 {
210   Score_element*me = (Score_element*)this;
211   Direction d (Directional_element_interface (me).get ());
212   Bezier_bow b (get_encompass_offset_arr (), d);
213
214   Real staff_space = Staff_symbol_referencer::staff_space (me);
215   Real h_inf = paper_l ()->get_var ("tie_height_limit_factor") * staff_space;
216   Real r_0 = paper_l ()->get_var ("tie_ratio");
217
218   b.set_default_bezier (h_inf, r_0);
219   Bezier c = b.get_bezier ();
220
221   /* should do me for slurs as well. */
222   Array<Real> horizontal (c.solve_derivative (Offset (1,0)));
223
224   if (horizontal.size ())
225     {
226       /*
227         ugh. Doesnt work for non-horizontal curves.
228        */
229       Real y = c.curve_point (horizontal[0])[Y_AXIS];
230
231       Real ry = rint (y/staff_space) * staff_space;
232       Real diff = ry - y;
233       Real newy = y;
234       if (fabs (y) <= 2.0
235           && fabs (diff) < paper_l ()->get_var ("tie_staffline_clearance"))
236         {
237           newy = ry - 0.5 * staff_space * sign (diff) ;
238         }
239
240       Real y0 = c.control_ [0][Y_AXIS];
241       c.control_[2][Y_AXIS] = 
242       c.control_[1][Y_AXIS] =
243         (c.control_[1][Y_AXIS] - y0)  * ((newy - y0) / (y - y0)) + y0; 
244     }
245   else
246     programming_error ("Tie is nowhere horizontal");
247   return c;
248 }
249
250 Array<Offset>
251 Tie::get_encompass_offset_arr () const
252 {
253   Array<Offset> offset_arr;
254   offset_arr.push (Offset (dx_f_drul_[LEFT], dy_f_drul_[LEFT]));
255   offset_arr.push (Offset (spanner_length () + dx_f_drul_[RIGHT],
256                            dy_f_drul_[RIGHT]));
257                       
258   return offset_arr;
259 }
260
261