lilypond-1.3.100
[lilypond.git] / lily / tie.cc
blobc68d2ebe203856c7a6adf2c65fe35f7009c871c1
1 /*
2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include <math.h>
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 "debug.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"
27 tie: Connect two noteheads.
29 What if we have
31 c4 ~ \clef bass ; c4 or
33 c4 \staffchange c4
35 do we have non-horizontal ties then?
39 void
40 Tie::set_head (Score_element*me,Direction d, Item * head_l)
42 assert (!head (me,d));
43 index_set_cell (me->get_elt_property ("heads"), d, head_l->self_scm ());
45 dynamic_cast<Spanner*> (me)->set_bound (d, head_l);
46 me->add_dependency (head_l);
49 void
50 Tie::set_interface (Score_element*me)
52 me->set_elt_property ("heads", gh_cons (SCM_EOL, SCM_EOL));
53 me->set_interface (ly_symbol2scm ("tie-interface"));
56 bool
57 Tie::has_interface (Score_element*me)
59 return me->has_interface (ly_symbol2scm ("tie-interface"));
62 Score_element*
63 Tie::head (Score_element*me, Direction d)
65 SCM c = me->get_elt_property ("heads");
66 c = index_cell (c, d);
68 return unsmob_element (c);
71 Real
72 Tie::position_f (Score_element*me)
74 Direction d = head (me,LEFT) ? LEFT:RIGHT;
75 return Staff_symbol_referencer::position_f (head (me,d));
80 Default: Put the tie oppositie of the stem [Wanske p231]
82 In case of chords: Tie_column takes over
84 The direction of the Tie is more complicated (See [Ross] p136 and
85 further).
87 Direction
88 Tie::get_default_dir (Score_element*me)
90 Item * sl = head(me,LEFT) ? Rhythmic_head::stem_l (head (me,LEFT)) :0;
91 Item * sr = head(me,RIGHT) ? Rhythmic_head::stem_l (head (me,RIGHT)) :0;
93 if (sl && sr)
95 if (Directional_element_interface::get (sl) == UP
96 && Directional_element_interface::get (sr) == UP)
97 return DOWN;
99 else if (sl || sr)
101 Item *s = sl ? sl : sr;
102 return - Directional_element_interface::get (s);
106 return UP;
111 Tie::get_control_points (SCM smob)
113 Spanner*me = dynamic_cast<Spanner*> (unsmob_element (smob));
114 Direction headdir = CENTER;
115 if (head (me,LEFT))
116 headdir = LEFT;
117 else if (head(me,RIGHT))
118 headdir = RIGHT;
119 else
121 programming_error ("Tie without heads.");
122 me->suicide ();
123 return SCM_UNSPECIFIED;
126 if (!Directional_element_interface::get (me))
127 Directional_element_interface::set (me, Tie::get_default_dir (me));
129 Real staff_space = Staff_symbol_referencer::staff_space (me);
131 Real x_gap_f = gh_scm2double (me->get_elt_property ("x-gap"));
133 Score_element* l = me->get_bound (LEFT);
134 Score_element* r = me->get_bound (RIGHT);
136 Score_element* commonx = me->common_refpoint (l, X_AXIS);
137 commonx = me->common_refpoint (r, X_AXIS);
139 Real left_x;
142 this is a kludge: the tie has to be long enough to be
143 visible, but should not go through key sigs.
145 (please fixme)
147 Real lambda = 0.5;
149 if (Note_head::has_interface (me->get_bound (LEFT)))
150 left_x = l->extent (l, X_AXIS)[RIGHT] + x_gap_f;
151 else
152 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
155 Real width;
156 if (Note_head::has_interface (me->get_bound (LEFT))
157 && Note_head::has_interface (me->get_bound (RIGHT)))
159 width =
160 + r->extent (commonx,X_AXIS)[LEFT]
161 - l->extent (commonx, X_AXIS)[RIGHT]
162 -2 * x_gap_f;
164 else
166 if (Note_head::has_interface (me->get_bound (LEFT)))
167 width = r->relative_coordinate (commonx, X_AXIS)
168 - l->extent (commonx, X_AXIS)[RIGHT]
169 - 2 * x_gap_f;
170 else
171 width =
172 - l->extent (commonx, X_AXIS).linear_combination (lambda)
173 + r->extent (commonx, X_AXIS)[LEFT]
174 - 2 * x_gap_f;
177 Direction dir = Directional_element_interface::get(me);
179 SCM details = me->get_elt_property ("details");
181 SCM lim // groetjes aan de chirurgendochter.
182 = scm_assq (ly_symbol2scm ("height-limit"),details);
184 Real h_inf = gh_scm2double (gh_cdr (lim)) * staff_space;
185 Real r_0 = gh_scm2double (gh_cdr (scm_assq (ly_symbol2scm ("ratio"),details)));
187 Bezier b = slur_shape (width, h_inf, r_0);
189 Offset leave_dir = b.control_[1] - b.control_[0];
191 Score_element *hed =head (me, headdir);
192 Real dx = (hed->extent (hed, X_AXIS).length () + x_gap_f)/2.0;
193 Real max_gap = leave_dir[Y_AXIS] * dx / leave_dir[X_AXIS];
196 for small ties (t small) we want to start in the Y-center (so dy = 0), for
197 large ties, the tie should appear to come from the center of the
198 head, so dy = max_gap
200 maybe use a different formula?
202 TODO: what if 2 heads have different size.
204 TODO: for small ties, it is better to start over the heads
205 iso. next to the heads.
207 Real t = (width / staff_space - 5.0); // ugh.
208 Real dy = t > 0 ? max_gap * sqr (t / (1 + t)) : 0.0;
210 Real ypos = Tie::position_f (me) * staff_space/2 + dir * dy;
213 todo: prevent ending / staffline collision.
215 todo: tie / stem collision
218 b = slur_shape(width,h_inf, r_0);
219 b.scale (1, dir);
220 b.translate (Offset (left_x, ypos));
224 Avoid colliding of the horizontal part with stafflines.
227 TODO: redo this, heuristic is half-baken, and ties often look ugly
228 as a result.
230 TODO: doesn't work when on staff with even number of lines.
232 Array<Real> horizontal (b.solve_derivative (Offset (1,0)));
233 if (horizontal.size ())
236 ugh. Doesnt work for non-horizontal curves.
238 Real y = b.curve_point (horizontal[0])[Y_AXIS];
240 Real ry = rint (y/staff_space) * staff_space;
241 Real diff = ry - y;
242 Real newy = y;
244 Real clear = staff_space * gh_scm2double (me->get_elt_property ("staffline-clearance"));
246 if (fabs (y) <= Staff_symbol_referencer::staff_radius (me)
247 && fabs (diff) < clear)
249 Real y1 = ry + clear;
250 Real y2 = ry - clear;
252 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
254 // newy = ry - 0.5 * staff_space * sign (diff) ;
257 we don't want horizontal ties
259 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
261 newy = newy + dir * staff_space;
265 Real y0 = b.control_ [0][Y_AXIS];
266 b.control_[2][Y_AXIS] =
267 b.control_[1][Y_AXIS] =
268 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
270 else
271 programming_error ("Tie is nowhere horizontal");
275 SCM controls = SCM_EOL;
276 for (int i= 4; i--;)
277 controls = gh_cons ( ly_offset2scm (b.control_[i]), controls);
278 return controls;
281 MAKE_SCHEME_CALLBACK(Tie,set_spacing_rods,1);
284 TODO: set minimum distances for begin/end of line
287 Tie::set_spacing_rods (SCM smob)
289 Score_element*me = unsmob_element (smob);
290 Spanner*sp = dynamic_cast<Spanner*> (me);
291 Rod r;
293 r.item_l_drul_ [LEFT]=sp->get_bound (LEFT);
294 r.item_l_drul_ [RIGHT]=sp->get_bound (RIGHT);
296 r.distance_f_
297 = gh_scm2double (me->get_elt_property ("minimum-length"))
298 * me->paper_l ()->get_var ("staffspace");
299 r.add_to_cols ();
300 return SCM_UNSPECIFIED;
303 MAKE_SCHEME_CALLBACK(Tie,brew_molecule,1);
305 Tie::brew_molecule (SCM smob)
307 Score_element*me = unsmob_element (smob);
309 SCM cp = me->get_elt_property ("control-points");
310 if (cp == SCM_EOL)
312 cp = get_control_points (smob);
313 me->set_elt_property ("control-points", cp);
316 Real thick =
317 gh_scm2double (me->get_elt_property ("thickness"))
318 * me->paper_l ()->get_var ("stafflinethickness");
320 Bezier b;
321 int i = 0;
322 for (SCM s= cp; s != SCM_EOL; s = gh_cdr (s))
324 b.control_[i] = ly_scm2offset (gh_car (s));
325 i++;
328 Molecule a = Lookup::slur (b, Directional_element_interface::get (me) * thick, thick);
330 return a.create_scheme ();