2 tie-column.cc -- implement Tie_column
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include "tie-column.hh"
20 #include "staff-symbol-referencer.hh"
22 #include "paper-column.hh"
24 #include "pointer-group-interface.hh"
26 #include "directional-element-interface.hh"
27 #include "rhythmic-head.hh"
30 Tie_column::add_tie (Grob *me, Grob *tie)
32 if (tie->get_parent (Y_AXIS)
33 && Tie_column::has_interface (tie->get_parent (Y_AXIS)))
36 if (!Pointer_group_interface::count (me, ly_symbol2scm ("ties")))
38 dynamic_cast<Spanner *> (me)->set_bound (LEFT, Tie::head (tie, LEFT));
39 dynamic_cast<Spanner *> (me)->set_bound (RIGHT, Tie::head (tie, RIGHT));
42 tie->set_parent (me, Y_AXIS);
43 Pointer_group_interface::add_grob (me, ly_symbol2scm ("ties"), tie);
44 tie->add_dependency (me);
48 Tie_column::set_directions (Grob *me)
50 if (!to_boolean (me->get_property ("positioning-done")))
52 me->set_property ("positioning-done", SCM_BOOL_T);
58 Tie::compare (Grob *const &s1,
61 return sign (Tie::get_position (s1) - Tie::get_position (s2));
64 MAKE_SCHEME_CALLBACK (Tie_column, after_line_breaking, 1);
66 Tie_column::after_line_breaking (SCM smob)
68 set_directions (unsmob_grob (smob));
69 return SCM_UNSPECIFIED;
73 Extend the spanner over its Tie constituents.
75 MAKE_SCHEME_CALLBACK (Tie_column, before_line_breaking, 1);
77 Tie_column::before_line_breaking (SCM smob)
79 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
80 for (SCM s = me->get_property ("ties"); scm_is_pair (s); s = scm_cdr (s))
82 Spanner *tie = dynamic_cast<Spanner *> (unsmob_grob (scm_car (s)));
86 if (dir * tie->get_bound (dir)->get_column ()->get_rank ()
87 > dir * me->get_bound (dir)->get_column ()->get_rank ())
88 me->set_bound (dir, Tie::head (tie, dir));
90 while (flip (&dir) != LEFT);
92 return SCM_UNSPECIFIED;
98 set_chord_outlines (Drul_array< Array<Skyline_entry> > *skyline_drul,
99 Link_array<Grob> ties,
104 Real staff_space = Staff_symbol_referencer::staff_space (ties[0]);
111 for (int i = 0; i < ties.size (); i++)
113 Spanner *tie = dynamic_cast<Spanner*> (ties[i]);
115 Grob *head = Tie::head (tie, d);
120 stem = unsmob_grob (head->get_object ("stem"));
122 Real p = Tie::get_position (tie);
123 Interval y ((p-1) * 0.5 * staff_space,
124 (p+1) * 0.5 * staff_space);
126 Interval x = head->extent (common, X_AXIS);
127 boxes.push (Box (x, y));
131 (*skyline_drul)[d] = empty_skyline (-d);
133 Spanner *tie = dynamic_cast<Spanner*> (ties[0]);
134 if (tie->get_bound (d)->break_status_dir ())
136 Real x = robust_relative_extent (tie->get_bound (d),
140 (*skyline_drul)[d].elem_ref (0).height_ = x;
143 for (int i = 0; i < boxes.size (); i++)
144 insert_extent_into_skyline (&skyline_drul->elem_ref (d),
145 boxes[i], Y_AXIS, -d);
147 && !Stem::is_invisible (stem))
150 x.add_point (stem->relative_coordinate (common, X_AXIS));
151 x.widen (staff_space / 20); // ugh.
153 y.add_point (Stem::stem_end_position (stem) * staff_space * .5);
155 Direction stemdir = Stem::get_direction (stem);
156 y.add_point (Stem::head_positions (stem)[-stemdir]
159 insert_extent_into_skyline (&skyline_drul->elem_ref (d),
160 Box (x,y), Y_AXIS, -d);
166 Box flag_box = Stem::get_translated_flag (stem).extent_box ();
167 flag_box.translate( Offset (x[RIGHT], X_AXIS));
168 insert_extent_into_skyline (&skyline_drul->elem_ref (d),
177 Direction updowndir = DOWN;
184 Box b = boxes.boundary (updowndir, 0);
186 x[-d] = b[X_AXIS].linear_combination (-d / 2);
187 y[-updowndir] = b[Y_AXIS][updowndir];
188 y[updowndir] = updowndir * infinity_f;
192 insert_extent_into_skyline (&skyline_drul->elem_ref (d),
196 while (flip (&updowndir) != DOWN);
198 for (int i = 0; i < ties.size (); i++)
200 Spanner *tie = dynamic_cast<Spanner*> (ties[i]);
201 Grob *head = Tie::head (tie, d);
205 Grob *dots = unsmob_grob (head->get_object ("dot"));
206 if (dots && d == LEFT)
208 Interval x = dots->extent (common, X_AXIS);
209 Real p = Staff_symbol_referencer::get_position (dots);
212 y *= (staff_space /4);
213 y.translate (p * staff_space * .5);
215 insert_extent_into_skyline (&skyline_drul->elem_ref (d),
216 Box (x,y), Y_AXIS, -d);
221 while (flip (&d) != LEFT);
226 Tie_column::new_directions (Grob *me)
228 extract_grob_set (me, "ties", ro_ties);
229 Link_array<Grob> ties (ro_ties);
233 if (ties.size() == 1)
235 Tie::set_default_control_points (ties[0]);
239 ties.sort (&Tie::compare);
241 Array<Tie_configuration> tie_configs;
242 for (int i = 0; i < ties.size (); i++)
244 Tie_configuration conf;
245 conf.dir_ = get_grob_direction (ties[i]);
246 conf.position_ = Tie::get_position (ties[i]);
247 tie_configs.push (conf);
250 SCM manual_configs = me->get_property ("tie-configuration");
251 bool manual_override = false;
253 for (SCM s = manual_configs;
254 scm_is_pair (s) && k < tie_configs.size(); s = scm_cdr (s))
256 SCM entry = scm_car (s);
257 if (!scm_is_pair (entry))
260 manual_override = true;
261 tie_configs[k].position_ = robust_scm2double (scm_car (entry), tie_configs[k].position_);
262 tie_configs[k].dir_ = Direction (robust_scm2int (scm_cdr (entry), tie_configs[k].dir_));
266 if (!tie_configs[0].dir_)
267 tie_configs[0].dir_ = DOWN;
268 if (!tie_configs.top().dir_)
269 tie_configs.top().dir_ = UP;
274 for (int i = 1; i < tie_configs.size(); i++)
276 if (fabs (tie_configs[i-1].position_ - tie_configs[i].position_) <= 1)
278 if (!tie_configs[i-1].dir_)
279 tie_configs[i-1].dir_ = DOWN;
280 if (!tie_configs[i].dir_)
281 tie_configs[i].dir_ = UP;
285 for (int i = 1; i < tie_configs.size() - 1; i++)
287 if (tie_configs[i].dir_)
290 Direction position_dir = (Direction) sign (tie_configs[i].position_);
294 tie_configs[i].dir_ = position_dir;
298 for (int i = 0; i < ties.size (); i++)
300 common = dynamic_cast<Spanner*> (ties[i])->get_bound (LEFT)->common_refpoint (common, X_AXIS);
301 common = dynamic_cast<Spanner*> (ties[i])->get_bound (RIGHT)->common_refpoint (common, X_AXIS);
304 Drul_array< Array<Skyline_entry> > skylines;
305 set_chord_outlines (&skylines, ties, common);
308 details.init (ties[0]);
311 Let the ties flow out, according to our single-tie formatting.
313 if (!manual_override)
315 Tie::get_configuration (ties[0], common, &tie_configs.elem_ref (0),
319 Tie::get_configuration (ties.top (), common,
320 &tie_configs.elem_ref (tie_configs.size()-1),
327 Calculate final width and shape of the ties.
329 Real staff_space = Staff_symbol_referencer::staff_space (ties[0]);
330 Real gap = robust_scm2double (ties[0]->get_property ("x-gap"), 0.2);
331 for (int i = 0; i < ties.size(); i++)
334 && (i == 0 || i == ties.size () -1))
337 Tie_configuration conf = tie_configs[i];
338 conf = tie_configs[i];
341 bool on_line = Staff_symbol_referencer::on_staffline (ties[0],
342 int (rint (conf.position_)));
344 line_dy = - sign (conf.height (details) - 0.6 * staff_space)
345 * 0.2 * staff_space * conf.dir_;
347 Real y = conf.position_ * staff_space * 0.5
350 = get_skyline_attachment (skylines, y);
351 conf.attachment_x_.intersect (get_skyline_attachment (skylines,
352 y + conf.dir_ * staff_space * .5));
355 conf.delta_y_ += line_dy;
356 conf.attachment_x_.widen (-gap);
358 && Staff_symbol_referencer::staff_radius (ties[0]) * staff_space > y)
359 conf.center_tie_vertically (details);
361 tie_configs[i] = conf;
365 Try to shift small ties into available spaces.
367 if (!manual_override)
369 set<int> positions_taken;
370 for (int i = 0; i < tie_configs.size (); i++)
371 positions_taken.insert (int (rint (tie_configs[i].position_)));
373 for (int i = 0; i < tie_configs.size (); i++)
375 Tie_configuration * conf = &tie_configs.elem_ref (i);
378 on staff line and small enough, translate a little further
380 Real h = conf->height (details);
381 bool next_free = positions_taken.find (int (rint (conf->position_ + conf->dir_)))
382 == positions_taken.end ();
383 bool on_line = Staff_symbol_referencer::on_staffline (ties[0],
384 int (rint (conf->position_ + conf->delta_y_)));
386 if (on_line && h < 0.4 * staff_space)
388 positions_taken.insert (int (rint (conf->position_ + conf->dir_)));
389 conf->delta_y_ += 0.2 * staff_space * conf->dir_;
391 else if (!on_line && h > 0.6 * staff_space)
393 positions_taken.insert (int (rint (conf->position_ + conf->dir_)));
394 conf->delta_y_ += 0.5 * staff_space * conf->dir_;
399 for (int i = 0; i < ties.size(); i++)
401 Tie::set_control_points (ties[i], common, tie_configs[i],
404 set_grob_direction (ties[i], tie_configs[i].dir_);
409 ADD_INTERFACE (Tie_column, "tie-column-interface",
410 "Object that sets directions of multiple ties in a tied chord",