2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
13 #include "output-def.hh"
15 #include "rhythmic-head.hh"
17 #include "paper-column.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "directional-element-interface.hh"
22 #include "bezier-bow.hh"
24 #include "note-head.hh"
25 #include "tie-column.hh"
28 tie: Connect two noteheads.
32 c4 ~ \clef bass ; c4 or
36 do we have non-horizontal ties then?
41 Tie::set_head (Grob
*me
,Direction d
, Grob
* h
)
43 assert (!head (me
,d
));
44 index_set_cell (me
->get_property ("head-pair"), d
, h
->self_scm ());
46 dynamic_cast<Spanner
*> (me
)->set_bound (d
, h
);
47 me
->add_dependency (h
);
51 Tie::set_interface (Grob
*me
)
53 me
->set_property ("head-pair", scm_cons (SCM_EOL
, SCM_EOL
));
58 Tie::head (Grob
*me
, Direction d
)
60 SCM c
= me
->get_property ("head-pair");
63 return unsmob_grob (index_get_cell (c
, d
));
69 Tie::get_position (Grob
*me
)
71 Direction d
= head (me
,LEFT
) ? LEFT
:RIGHT
;
72 return Staff_symbol_referencer::get_position (head (me
,d
));
77 Default: Put the tie oppositie of the stem [Wanske p231]
79 In case of chords: Tie_column takes over
81 The direction of the Tie is more complicated (See [Ross] p136 and
84 (what about linebreaks? )
88 Tie::get_default_dir (Grob
*me
)
90 Item
* sl
= head (me
,LEFT
) ? Rhythmic_head::get_stem (head (me
,LEFT
)) :0;
91 Item
* sr
= head (me
,RIGHT
) ? Rhythmic_head::get_stem (head (me
,RIGHT
)) :0;
94 if (get_grob_direction (sl
) == UP
95 && get_grob_direction (sr
) == UP
)
100 Item
*s
= sl
? sl
: sr
;
101 return - get_grob_direction (s
);
110 Tie::set_direction (Grob
*me
)
112 if (!get_grob_direction (me
))
114 if (Tie_column::has_interface (me
->get_parent (Y_AXIS
)))
115 Tie_column::set_directions (me
->get_parent (Y_AXIS
));
117 set_grob_direction (me
, Tie::get_default_dir (me
));
122 TODO: we should also use thickness for computing the clearance
123 between head and tie. Very thick ties will now touch the note head.
127 Tie::get_control_points (SCM smob
)
129 Spanner
*me
= unsmob_spanner (smob
);
130 Direction headdir
= CENTER
;
133 else if (head (me
,RIGHT
))
137 programming_error ("Tie without heads.");
144 Direction dir
= get_grob_direction (me
);
146 Real staff_space
= Staff_symbol_referencer::staff_space (me
);
148 Real x_gap_f
= robust_scm2double (me
->get_property ("x-gap"), 0);
150 Grob
* l
= me
->get_bound (LEFT
);
151 Grob
* r
= me
->get_bound (RIGHT
);
153 Grob
* commonx
= me
->common_refpoint (l
, X_AXIS
);
154 commonx
= me
->common_refpoint (r
, X_AXIS
);
159 the tie has to be long enough to be visible, but should not go
160 through key sigs. In the 1.5 series the pref.matter - note
161 distance is fixed , so this won't be a problem anymore.
165 if (Note_head::has_interface (l
))
170 This correction is due te the shape of the black note head.
172 if (Rhythmic_head::duration_log (l
) == 2)
174 left_x
= l
->extent (l
, X_AXIS
).linear_combination (where
)
178 left_x
= l
->extent (l
, X_AXIS
).linear_combination (lambda
);
182 if (Note_head::has_interface (l
) && Note_head::has_interface (r
))
185 + r
->extent (commonx
,X_AXIS
)[LEFT
]
186 - l
->extent (commonx
, X_AXIS
)[RIGHT
]
191 if (Note_head::has_interface (l
))
192 width
= r
->relative_coordinate (commonx
, X_AXIS
)
193 - l
->extent (commonx
, X_AXIS
)[RIGHT
]
197 - l
->extent (commonx
, X_AXIS
).linear_combination (lambda
)
198 + r
->extent (commonx
, X_AXIS
)[LEFT
]
204 SCM details
= me
->get_property ("details");
206 SCM lim
// groetjes aan de chirurgendochter.
207 = scm_assq (ly_symbol2scm ("height-limit"),details
);
209 Real h_inf
= ly_scm2double (ly_cdr (lim
)) * staff_space
;
210 Real r_0
= ly_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details
)));
212 Bezier b
= slur_shape (width
, h_inf
, r_0
);
215 I think this better, particularly for small ties. It always allows the user to move ties if
216 they seem in the wrong place
218 TODO: what if 2 heads have different size.
222 Real ypos
= Tie::get_position (me
) * staff_space
/2
223 + dir
* ly_scm2double (me
->get_property ("y-offset"));;
226 Make sure we don't start on a dots
228 if (Note_head::has_interface (l
) && Rhythmic_head::get_dots (l
))
230 Grob
* dots
= Rhythmic_head::get_dots (l
);
231 if (fabs (staff_space
* Staff_symbol_referencer::get_position (dots
) /2
240 todo: prevent ending / staffline collision.
242 todo: tie / stem collision
245 b
= slur_shape (width
,h_inf
, r_0
);
247 b
.translate (Offset (left_x
, ypos
));
251 Avoid colliding of the horizontal part with stafflines.
254 TODO: redo this, heuristic is half-baken, and ties often look ugly
257 TODO: doesn't work when on staff with even number of lines.
259 Array
<Real
> horizontal (b
.solve_derivative (Offset (1,0)));
260 if (horizontal
.size ())
263 ugh. Doesnt work for non-horizontal curves.
265 Real y
= b
.curve_point (horizontal
[0])[Y_AXIS
];
267 Real ry
= rint (y
/staff_space
) * staff_space
;
271 Real clear
= staff_space
* ly_scm2double (me
->get_property ("staffline-clearance"));
274 Staff_symbol_referencer::staff_radius (me
) * staff_space
+ clear
275 && fabs (diff
) < clear
)
277 Real y1
= ry
+ clear
;
278 Real y2
= ry
- clear
;
281 ugh, we shove the 0.5 out of our sleeves.
283 Any way. This test is to make sure that staffline
284 collision avoidance does not result in completely flat
287 if (fabs (y1
- ypos
) < 0.5)
289 else if (fabs (y2
- ypos
) < 0.5)
292 newy
= (fabs (y1
- y
) < fabs (y2
- y
)) ? y1
: y2
;
294 // newy = ry - 0.5 * staff_space * sign (diff) ;
297 we don't want horizontal ties
299 if (fabs (newy
- b
.control_
[0][Y_AXIS
]) < 1e-2)
301 newy
= newy
+ dir
* staff_space
;
305 Real y0
= b
.control_
[0][Y_AXIS
];
306 b
.control_
[2][Y_AXIS
] =
307 b
.control_
[1][Y_AXIS
] =
308 (b
.control_
[1][Y_AXIS
] - y0
) * ((newy
- y0
) / (y
- y0
)) + y0
;
311 programming_error ("Tie is nowhere horizontal");
315 SCM controls
= SCM_EOL
;
317 controls
= scm_cons (ly_offset2scm (b
.control_
[i
]), controls
);
322 MAKE_SCHEME_CALLBACK (Tie
,print
,1);
324 Tie::print (SCM smob
)
326 Grob
*me
= unsmob_grob (smob
);
328 SCM cp
= me
->get_property ("control-points");
329 if (!ly_c_pair_p (cp
)) // list is more accurate
331 cp
= get_control_points (smob
);
332 me
->set_property ("control-points", cp
);
335 if (!ly_c_pair_p (cp
))
336 return Stencil ().smobbed_copy ();
339 = Staff_symbol_referencer::line_thickness (me
)
340 * robust_scm2double (me
->get_property ("thickness"), 1);
344 for (SCM s
= cp
; s
!= SCM_EOL
; s
= ly_cdr (s
))
346 b
.control_
[i
] = ly_scm2offset (ly_car (s
));
350 Stencil a
= Lookup::slur (b
, get_grob_direction (me
) * thick
, thick
);
352 return a
.smobbed_copy ();
357 ADD_INTERFACE (Tie
,"tie-interface",
358 "A tie connecting two noteheads.\n"
360 "y-offset staffline-clearance control-points head-pair details thickness x-gap direction minimum-length");