lilypond-1.3.80
[lilypond.git] / lily / tie.cc
blob2b214131b1f4d5f4ee03169d1bfa4a0b5ac7d24f
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>
10 #include "spanner.hh"
11 #include "lookup.hh"
12 #include "paper-def.hh"
13 #include "tie.hh"
14 #include "rhythmic-head.hh"
15 #include "bezier.hh"
16 #include "paper-column.hh"
17 #include "debug.hh"
18 #include "staff-symbol-referencer.hh"
19 #include "directional-element-interface.hh"
20 #include "molecule.hh"
21 #include "bezier-bow.hh"
22 #include "stem.hh"
25 tie: Connect two noteheads.
27 What if we have
29 c4 ~ \clef bass ; c4 or
31 c4 \staffchange c4
33 do we have non-horizontal ties then?
37 void
38 Tie::set_head (Score_element*me,Direction d, Item * head_l)
40 assert (!head (me,d));
41 index_set_cell (me->get_elt_property ("heads"), d, head_l->self_scm ());
43 dynamic_cast<Spanner*> (me)->set_bound (d, head_l);
44 me->add_dependency (head_l);
47 void
48 Tie::set_interface (Score_element*me)
50 me->set_elt_property ("heads", gh_cons (SCM_EOL, SCM_EOL));
51 me->set_interface (ly_symbol2scm ("tie-interface"));
54 bool
55 Tie::has_interface (Score_element*me)
57 return me->has_interface (ly_symbol2scm ("tie-interface"));
60 Score_element*
61 Tie::head (Score_element*me, Direction d)
63 SCM c = me->get_elt_property ("heads");
64 c = index_cell (c, d);
66 return unsmob_element (c);
69 Real
70 Tie::position_f (Score_element*me)
72 Direction d = head (me,LEFT) ? LEFT:RIGHT;
73 return Staff_symbol_referencer::position_f (head (me,d));
78 The direction of the Tie is more complicated (See [Ross] p136 and
79 further), the case of multiple ties is handled by Tie_column.
81 Direction
82 Tie::get_default_dir (Score_element*me)
84 Item * sl = head(me,LEFT) ? Rhythmic_head::stem_l (head (me,LEFT)) :0;
85 Item * sr = head(me,RIGHT) ? Rhythmic_head::stem_l (head (me,RIGHT)) :0;
88 if (sl && sr)
90 if (Directional_element_interface (sl).get () == UP
91 && Directional_element_interface (sr).get () == UP)
92 return DOWN;
94 else if (sl || sr)
96 Item *s = sl ? sl : sr;
97 return - Directional_element_interface (s). get ();
101 return UP;
106 Tie::get_control_points (SCM smob)
108 Spanner*me = dynamic_cast<Spanner*> (unsmob_element (smob));
109 Direction headdir = CENTER;
110 if (head (me,LEFT))
111 headdir = LEFT;
112 else if (head(me,RIGHT))
113 headdir = RIGHT;
114 else
116 programming_error ("Tie without heads.");
117 me->suicide ();
118 return SCM_UNSPECIFIED;
121 if (!Directional_element_interface (me).get ())
122 Directional_element_interface (me).set (Tie::get_default_dir (me));
124 Real staff_space = Staff_symbol_referencer::staff_space (me);
126 Real x_gap_f = me->paper_l ()->get_var ("tie_x_gap");
128 Score_element* commonx = me->common_refpoint (me->get_bound (LEFT), X_AXIS);
129 commonx = me->common_refpoint (me->get_bound (RIGHT), X_AXIS);
131 Score_element* l = me->get_bound (LEFT);
132 Score_element* r = me->get_bound (RIGHT);
133 Real width = r->relative_coordinate (commonx, X_AXIS)
134 + r->extent (X_AXIS)[LEFT]
135 - l->relative_coordinate (commonx, X_AXIS)
136 - l->extent (X_AXIS)[RIGHT]
137 -2* x_gap_f;
139 Real left_x = l->extent (X_AXIS)[RIGHT] + x_gap_f;
141 Direction dir = Directional_element_interface (me).get();
143 Real h_inf = me->paper_l ()->get_var ("tie_height_limit_factor") * staff_space;
144 Real r_0 = me->paper_l ()->get_var ("tie_ratio");
147 Bezier b = slur_shape (width, h_inf, r_0);
149 Offset leave_dir = b.control_[1] - b.control_[0];
151 Real dx = (head (me, headdir)->extent (X_AXIS).length () + x_gap_f)/2.0;
152 Real max_gap = leave_dir[Y_AXIS] * dx / leave_dir[X_AXIS];
155 for small ties (t small) we want to start in the Y-center (so dy = 0), for
156 large ties, the tie should appear to come from the center of the
157 head, so dy = max_gap
159 maybe use a different formula?
161 TODO: what if 2 heads have different size.
163 TODO: for small ties, it is better to start over the heads
164 iso. next to the heads.
166 Real t = (width / staff_space - 5.0); // ugh.
167 Real dy = t > 0 ? max_gap * sqr (t / (1 + t)) : 0.0;
169 Real ypos = Tie::position_f (me) * staff_space/2 + dir * dy;
172 todo: prevent ending / staffline collision.
174 todo: tie / stem collision
177 b = slur_shape(width,h_inf, r_0);
178 b.scale (1, dir);
179 b.translate (Offset (left_x, ypos));
183 Avoid colliding of the horizontal part with stafflines.
185 should do me for slurs as well.
188 Array<Real> horizontal (b.solve_derivative (Offset (1,0)));
189 if (horizontal.size ())
192 ugh. Doesnt work for non-horizontal curves.
194 Real y = b.curve_point (horizontal[0])[Y_AXIS];
196 Real ry = rint (y/staff_space) * staff_space;
197 Real diff = ry - y;
198 Real newy = y;
199 if (fabs (y) <= Staff_symbol_referencer::staff_radius (me)
200 && fabs (diff) < me->paper_l ()->get_var ("tie_staffline_clearance"))
202 newy = ry - 0.5 * staff_space * sign (diff) ;
205 Real y0 = b.control_ [0][Y_AXIS];
206 b.control_[2][Y_AXIS] =
207 b.control_[1][Y_AXIS] =
208 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
210 else
211 programming_error ("Tie is nowhere horizontal");
215 SCM controls = SCM_EOL;
216 for (int i= 4; i--;)
217 controls = gh_cons ( ly_offset2scm (b.control_[i]), controls);
218 return controls;
221 MAKE_SCHEME_CALLBACK(Tie,set_spacing_rods);
224 TODO: set minimum distances for begin/end of line
227 Tie::set_spacing_rods (SCM smob)
229 Score_element*me = unsmob_element (smob);
230 Spanner*sp = dynamic_cast<Spanner*> (me);
231 Rod r;
233 r.item_l_drul_ [LEFT]=sp->get_bound (LEFT);
234 r.item_l_drul_ [RIGHT]=sp->get_bound (RIGHT);
236 r.distance_f_
237 = gh_scm2double (me->get_elt_property ("minimum-length"))
238 * me->paper_l ()->get_var ("staffspace");
239 r.add_to_cols ();
240 return SCM_UNSPECIFIED;
243 MAKE_SCHEME_CALLBACK(Tie,brew_molecule);
245 Tie::brew_molecule (SCM smob)
247 Score_element*me = unsmob_element (smob);
249 SCM cp = me->get_elt_property ("control-points");
250 if (cp == SCM_EOL)
252 cp = get_control_points (smob);
253 me->set_elt_property ("control-points", cp);
256 Real thick =
257 gh_scm2double (me->get_elt_property ("thickness"))
258 * me->paper_l ()->get_var ("stafflinethickness");
260 Bezier b;
261 int i = 0;
262 for (SCM s= cp; s != SCM_EOL; s = gh_cdr (s))
264 b.control_[i] = ly_scm2offset (gh_car (s));
265 i++;
268 Molecule a = me->lookup_l ()->slur (b, Directional_element_interface (me).get () * thick, thick);
270 return a.create_scheme ();