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>
10 #include "paper-def.hh"
12 #include "note-head.hh"
14 #include "paper-column.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "directional-element-interface.hh"
18 #include "molecule.hh"
19 #include "bezier-bow.hh"
23 Tie::set_head (Direction d, Item * head_l)
26 index_set_cell (get_elt_property ("heads"), d, head_l->self_scm_);
28 set_bounds (d, head_l);
29 add_dependency (head_l);
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;
41 Tie::head (Direction d) const
43 SCM c = get_elt_property ("heads");
44 c = index_cell (c, d);
46 return dynamic_cast<Note_head*> (unsmob_element (c));
51 ugh: direction of the Tie is more complicated. See [Ross] p136 and further
54 Tie::get_default_dir () const
56 Stem * sl = head(LEFT) ? head (LEFT)->stem_l () :0;
57 Stem * sr = head(RIGHT) ? head (RIGHT)->stem_l () :0;
59 if (sl && directional_element (sl).get () == UP
60 && sr && directional_element (sr).get () == UP)
66 Real p1 = Staff_symbol_referencer_interface (head (LEFT)).position_f () ;
67 Real p2 = Staff_symbol_referencer_interface (head (RIGHT)).position_f () ;
69 int m = int (p1 + p2);
72 If dir is not determined: inverse of stem: down
73 (see stem::get_default_dir ())
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;
81 Tie::do_add_processing()
83 if (!(head (LEFT) && head (RIGHT)))
84 warning (_ ("lonely tie"));
87 Drul_array<Note_head *> new_head_drul;
88 new_head_drul[LEFT] = head(LEFT);
89 new_head_drul[RIGHT] = head(RIGHT);
92 new_head_drul[d] = head((Direction)-d);
93 } while (flip(&d) != LEFT);
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_ );
101 Tie::do_post_processing()
103 if (!head (LEFT) && !head (RIGHT))
105 programming_error ("Tie without heads.");
106 set_elt_property ("transparent", SCM_BOOL_T);
112 if (!directional_element (this).get ())
113 directional_element (this).set (get_default_dir ());
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");
121 Slur and tie placement [OSU]
125 * x = inner vertical tangent - d * gap
131 OSU: not different for outer notes, so why all this code?
132 ie, can we drop this, or should it be made switchable.
138 Real head_width_f = head (d)
139 ? head (d)->extent (X_AXIS).length ()
142 side attached to outer (upper or lower) notehead of chord
149 to second tie, middle notehead seems not extremal
151 Getting scared a bit by score-element's comment:
152 // is this a good idea?
154 // FIXME extremal deprecated
155 && (head (d)->get_elt_property ("extremal")
159 dx_f_drul_[d] += head_width_f;
160 dx_f_drul_[d] += -d * x_gap_f;
163 side attached to inner notehead
167 dx_f_drul_[d] += -d * head_width_f;
169 } while (flip (&d) != LEFT);
174 dx_f_drul_[LEFT] = head (LEFT)->extent (X_AXIS).length ();
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;
183 Slur and tie placement [OSU] -- check this
187 * y = dx < 5ss: horizontal tangent
188 y = dx >= 5ss: y next interline - d * 0.25 ss
190 which probably means that OSU assumes that
198 Real ypos = head (LEFT)
199 ? Staff_symbol_referencer_interface (head (LEFT)).position_f ()
200 : Staff_symbol_referencer_interface (head (RIGHT)).position_f () ;
202 Real y_f = half_staff_space * ypos;
203 int ypos_i = int (ypos);
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"))
209 if (abs (ypos_i) % 2)
210 y_f += dir * half_staff_space;
211 y_f += dir * y_gap_f;
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;
221 dy_f_drul_[LEFT] = dy_f_drul_[RIGHT] = y_f;
227 Tie::get_rods () const
231 r.item_l_drul_ = spanned_drul_;
232 r.distance_f_ = paper_l ()->get_var ("tie_x_minimum");
241 Tie::do_brew_molecule_p () const
243 Real thick = paper_l ()->get_var ("tie_thickness");
244 Bezier one = get_curve ();
247 SCM d = get_elt_property ("dashed");
249 a = lookup_l ()->dashed_slur (one, thick, gh_scm2int (d));
251 a = lookup_l ()->slur (one, directional_element (this).get () * thick, thick);
253 return new Molecule (a);
259 Tie::get_curve () const
261 Bezier_bow b (get_encompass_offset_arr (), directional_element (this).get ());
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");
268 return b.get_curve ();
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*.
284 * slurs shouldn't be too high
285 let's try : h <= 1.2 b && h <= 3 staffheight?
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).
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
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.
303 Bezier_Tie::calc_clipping ()
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");
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;
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_;
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))
327 if ((begin_dy > 0) || (end_dy > 0))
329 Real dy = (begin_dy + end_dy) / 4;
331 encompass_[0][Y_AXIS] += dir_ * dy;
332 encompass_.top ()[Y_AXIS] += dir_ * dy;
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;
343 encompass_[0][Y_AXIS] += dir_ * begin_dy;
344 encompass_.top ()[Y_AXIS] += dir_ * end_dy;
346 Offset delta = encompass_.top () - encompass_[0];
347 alpha_ = delta.arg ();
359 Tie::get_encompass_offset_arr () const
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],