2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
13 #include "directional-element-interface.hh"
14 #include "font-interface.hh"
15 #include "grob-array.hh"
17 #include "note-head.hh"
18 #include "output-def.hh"
19 #include "paper-column.hh"
20 #include "pointer-group-interface.hh"
21 #include "rhythmic-head.hh"
23 #include "staff-symbol-referencer.hh"
25 #include "text-interface.hh"
26 #include "tie-column.hh"
27 #include "tie-configuration.hh"
28 #include "tie-formatting-problem.hh"
30 #include "semi-tie-column.hh"
34 Tie::less (Grob
*const &s1
, Grob
*const &s2
)
36 return Tie::get_position (s1
) < Tie::get_position (s2
);
40 Tie::set_head (Grob
*me
, Direction d
, Grob
*h
)
42 dynamic_cast<Spanner
*> (me
)->set_bound (d
, h
);
46 Tie::head (Grob
*me
, Direction d
)
48 if (is_direction (me
->get_property ("head-direction")))
50 Direction hd
= to_dir (me
->get_property ("head-direction"));
53 ? unsmob_grob (me
->get_object ("note-head"))
57 Item
*it
= dynamic_cast<Spanner
*> (me
)->get_bound (d
);
58 if (Note_head::has_interface (it
))
65 Tie::get_column_rank (Grob
*me
, Direction d
)
67 Spanner
*span
= dynamic_cast<Spanner
*> (me
);
68 Grob
*h
= head (me
, d
);
70 h
= span
->get_bound (d
);
72 Grob
*col
= dynamic_cast<Item
*> (h
)->get_column ();
73 return Paper_column::get_rank (col
);
77 Tie::get_position (Grob
*me
)
82 Grob
*h
= head (me
, d
);
84 return (int) rint (Staff_symbol_referencer::get_position (h
));
86 while (flip (&d
) != LEFT
);
90 TODO: this is theoretically possible for ties across more than 2
91 systems.. We should look at the first broken copy.
94 programming_error ("Tie without heads. Suicide");
100 Default: Put the tie oppositie of the stem [Wanske p231]
102 In case of chords: Tie_column takes over
104 The direction of the Tie is more complicated (See [Ross] p136 and
107 (what about linebreaks? )
110 Tie::get_default_dir (Grob
*me
)
112 Drul_array
<Grob
*> stems
;
116 Grob
*one_head
= head (me
, d
);
117 if (!one_head
&& dynamic_cast<Spanner
*> (me
))
118 one_head
= Tie::head (dynamic_cast<Spanner
*> (me
)->broken_neighbor (d
), d
);
120 Grob
*stem
= one_head
? Rhythmic_head::get_stem (one_head
) : 0;
122 stem
= Stem::is_invisible (stem
) ? 0 : stem
;
126 while (flip (&d
)!= LEFT
);
128 if (stems
[LEFT
] && stems
[RIGHT
])
130 if (get_grob_direction (stems
[LEFT
]) == UP
131 && get_grob_direction (stems
[RIGHT
]) == UP
)
134 else if (stems
[LEFT
] || stems
[RIGHT
])
136 Grob
*s
= stems
[LEFT
] ? stems
[LEFT
] : stems
[RIGHT
];
137 return -get_grob_direction (s
);
139 else if (int p
= get_position (me
))
140 return Direction (sign (p
));
142 return to_dir (me
->get_property("neutral-direction"));
146 MAKE_SCHEME_CALLBACK (Tie
, calc_direction
, 1);
148 Tie::calc_direction (SCM smob
)
150 Grob
*me
= unsmob_grob (smob
);
151 Grob
*yparent
= me
->get_parent (Y_AXIS
);
152 if ((Tie_column::has_interface (yparent
)
153 || Semi_tie_column::has_interface (yparent
))
154 && unsmob_grob_array (yparent
->get_object ("ties"))
155 // && unsmob_grob_array (yparent->get_object ("ties"))->size () > 1
158 /* trigger positioning. */
159 (void) yparent
->get_property ("positioning-done");
161 return me
->get_property_data ("direction");
164 return scm_from_int (Tie::get_default_dir (me
));
169 Tie::get_default_control_points (Grob
*me_grob
)
171 Spanner
*me
= dynamic_cast<Spanner
*> (me_grob
);
173 common
= me
->get_bound (LEFT
)->common_refpoint (common
, X_AXIS
);
174 common
= me
->get_bound (RIGHT
)->common_refpoint (common
, X_AXIS
);
176 Tie_formatting_problem problem
;
177 problem
.from_tie (me
);
179 Tie_specification spec
= problem
.get_tie_specification (0);
184 Ties_configuration conf
185 = problem
.generate_optimal_configuration ();
187 return get_control_points (me
, problem
.common_x_refpoint (),
188 conf
[0], problem
.details_
);
192 Tie::get_control_points (Grob
*me
,
194 Tie_configuration
const &conf
,
195 Tie_details
const &details
198 Bezier b
= conf
.get_transformed_bezier (details
);
199 b
.translate (Offset (- me
->relative_coordinate (common
, X_AXIS
), 0));
201 SCM controls
= SCM_EOL
;
202 for (int i
= 4; i
--;)
204 if (!b
.control_
[i
].is_sane ())
205 programming_error ("Insane offset");
206 controls
= scm_cons (ly_offset2scm (b
.control_
[i
]), controls
);
211 MAKE_SCHEME_CALLBACK (Tie
, calc_control_points
, 1);
213 Tie::calc_control_points (SCM smob
)
215 Grob
*me
= unsmob_grob (smob
);
217 Grob
*yparent
= me
->get_parent (Y_AXIS
);
218 if ((Tie_column::has_interface (yparent
)
219 || Semi_tie_column::has_interface (yparent
))
220 && unsmob_grob_array (yparent
->get_object ("ties")))
222 extract_grob_set (yparent
, "ties", ties
);
223 if (me
->original() && ties
.size() == 1
224 && !to_dir (me
->get_property_data ("direction")))
226 assert (ties
[0] == me
);
227 set_grob_direction (me
, Tie::get_default_dir (me
));
230 /* trigger positioning. */
231 (void) yparent
->get_property ("positioning-done");
234 SCM cp
= me
->get_property_data ("control-points");
235 if (!scm_is_pair (cp
))
237 cp
= get_default_control_points (me
);
244 TODO: merge with Slur::print.
246 MAKE_SCHEME_CALLBACK (Tie
, print
, 1);
248 Tie::print (SCM smob
)
250 Grob
*me
= unsmob_grob (smob
);
252 SCM cp
= me
->get_property ("control-points");
254 Real staff_thick
= Staff_symbol_referencer::line_thickness (me
);
255 Real base_thick
= staff_thick
* robust_scm2double (me
->get_property ("thickness"), 1);
256 Real line_thick
= staff_thick
* robust_scm2double (me
->get_property ("line-thickness"), 1);
260 for (SCM s
= cp
; scm_is_pair (s
); s
= scm_cdr (s
))
262 b
.control_
[i
] = ly_scm2offset (scm_car (s
));
268 SCM p
= me
->get_property ("dash-period");
269 SCM f
= me
->get_property ("dash-fraction");
270 if (scm_is_number (p
) && scm_is_number (f
))
271 a
= Lookup::dashed_slur (b
,
273 robust_scm2double (p
, 1.0),
274 robust_scm2double (f
, 0));
277 get_grob_direction (me
) * base_thick
,
280 #if DEBUG_TIE_SCORING
281 SCM annotation
= me
->get_property ("annotation");
282 if (!scm_is_string (annotation
))
284 SCM debug
= me
->layout ()->lookup_variable (ly_symbol2scm ("debug-tie-scoring"));
285 if (to_boolean (debug
))
286 annotation
= me
->get_property ("quant-score");
288 if (scm_is_string (annotation
))
291 SCM properties
= Font_interface::text_font_alist_chain (me
);
293 Stencil tm
= *unsmob_stencil (Text_interface::interpret_markup
294 (me
->layout ()->self_scm (), properties
,
296 tm
.translate (Offset (b
.control_
[3][X_AXIS
] + 0.5,
297 b
.control_
[0][Y_AXIS
] * 2));
298 tm
= tm
.in_color (1, 0, 0);
301 It would be nice if we could put this in a different layer,
302 but alas, this must be done with a Tie override.
308 return a
.smobbed_copy ();
312 "A horizontal curve connecting two noteheads.",
316 "avoid-slur " // UGH.