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>
12 #include "paper-def.hh"
14 #include "rhythmic-head.hh"
16 #include "paper-column.hh"
18 #include "staff-symbol-referencer.hh"
19 #include "directional-element-interface.hh"
20 #include "molecule.hh"
21 #include "bezier-bow.hh"
23 #include "note-head.hh"
26 tie: Connect two noteheads.
30 c4 ~ \clef bass ; c4 or
34 do we have non-horizontal ties then?
39 Tie::set_head (Score_element*me,Direction d, Item * head_l)
41 assert (!head (me,d));
42 index_set_cell (me->get_elt_property ("heads"), d, head_l->self_scm ());
44 dynamic_cast<Spanner*> (me)->set_bound (d, head_l);
45 me->add_dependency (head_l);
49 Tie::set_interface (Score_element*me)
51 me->set_elt_property ("heads", gh_cons (SCM_EOL, SCM_EOL));
52 me->set_interface (ly_symbol2scm ("tie-interface"));
56 Tie::has_interface (Score_element*me)
58 return me->has_interface (ly_symbol2scm ("tie-interface"));
62 Tie::head (Score_element*me, Direction d)
64 SCM c = me->get_elt_property ("heads");
65 c = index_cell (c, d);
67 return unsmob_element (c);
71 Tie::position_f (Score_element*me)
73 Direction d = head (me,LEFT) ? LEFT:RIGHT;
74 return Staff_symbol_referencer::position_f (head (me,d));
79 Default: Put the tie oppositie of the stem [Wanske p231]
81 In case of chords: Tie_column takes over
83 The direction of the Tie is more complicated (See [Ross] p136 and
87 Tie::get_default_dir (Score_element*me)
89 Item * sl = head(me,LEFT) ? Rhythmic_head::stem_l (head (me,LEFT)) :0;
90 Item * sr = head(me,RIGHT) ? Rhythmic_head::stem_l (head (me,RIGHT)) :0;
94 if (Directional_element_interface::get (sl) == UP
95 && Directional_element_interface::get (sr) == UP)
100 Item *s = sl ? sl : sr;
101 return - Directional_element_interface::get (s);
110 Tie::get_control_points (SCM smob)
112 Spanner*me = dynamic_cast<Spanner*> (unsmob_element (smob));
113 Direction headdir = CENTER;
116 else if (head(me,RIGHT))
120 programming_error ("Tie without heads.");
122 return SCM_UNSPECIFIED;
125 if (!Directional_element_interface::get (me))
126 Directional_element_interface::set (me, Tie::get_default_dir (me));
128 Real staff_space = Staff_symbol_referencer::staff_space (me);
130 Real x_gap_f = gh_scm2double (me->get_elt_property ("x-gap"));
132 Score_element* commonx = me->common_refpoint (me->get_bound (LEFT), X_AXIS);
133 commonx = me->common_refpoint (me->get_bound (RIGHT), X_AXIS);
135 Score_element* l = me->get_bound (LEFT);
136 Score_element* r = me->get_bound (RIGHT);
139 if (Note_head::has_interface (me->get_bound (LEFT)))
140 left_x = l->extent (X_AXIS)[RIGHT] + x_gap_f;
142 left_x = l->extent (X_AXIS).length () / 2;
145 if (Note_head::has_interface (me->get_bound (LEFT))
146 && Note_head::has_interface (me->get_bound (RIGHT)))
148 width = r->relative_coordinate (commonx, X_AXIS)
149 + r->extent (X_AXIS)[LEFT]
150 - l->relative_coordinate (commonx, X_AXIS)
151 - l->extent (X_AXIS)[RIGHT]
156 if (Note_head::has_interface (me->get_bound (LEFT)))
157 width = r->relative_coordinate (commonx, X_AXIS)
158 - l->relative_coordinate (commonx, X_AXIS)
159 - l->extent (X_AXIS)[RIGHT]
162 width = r->relative_coordinate (commonx, X_AXIS)
163 - l->extent (X_AXIS).length () / 2
164 + r->extent (X_AXIS)[LEFT]
165 - l->relative_coordinate (commonx, X_AXIS)
169 Direction dir = Directional_element_interface::get(me);
171 SCM details = me->get_elt_property ("details");
173 SCM lim // groetjes aan de chirurgendochter.
174 = scm_assq (ly_symbol2scm ("height-limit"),details);
176 Real h_inf = gh_scm2double (gh_cdr (lim)) * staff_space;
177 Real r_0 = gh_scm2double (gh_cdr (scm_assq (ly_symbol2scm ("ratio"),details)));
179 Bezier b = slur_shape (width, h_inf, r_0);
181 Offset leave_dir = b.control_[1] - b.control_[0];
183 Real dx = (head (me, headdir)->extent (X_AXIS).length () + x_gap_f)/2.0;
184 Real max_gap = leave_dir[Y_AXIS] * dx / leave_dir[X_AXIS];
187 for small ties (t small) we want to start in the Y-center (so dy = 0), for
188 large ties, the tie should appear to come from the center of the
189 head, so dy = max_gap
191 maybe use a different formula?
193 TODO: what if 2 heads have different size.
195 TODO: for small ties, it is better to start over the heads
196 iso. next to the heads.
198 Real t = (width / staff_space - 5.0); // ugh.
199 Real dy = t > 0 ? max_gap * sqr (t / (1 + t)) : 0.0;
201 Real ypos = Tie::position_f (me) * staff_space/2 + dir * dy;
204 todo: prevent ending / staffline collision.
206 todo: tie / stem collision
209 b = slur_shape(width,h_inf, r_0);
211 b.translate (Offset (left_x, ypos));
215 Avoid colliding of the horizontal part with stafflines.
217 should do me for slurs as well.
220 Array<Real> horizontal (b.solve_derivative (Offset (1,0)));
221 if (horizontal.size ())
224 ugh. Doesnt work for non-horizontal curves.
226 Real y = b.curve_point (horizontal[0])[Y_AXIS];
228 Real ry = rint (y/staff_space) * staff_space;
232 Real clear = staff_space * gh_scm2double (me->get_elt_property ("staffline-clearance"));
234 if (fabs (y) <= Staff_symbol_referencer::staff_radius (me)
235 && fabs (diff) < clear)
237 newy = ry - 0.5 * staff_space * sign (diff) ;
240 Real y0 = b.control_ [0][Y_AXIS];
241 b.control_[2][Y_AXIS] =
242 b.control_[1][Y_AXIS] =
243 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
246 programming_error ("Tie is nowhere horizontal");
250 SCM controls = SCM_EOL;
252 controls = gh_cons ( ly_offset2scm (b.control_[i]), controls);
256 MAKE_SCHEME_CALLBACK(Tie,set_spacing_rods);
259 TODO: set minimum distances for begin/end of line
262 Tie::set_spacing_rods (SCM smob)
264 Score_element*me = unsmob_element (smob);
265 Spanner*sp = dynamic_cast<Spanner*> (me);
268 r.item_l_drul_ [LEFT]=sp->get_bound (LEFT);
269 r.item_l_drul_ [RIGHT]=sp->get_bound (RIGHT);
272 = gh_scm2double (me->get_elt_property ("minimum-length"))
273 * me->paper_l ()->get_var ("staffspace");
275 return SCM_UNSPECIFIED;
278 MAKE_SCHEME_CALLBACK(Tie,brew_molecule);
280 Tie::brew_molecule (SCM smob)
282 Score_element*me = unsmob_element (smob);
284 SCM cp = me->get_elt_property ("control-points");
287 cp = get_control_points (smob);
288 me->set_elt_property ("control-points", cp);
292 gh_scm2double (me->get_elt_property ("thickness"))
293 * me->paper_l ()->get_var ("stafflinethickness");
297 for (SCM s= cp; s != SCM_EOL; s = gh_cdr (s))
299 b.control_[i] = ly_scm2offset (gh_car (s));
303 Molecule a = me->lookup_l ()->slur (b, Directional_element_interface::get (me) * thick, thick);
305 return a.create_scheme ();