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"
22 Tie::set_head (Direction d, Item * head_l)
25 index_set_cell (get_elt_property ("heads"), d, head_l->self_scm_);
27 set_bounds (d, head_l);
28 add_dependency (head_l);
33 set_elt_property ("heads", gh_cons (SCM_EOL, SCM_EOL));
34 dy_f_drul_[LEFT] = dy_f_drul_[RIGHT] = 0.0;
35 dx_f_drul_[LEFT] = dx_f_drul_[RIGHT] = 0.0;
40 Tie::head (Direction d) const
42 SCM c = get_elt_property ("heads");
43 c = index_cell (c, d);
45 return dynamic_cast<Note_head*> (unsmob_element (c));
50 ugh: direction of the Tie is more complicated. See [Ross] p136 and further
53 Tie::get_default_dir () const
55 Real p1 = Staff_symbol_referencer_interface (head (LEFT)).position_f () ;
56 Real p2 = Staff_symbol_referencer_interface (head (RIGHT)).position_f () ;
58 int m = int (p1 + p2);
61 If dir is not determined: inverse of stem: down
62 (see stem::get_default_dir ())
64 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
65 return (m == 0) ? other_dir (neutral_dir) : (m < 0) ? DOWN : UP;
69 Tie::do_add_processing()
71 if (!(head (LEFT) && head (RIGHT)))
72 warning (_ ("lonely tie"));
75 Drul_array<Note_head *> new_head_drul;
76 new_head_drul[LEFT] = head(LEFT);
77 new_head_drul[RIGHT] = head(RIGHT);
80 new_head_drul[d] = head((Direction)-d);
81 } while (flip(&d) != LEFT);
83 index_set_cell (get_elt_property ("heads"), LEFT, new_head_drul[LEFT]->self_scm_ );
84 index_set_cell (get_elt_property ("heads"), RIGHT, new_head_drul[LEFT]->self_scm_ );
89 Tie::do_post_processing()
91 if (!head (LEFT) && !head (RIGHT))
93 programming_error ("Tie without heads.");
94 set_elt_property ("transparent", SCM_BOOL_T);
100 Real staff_space = paper_l ()->get_var ("interline");
101 Real half_staff_space = staff_space / 2;
102 Real x_gap_f = paper_l ()->get_var ("tie_x_gap");
103 Real y_gap_f = paper_l ()->get_var ("tie_y_gap");
106 Slur and tie placement [OSU]
110 * x = inner vertical tangent - d * gap
116 OSU: not different for outer notes, so why all this code?
117 ie, can we drop this, or should it be made switchable.
123 Real head_width_f = head (d)
124 ? head (d)->extent (X_AXIS).length ()
127 side attached to outer (upper or lower) notehead of chord
134 to second tie, middle notehead seems not extremal
136 Getting scared a bit by score-element's comment:
137 // is this a good idea?
139 // FIXME extremal deprecated
140 && (head (d)->get_elt_property ("extremal")
144 dx_f_drul_[d] += head_width_f;
145 dx_f_drul_[d] += -d * x_gap_f;
148 side attached to inner notehead
152 dx_f_drul_[d] += -d * head_width_f;
154 } while (flip (&d) != LEFT);
159 dx_f_drul_[LEFT] = head (LEFT)->extent (X_AXIS).length ();
161 dx_f_drul_[LEFT] = get_broken_left_end_align ();
162 dx_f_drul_[LEFT] += x_gap_f;
163 dx_f_drul_[RIGHT] -= x_gap_f;
168 Slur and tie placement [OSU] -- check this
172 * y = dx < 5ss: horizontal tangent
173 y = dx >= 5ss: y next interline - d * 0.25 ss
175 which probably means that OSU assumes that
183 Real ypos = head (LEFT)
184 ? Staff_symbol_referencer_interface (head (LEFT)).position_f ()
185 : Staff_symbol_referencer_interface (head (RIGHT)).position_f () ;
187 Real y_f = half_staff_space * ypos;
188 int ypos_i = int (ypos);
190 Real dx_f = extent (X_AXIS).length () + dx_f_drul_[RIGHT] - dx_f_drul_[LEFT];
191 Direction dir = directional_element (this).get();
192 if (dx_f < paper_l ()->get_var ("tie_staffspace_length"))
194 if (abs (ypos_i) % 2)
195 y_f += dir * half_staff_space;
196 y_f += dir * y_gap_f;
200 if (! (abs (ypos_i) % 2))
201 y_f += dir * half_staff_space;
202 y_f += dir * half_staff_space;
203 y_f -= dir * y_gap_f;
206 dy_f_drul_[LEFT] = dy_f_drul_[RIGHT] = y_f;
212 Tie::get_rods () const
216 r.item_l_drul_ = spanned_drul_;
217 r.distance_f_ = paper_l ()->get_var ("tie_x_minimum");
226 Tie::do_brew_molecule_p () const
228 Real thick = paper_l ()->get_var ("slur_thickness");
229 Bezier one = get_curve ();
232 SCM d = get_elt_property ("dashed");
234 a = lookup_l ()->dashed_slur (one, thick, gh_scm2int (d));
236 a = lookup_l ()->slur (one, directional_element (this).get () * thick, thick);
238 return new Molecule (a);
244 Tie::get_curve () const
246 Bezier_bow b (get_encompass_offset_arr (), directional_element (this).get ());
248 b.ratio_ = paper_l ()->get_var ("slur_ratio");
249 b.height_limit_ = paper_l ()->get_var ("slur_height_limit");
250 b.rc_factor_ = paper_l ()->get_var ("slur_rc_factor");
253 return b.get_curve ();
265 This function tries to address two issues:
266 * the tangents of the slur should always point inwards
267 in the actual slur, i.e. *after rotating back*.
269 * slurs shouldn't be too high
270 let's try : h <= 1.2 b && h <= 3 staffheight?
272 We could calculate the tangent of the bezier curve from
273 both ends going inward, and clip the slur at the point
274 where the tangent (after rotation) points up (or inward
275 with a certain maximum angle).
277 However, we assume that real clipping is not the best
278 answer. We expect that moving the outer control point up
279 if the slur becomes too high will result in a nicer slur
282 Knowing that the tangent is the line through the first
283 two control points, we'll clip (move the outer control
284 point upwards) too if the tangent points outwards.
288 Bezier_Tie::calc_clipping ()
290 Real clip_height = paper_l_->get_var ("slur_clip_height");
291 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
292 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
294 Real b = curve_.control_[3][X_AXIS] - curve_.control_[0][X_AXIS];
295 Real clip_h = clip_ratio * b <? clip_height;
296 Real begin_h = curve_.control_[1][Y_AXIS] - curve_.control_[0][Y_AXIS];
297 Real end_h = curve_.control_[2][Y_AXIS] - curve_.control_[3][Y_AXIS];
298 Real begin_dy = 0 >? begin_h - clip_h;
299 Real end_dy = 0 >? end_h - clip_h;
302 Real begin_alpha = (curve_.control_[1] - curve_.control_[0]).arg () + dir_ * alpha_;
303 Real end_alpha = pi - (curve_.control_[2] - curve_.control_[3]).arg () - dir_ * alpha_;
305 Real max_alpha = clip_angle / 90 * pi / 2;
306 if ((begin_dy < 0) && (end_dy < 0)
307 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
312 if ((begin_dy > 0) || (end_dy > 0))
314 Real dy = (begin_dy + end_dy) / 4;
316 encompass_[0][Y_AXIS] += dir_ * dy;
317 encompass_.top ()[Y_AXIS] += dir_ * dy;
323 if (begin_alpha >= max_alpha)
324 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
325 if (end_alpha >= max_alpha)
326 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
328 encompass_[0][Y_AXIS] += dir_ * begin_dy;
329 encompass_.top ()[Y_AXIS] += dir_ * end_dy;
331 Offset delta = encompass_.top () - encompass_[0];
332 alpha_ = delta.arg ();
344 Tie::get_encompass_offset_arr () const
346 Array<Offset> offset_arr;
347 offset_arr.push (Offset (dx_f_drul_[LEFT], dy_f_drul_[LEFT]));
348 offset_arr.push (Offset (spanner_length () + dx_f_drul_[RIGHT],