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"
15 #include "note-head.hh"
18 #include "staff-symbol-referencer.hh"
20 #include "paper-column.hh"
22 #include "pointer-group-interface.hh"
24 #include "directional-element-interface.hh"
25 #include "rhythmic-head.hh"
28 Tie_column::add_tie (Grob *me, Grob *tie)
30 if (tie->get_parent (Y_AXIS)
31 && Tie_column::has_interface (tie->get_parent (Y_AXIS)))
34 if (!Pointer_group_interface::count (me, ly_symbol2scm ("ties")))
36 dynamic_cast<Spanner *> (me)->set_bound (LEFT, Tie::head (tie, LEFT));
37 dynamic_cast<Spanner *> (me)->set_bound (RIGHT, Tie::head (tie, RIGHT));
40 tie->set_parent (me, Y_AXIS);
41 Pointer_group_interface::add_grob (me, ly_symbol2scm ("ties"), tie);
42 tie->add_dependency (me);
46 Tie_column::set_directions (Grob *me)
48 if (!to_boolean (me->get_property ("positioning-done")))
50 me->set_property ("positioning-done", SCM_BOOL_T);
56 Tie::compare (Grob *const &s1,
59 return sign (Tie::get_position (s1) - Tie::get_position (s2));
62 MAKE_SCHEME_CALLBACK (Tie_column, after_line_breaking, 1);
64 Tie_column::after_line_breaking (SCM smob)
66 set_directions (unsmob_grob (smob));
67 return SCM_UNSPECIFIED;
71 Extend the spanner over its Tie constituents.
73 MAKE_SCHEME_CALLBACK (Tie_column, before_line_breaking, 1);
75 Tie_column::before_line_breaking (SCM smob)
77 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
78 for (SCM s = me->get_property ("ties"); scm_is_pair (s); s = scm_cdr (s))
80 Spanner *tie = dynamic_cast<Spanner *> (unsmob_grob (scm_car (s)));
84 if (dir * tie->get_bound (dir)->get_column ()->get_rank ()
85 > dir * me->get_bound (dir)->get_column ()->get_rank ())
86 me->set_bound (dir, Tie::head (tie, dir));
88 while (flip (&dir) != LEFT);
90 return SCM_UNSPECIFIED;
95 set_chord_outline (Array<Skyline_entry> *skyline,
96 Link_array<Item> bounds,
100 Real staff_space = Staff_symbol_referencer::staff_space (bounds[0]);
106 for (int i = 0; i < bounds.size (); i++)
108 Grob *head = bounds[i];
109 if (!Note_head::has_interface (head))
113 stem = unsmob_grob (head->get_object ("stem"));
115 Real p = Staff_symbol_referencer::get_position (head);
116 Interval y ((p-1) * 0.5 * staff_space,
117 (p+1) * 0.5 * staff_space);
119 Interval x = head->extent (common, X_AXIS);
120 boxes.push (Box (x, y));
124 (*skyline) = empty_skyline (-d);
126 if (bounds[0]->break_status_dir ())
128 Real x = robust_relative_extent (bounds[0], common, X_AXIS)[-d];
129 skyline->elem_ref (0).height_ = x;
132 for (int i = 0; i < boxes.size (); i++)
133 insert_extent_into_skyline (skyline,
134 boxes[i], Y_AXIS, -d);
136 && !Stem::is_invisible (stem))
139 x.add_point (stem->relative_coordinate (common, X_AXIS));
140 x.widen (staff_space / 20); // ugh.
142 y.add_point (Stem::stem_end_position (stem) * staff_space * .5);
144 Direction stemdir = Stem::get_direction (stem);
145 y.add_point (Stem::head_positions (stem)[-stemdir]
148 insert_extent_into_skyline (skyline, Box (x,y), Y_AXIS, -d);
154 Box flag_box = Stem::get_translated_flag (stem).extent_box ();
155 flag_box.translate( Offset (x[RIGHT], X_AXIS));
156 insert_extent_into_skyline (skyline, flag_box,
161 Direction updowndir = DOWN;
168 Box b = boxes.boundary (updowndir, 0);
170 x[-d] = b[X_AXIS].linear_combination (-d / 2);
171 y[-updowndir] = b[Y_AXIS][updowndir];
172 y[updowndir] = updowndir * infinity_f;
176 insert_extent_into_skyline (skyline,
180 while (flip (&updowndir) != DOWN);
182 for (int i = 0; i < bounds.size (); i++)
184 if (!Note_head::has_interface (bounds[i]))
188 Grob *dots = unsmob_grob (bounds[i]->get_object ("dot"));
189 if (dots && d == LEFT)
191 Interval x = dots->extent (common, X_AXIS);
192 Real p = Staff_symbol_referencer::get_position (dots);
195 y *= (staff_space /4);
196 y.translate (p * staff_space * .5);
198 insert_extent_into_skyline (skyline,
199 Box (x,y), Y_AXIS, -d);
205 set_chord_outlines (Drul_array< Array<Skyline_entry> > *skyline_drul,
206 Link_array<Grob> ties,
213 Link_array<Item> bounds;
215 for (int i = 0; i < ties.size (); i++)
217 Item *it = dynamic_cast<Spanner*> (ties[i])->get_bound (d);
222 set_chord_outline (&skyline_drul->elem_ref (d),
225 while (flip (&d) != LEFT);
229 shift_small_ties (Array<Tie_configuration> *tie_configs,
230 Grob *staff_referencer,
231 Tie_details const &details)
233 set<int> positions_taken;
234 for (int i = 0; i < tie_configs->size (); i++)
235 positions_taken.insert (int (rint (tie_configs->elem (i).position_)));
237 for (int i = 0; i < tie_configs->size (); i++)
239 Tie_configuration * conf = &tie_configs->elem_ref (i);
242 on staff line and small enough, translate a little further
244 Real h = conf->height (details);
245 bool next_free = positions_taken.find (int (rint (conf->position_ + conf->dir_)))
246 == positions_taken.end ();
248 int rounded_pos = int (rint (conf->position_ + conf->delta_y_ / details.staff_space_));
249 bool on_line = Staff_symbol_referencer::on_staffline (staff_referencer, rounded_pos);
252 if (on_line && h < 0.4 * details.staff_space_)
254 positions_taken.insert (int (rint (conf->position_ + conf->dir_)));
255 conf->delta_y_ += 0.2 * details.staff_space_ * conf->dir_;
257 else if (!on_line && h > 0.6 * details.staff_space_)
259 positions_taken.insert (int (rint (conf->position_ + conf->dir_)));
260 conf->delta_y_ += 0.5 * details.staff_space_ * conf->dir_;
267 final_shape_adjustment (Tie_configuration &conf,
268 Drul_array< Array<Skyline_entry> > const &skylines,
269 Grob *staff_referencer,
270 Tie_details const &details)
273 bool on_line = Staff_symbol_referencer::on_staffline (staff_referencer,
274 int (rint (conf.position_)));
276 line_dy = - sign (conf.height (details) - 0.6 * details.staff_space_)
277 * 0.2 * details.staff_space_ * conf.dir_;
279 Real y = conf.position_ * details.staff_space_ * 0.5
282 conf.attachment_x_ = get_skyline_attachment (skylines, y);
283 conf.attachment_x_.intersect (get_skyline_attachment (skylines,
284 y + conf.dir_ * details.staff_space_ * 0.5));
286 conf.delta_y_ += line_dy;
287 conf.attachment_x_.widen (-details.x_gap_);
289 && Staff_symbol_referencer::staff_radius (staff_referencer) * details.staff_space_ > y)
290 conf.center_tie_vertically (details);
294 set_tie_config_directions (Array<Tie_configuration> *tie_configs_ptr)
296 Array<Tie_configuration> &tie_configs (*tie_configs_ptr);
298 if (!tie_configs[0].dir_)
299 tie_configs[0].dir_ = DOWN;
300 if (!tie_configs.top().dir_)
301 tie_configs.top().dir_ = UP;
306 for (int i = 1; i < tie_configs.size(); i++)
308 if (fabs (tie_configs[i-1].position_ - tie_configs[i].position_) <= 1)
310 if (!tie_configs[i-1].dir_)
311 tie_configs[i-1].dir_ = DOWN;
312 if (!tie_configs[i].dir_)
313 tie_configs[i].dir_ = UP;
317 for (int i = 1; i < tie_configs.size() - 1; i++)
319 if (tie_configs[i].dir_)
322 Direction position_dir = (Direction) sign (tie_configs[i].position_);
326 tie_configs[i].dir_ = position_dir;
332 Tie_column::new_directions (Grob *me)
334 extract_grob_set (me, "ties", ro_ties);
335 Link_array<Grob> ties (ro_ties);
339 if (ties.size() == 1)
341 Tie::set_default_control_points (ties[0]);
345 ties.sort (&Tie::compare);
347 Array<Tie_configuration> tie_configs;
348 for (int i = 0; i < ties.size (); i++)
350 Tie_configuration conf;
351 conf.dir_ = get_grob_direction (ties[i]);
352 conf.position_ = Tie::get_position (ties[i]);
353 tie_configs.push (conf);
356 SCM manual_configs = me->get_property ("tie-configuration");
357 bool manual_override = false;
359 for (SCM s = manual_configs;
360 scm_is_pair (s) && k < tie_configs.size(); s = scm_cdr (s))
362 SCM entry = scm_car (s);
363 if (!scm_is_pair (entry))
366 manual_override = true;
367 Real complete_pos = robust_scm2double (scm_car (entry), tie_configs[k].position_);
369 tie_configs[k].position_ = int (rint (complete_pos));
370 tie_configs[k].delta_y_ = complete_pos - tie_configs[k].position_;
371 tie_configs[k].dir_ = Direction (robust_scm2int (scm_cdr (entry), tie_configs[k].dir_));
375 set_tie_config_directions (&tie_configs);
378 for (int i = 0; i < ties.size (); i++)
380 common = dynamic_cast<Spanner*> (ties[i])->get_bound (LEFT)->common_refpoint (common, X_AXIS);
381 common = dynamic_cast<Spanner*> (ties[i])->get_bound (RIGHT)->common_refpoint (common, X_AXIS);
384 Drul_array< Array<Skyline_entry> > skylines;
385 set_chord_outlines (&skylines, ties, common);
388 details.init (ties[0]);
391 Let the ties flow out, according to our single-tie formatting.
393 if (!manual_override)
395 Tie::get_configuration (ties[0], common, &tie_configs.elem_ref (0),
399 Tie::get_configuration (ties.top (), common,
400 &tie_configs.elem_ref (tie_configs.size()-1),
407 Calculate final width and shape of the ties.
409 for (int i = 0; i < ties.size(); i++)
412 && (i == 0 || i == ties.size () -1))
416 final_shape_adjustment (tie_configs[i],
424 Try to shift small ties into available spaces.
426 if (!manual_override)
428 shift_small_ties (&tie_configs, ties[0], details);
431 for (int i = 0; i < ties.size(); i++)
433 Tie::set_control_points (ties[i], common, tie_configs[i],
436 set_grob_direction (ties[i], tie_configs[i].dir_);
441 ADD_INTERFACE (Tie_column, "tie-column-interface",
442 "Object that sets directions of multiple ties in a tied chord",