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
);
105 Tie::get_control_points (SCM smob
)
107 Spanner
*me
= unsmob_spanner (smob
);
108 Direction headdir
= CENTER
;
111 else if (head (me
,RIGHT
))
115 programming_error ("Tie without heads.");
117 return SCM_UNSPECIFIED
;
121 if (!Directional_element_interface::get (me
))
122 Directional_element_interface::set (me
, Tie::get_default_dir (me
));
123 Direction dir
= Directional_element_interface::get (me
);
125 Real staff_space
= Staff_symbol_referencer::staff_space (me
);
127 Real x_gap_f
= gh_scm2double (me
->get_grob_property ("x-gap"));
129 Grob
* l
= me
->get_bound (LEFT
);
130 Grob
* r
= me
->get_bound (RIGHT
);
132 Grob
* commonx
= me
->common_refpoint (l
, X_AXIS
);
133 commonx
= me
->common_refpoint (r
, X_AXIS
);
138 the tie has to be long enough to be visible, but should not go
139 through key sigs. In the 1.5 series the pref.matter - note
140 distance is fixed , so this won't be a problem anymore.
144 if (Note_head::has_interface (l
))
149 This correction is due te the shape of the black note head.
151 if (Rhythmic_head::duration_log (l
) == 2)
153 left_x
= l
->extent (l
, X_AXIS
).linear_combination (where
)
157 left_x
= l
->extent (l
, X_AXIS
).linear_combination (lambda
);
161 if (Note_head::has_interface (l
) && Note_head::has_interface (r
))
164 + r
->extent (commonx
,X_AXIS
)[LEFT
]
165 - l
->extent (commonx
, X_AXIS
)[RIGHT
]
170 if (Note_head::has_interface (l
))
171 width
= r
->relative_coordinate (commonx
, X_AXIS
)
172 - l
->extent (commonx
, X_AXIS
)[RIGHT
]
176 - l
->extent (commonx
, X_AXIS
).linear_combination (lambda
)
177 + r
->extent (commonx
, X_AXIS
)[LEFT
]
183 SCM details
= me
->get_grob_property ("details");
185 SCM lim
// groetjes aan de chirurgendochter.
186 = scm_assq (ly_symbol2scm ("height-limit"),details
);
188 Real h_inf
= gh_scm2double (ly_cdr (lim
)) * staff_space
;
189 Real r_0
= gh_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details
)));
191 Bezier b
= slur_shape (width
, h_inf
, r_0
);
194 I think this better, particularly for small ties. It always allows the user to move ties if
195 they seem in the wrong place
197 TODO: what if 2 heads have different size.
201 Real ypos
= Tie::get_position (me
) * staff_space
/2
202 + dir
* gh_scm2double (me
->get_grob_property ("y-offset"));;
205 Make sure we don't start on a dots
207 if (Note_head::has_interface (l
) && Rhythmic_head::get_dots (l
))
209 Grob
* dots
= Rhythmic_head::get_dots (l
);
210 if(fabs (staff_space
* Staff_symbol_referencer::get_position (dots
) /2
219 todo: prevent ending / staffline collision.
221 todo: tie / stem collision
224 b
= slur_shape (width
,h_inf
, r_0
);
226 b
.translate (Offset (left_x
, ypos
));
230 Avoid colliding of the horizontal part with stafflines.
233 TODO: redo this, heuristic is half-baken, and ties often look ugly
236 TODO: doesn't work when on staff with even number of lines.
238 Array
<Real
> horizontal (b
.solve_derivative (Offset (1,0)));
239 if (horizontal
.size ())
242 ugh. Doesnt work for non-horizontal curves.
244 Real y
= b
.curve_point (horizontal
[0])[Y_AXIS
];
246 Real ry
= rint (y
/staff_space
) * staff_space
;
250 Real clear
= staff_space
* gh_scm2double (me
->get_grob_property ("staffline-clearance"));
253 Staff_symbol_referencer::staff_radius (me
) * staff_space
+ clear
254 && fabs (diff
) < clear
)
256 Real y1
= ry
+ clear
;
257 Real y2
= ry
- clear
;
260 ugh, we shove the 0.5 out of our sleeves.
262 Any way. This test is to make sure that staffline
263 collision avoidance does not result in completely flat
266 if (fabs (y1
- ypos
) < 0.5)
268 else if (fabs (y2
- ypos
) < 0.5)
271 newy
= (fabs (y1
- y
) < fabs (y2
- y
)) ? y1
: y2
;
273 // newy = ry - 0.5 * staff_space * sign (diff) ;
276 we don't want horizontal ties
278 if (fabs (newy
- b
.control_
[0][Y_AXIS
]) < 1e-2)
280 newy
= newy
+ dir
* staff_space
;
284 Real y0
= b
.control_
[0][Y_AXIS
];
285 b
.control_
[2][Y_AXIS
] =
286 b
.control_
[1][Y_AXIS
] =
287 (b
.control_
[1][Y_AXIS
] - y0
) * ((newy
- y0
) / (y
- y0
)) + y0
;
290 programming_error ("Tie is nowhere horizontal");
294 SCM controls
= SCM_EOL
;
296 controls
= gh_cons (ly_offset2scm (b
.control_
[i
]), controls
);
301 MAKE_SCHEME_CALLBACK (Tie
,brew_molecule
,1);
303 Tie::brew_molecule (SCM smob
)
305 Grob
*me
= unsmob_grob (smob
);
307 SCM cp
= me
->get_grob_property ("control-points");
310 cp
= get_control_points (smob
);
311 me
->set_grob_property ("control-points", cp
);
315 gh_scm2double (me
->get_grob_property ("thickness"))
316 * me
->get_paper ()->get_var ("linethickness");
320 for (SCM s
= cp
; s
!= SCM_EOL
; s
= ly_cdr (s
))
322 b
.control_
[i
] = ly_scm2offset (ly_car (s
));
326 Molecule a
= Lookup::slur (b
, Directional_element_interface::get (me
) * thick
, thick
);
328 return a
.smobbed_copy ();
333 ADD_INTERFACE (Tie
,"tie-interface",
334 "A tie connecting two noteheads.
335 direction = Forced direction for all ties",
336 "y-offset staffline-clearance control-points heads details thickness x-gap direction minimum-length");