2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
13 #include "paper-def.hh"
15 #include "rhythmic-head.hh"
17 #include "paper-column.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "directional-element-interface.hh"
21 #include "molecule.hh"
22 #include "bezier-bow.hh"
24 #include "note-head.hh"
27 tie: Connect two noteheads.
31 c4 ~ \clef bass ; c4 or
35 do we have non-horizontal ties then?
40 Tie::set_head (Grob
*me
,Direction d
, Grob
* h
)
42 assert (!head (me
,d
));
43 index_set_cell (me
->get_grob_property ("heads"), d
, h
->self_scm ());
45 dynamic_cast<Spanner
*> (me
)->set_bound (d
, h
);
46 me
->add_dependency (h
);
50 Tie::set_interface (Grob
*me
)
52 me
->set_grob_property ("heads", gh_cons (SCM_EOL
, SCM_EOL
));
57 Tie::head (Grob
*me
, Direction d
)
59 SCM c
= me
->get_grob_property ("heads");
60 c
= index_get_cell (c
, d
);
62 return unsmob_grob (c
);
66 Tie::get_position (Grob
*me
)
68 Direction d
= head (me
,LEFT
) ? LEFT
:RIGHT
;
69 return Staff_symbol_referencer::get_position (head (me
,d
));
74 Default: Put the tie oppositie of the stem [Wanske p231]
76 In case of chords: Tie_column takes over
78 The direction of the Tie is more complicated (See [Ross] p136 and
82 Tie::get_default_dir (Grob
*me
)
84 Item
* sl
= head (me
,LEFT
) ? Rhythmic_head::get_stem (head (me
,LEFT
)) :0;
85 Item
* sr
= head (me
,RIGHT
) ? Rhythmic_head::get_stem (head (me
,RIGHT
)) :0;
89 if (Directional_element_interface::get (sl
) == UP
90 && Directional_element_interface::get (sr
) == UP
)
95 Item
*s
= sl
? sl
: sr
;
96 return - Directional_element_interface::get (s
);
104 TODO: we should also use thickness for computing the clearance
105 between head and tie. Very thick ties will now touch the note head.
109 Tie::get_control_points (SCM smob
)
111 Spanner
*me
= unsmob_spanner (smob
);
112 Direction headdir
= CENTER
;
115 else if (head (me
,RIGHT
))
119 programming_error ("Tie without heads.");
121 return SCM_UNSPECIFIED
;
125 if (!Directional_element_interface::get (me
))
126 Directional_element_interface::set (me
, Tie::get_default_dir (me
));
127 Direction dir
= Directional_element_interface::get (me
);
129 Real staff_space
= Staff_symbol_referencer::staff_space (me
);
131 Real x_gap_f
= gh_scm2double (me
->get_grob_property ("x-gap"));
133 Grob
* l
= me
->get_bound (LEFT
);
134 Grob
* r
= me
->get_bound (RIGHT
);
136 Grob
* commonx
= me
->common_refpoint (l
, X_AXIS
);
137 commonx
= me
->common_refpoint (r
, X_AXIS
);
142 the tie has to be long enough to be visible, but should not go
143 through key sigs. In the 1.5 series the pref.matter - note
144 distance is fixed , so this won't be a problem anymore.
148 if (Note_head::has_interface (l
))
153 This correction is due te the shape of the black note head.
155 if (Rhythmic_head::duration_log (l
) == 2)
157 left_x
= l
->extent (l
, X_AXIS
).linear_combination (where
)
161 left_x
= l
->extent (l
, X_AXIS
).linear_combination (lambda
);
165 if (Note_head::has_interface (l
) && Note_head::has_interface (r
))
168 + r
->extent (commonx
,X_AXIS
)[LEFT
]
169 - l
->extent (commonx
, X_AXIS
)[RIGHT
]
174 if (Note_head::has_interface (l
))
175 width
= r
->relative_coordinate (commonx
, X_AXIS
)
176 - l
->extent (commonx
, X_AXIS
)[RIGHT
]
180 - l
->extent (commonx
, X_AXIS
).linear_combination (lambda
)
181 + r
->extent (commonx
, X_AXIS
)[LEFT
]
187 SCM details
= me
->get_grob_property ("details");
189 SCM lim
// groetjes aan de chirurgendochter.
190 = scm_assq (ly_symbol2scm ("height-limit"),details
);
192 Real h_inf
= gh_scm2double (ly_cdr (lim
)) * staff_space
;
193 Real r_0
= gh_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details
)));
195 Bezier b
= slur_shape (width
, h_inf
, r_0
);
198 I think this better, particularly for small ties. It always allows the user to move ties if
199 they seem in the wrong place
201 TODO: what if 2 heads have different size.
205 Real ypos
= Tie::get_position (me
) * staff_space
/2
206 + dir
* gh_scm2double (me
->get_grob_property ("y-offset"));;
209 Make sure we don't start on a dots
211 if (Note_head::has_interface (l
) && Rhythmic_head::get_dots (l
))
213 Grob
* dots
= Rhythmic_head::get_dots (l
);
214 if(fabs (staff_space
* Staff_symbol_referencer::get_position (dots
) /2
223 todo: prevent ending / staffline collision.
225 todo: tie / stem collision
228 b
= slur_shape (width
,h_inf
, r_0
);
230 b
.translate (Offset (left_x
, ypos
));
234 Avoid colliding of the horizontal part with stafflines.
237 TODO: redo this, heuristic is half-baken, and ties often look ugly
240 TODO: doesn't work when on staff with even number of lines.
242 Array
<Real
> horizontal (b
.solve_derivative (Offset (1,0)));
243 if (horizontal
.size ())
246 ugh. Doesnt work for non-horizontal curves.
248 Real y
= b
.curve_point (horizontal
[0])[Y_AXIS
];
250 Real ry
= rint (y
/staff_space
) * staff_space
;
254 Real clear
= staff_space
* gh_scm2double (me
->get_grob_property ("staffline-clearance"));
257 Staff_symbol_referencer::staff_radius (me
) * staff_space
+ clear
258 && fabs (diff
) < clear
)
260 Real y1
= ry
+ clear
;
261 Real y2
= ry
- clear
;
264 ugh, we shove the 0.5 out of our sleeves.
266 Any way. This test is to make sure that staffline
267 collision avoidance does not result in completely flat
270 if (fabs (y1
- ypos
) < 0.5)
272 else if (fabs (y2
- ypos
) < 0.5)
275 newy
= (fabs (y1
- y
) < fabs (y2
- y
)) ? y1
: y2
;
277 // newy = ry - 0.5 * staff_space * sign (diff) ;
280 we don't want horizontal ties
282 if (fabs (newy
- b
.control_
[0][Y_AXIS
]) < 1e-2)
284 newy
= newy
+ dir
* staff_space
;
288 Real y0
= b
.control_
[0][Y_AXIS
];
289 b
.control_
[2][Y_AXIS
] =
290 b
.control_
[1][Y_AXIS
] =
291 (b
.control_
[1][Y_AXIS
] - y0
) * ((newy
- y0
) / (y
- y0
)) + y0
;
294 programming_error ("Tie is nowhere horizontal");
298 SCM controls
= SCM_EOL
;
300 controls
= gh_cons (ly_offset2scm (b
.control_
[i
]), controls
);
305 MAKE_SCHEME_CALLBACK (Tie
,brew_molecule
,1);
307 Tie::brew_molecule (SCM smob
)
309 Grob
*me
= unsmob_grob (smob
);
311 SCM cp
= me
->get_grob_property ("control-points");
314 cp
= get_control_points (smob
);
315 me
->set_grob_property ("control-points", cp
);
319 gh_scm2double (me
->get_grob_property ("thickness"))
320 * me
->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
324 for (SCM s
= cp
; s
!= SCM_EOL
; s
= ly_cdr (s
))
326 b
.control_
[i
] = ly_scm2offset (ly_car (s
));
330 Molecule a
= Lookup::slur (b
, Directional_element_interface::get (me
) * thick
, thick
);
332 return a
.smobbed_copy ();
337 ADD_INTERFACE (Tie
,"tie-interface",
338 "A tie connecting two noteheads.\n"
339 "direction = Forced direction for all ties",
340 "y-offset staffline-clearance control-points heads details thickness x-gap direction minimum-length");