2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
22 #include "tie-formatting-problem.hh"
24 #include "axis-group-interface.hh"
25 #include "paper-column.hh"
27 #include "directional-element-interface.hh"
28 #include "libc-extension.hh"
30 #include "note-head.hh"
31 #include "rhythmic-head.hh"
32 #include "semi-tie.hh"
34 #include "staff-symbol-referencer.hh"
36 #include "tie-configuration.hh"
39 #include "pointer-group-interface.hh"
40 #include "output-def.hh"
47 Tie_formatting_problem::print_ties_configuration (Ties_configuration const *ties)
49 for (vsize i = 0; i < ties->size (); i++)
51 char const *man_pos = (specifications_[i].has_manual_position_) ? "(M)" : "";
52 char const *man_dir = (specifications_[i].has_manual_dir_) ? "(M)" : "";
53 char const *dir = (ties->at (i).dir_ == UP) ? "up" : "dn";
55 printf ("(P%d%s, %s%s) ", ties->at (i).position_, man_pos, dir, man_dir);
61 Tie_formatting_problem::get_attachment (Real y, Drul_array<int> columns) const
63 Interval attachments (0, 0);
65 for (LEFT_and_RIGHT (d))
67 Tuple2<int> key (columns[d], int (d));
68 Chord_outline_map::const_iterator i (chord_outlines_.find (key));
69 if (i == chord_outlines_.end ())
70 programming_error ("Cannot find chord outline");
72 attachments[d] = i->second.height (y);
78 Tie_formatting_problem::Tie_formatting_problem ()
82 use_horizontal_spacing_ = true;
85 Tie_formatting_problem::~Tie_formatting_problem ()
87 for (Tie_configuration_map::const_iterator i (possibilities_.begin ());
88 i != possibilities_.end (); i++)
93 Tie_formatting_problem::set_column_chord_outline (vector<Item *> bounds,
97 Real staff_space = Staff_symbol_referencer::staff_space (bounds[0]);
100 vector<Box> head_boxes;
103 for (vsize i = 0; i < bounds.size (); i++)
105 Grob *head = bounds[i];
106 if (!has_interface<Note_head> (head))
110 stem = unsmob<Grob> (head->get_object ("stem"));
112 Real p = Staff_symbol_referencer::get_position (head);
113 Interval y ((p - 1) * 0.5 * staff_space,
114 (p + 1) * 0.5 * staff_space);
116 Interval x = head->extent (x_refpoint_, X_AXIS);
117 head_boxes.push_back (Box (x, y));
118 boxes.push_back (Box (x, y));
120 Grob *dots = Rhythmic_head::get_dots (head);
121 if (dir == LEFT && dots)
123 Interval x = dots->extent (x_refpoint_, X_AXIS);
124 int p = int (Staff_symbol_referencer::get_position (dots));
127 TODO: shouldn't this use column-rank dependent key?
129 dot_positions_.insert (p);
132 Interval y (dots->extent (dots, Y_AXIS));
133 y.translate (p * staff_space * 0.5);
135 boxes.push_back (Box (x, y));
139 Tuple2<int> key (column_rank, int (dir));
143 if (Stem::is_normal_stem (stem))
146 x.add_point (stem->relative_coordinate (x_refpoint_, X_AXIS));
147 x.widen (staff_space / 20); // ugh.
150 Real stem_end_position = 0.0;
151 if (Stem::is_cross_staff (stem))
152 stem_end_position = get_grob_direction (stem) * infinity_f;
155 if (use_horizontal_spacing_ || !Stem::get_beam (stem))
156 stem_end_position = stem->extent (stem, Y_AXIS)[get_grob_direction (stem)];
158 // May want to change this to the stem's pure height...
159 stem_end_position = Stem::head_positions (stem)[get_grob_direction (stem)]
163 y.add_point (stem_end_position);
165 Direction stemdir = get_grob_direction (stem);
166 y.add_point (Stem::head_positions (stem)[-stemdir]
172 boxes.push_back (Box (x, y));
174 stem_extents_[key].unite (Box (x, y));
178 Grob *flag = Stem::flag (stem);
181 Grob *commony = stem->common_refpoint (flag, Y_AXIS);
182 boxes.push_back (Box (flag->extent (x_refpoint_, X_AXIS),
183 flag->extent (commony, Y_AXIS)));
189 Grob *head = Stem::support_head (stem);
192 In case of invisible stem, don't pass x-center of heads.
194 Real x_center = head->extent (x_refpoint_, X_AXIS).center ();
196 x_ext[-dir] = x_center;
197 x_ext[dir] = infinity_f * dir;
199 for (vsize j = 0; j < head_boxes.size (); j++)
200 y_ext.unite (head_boxes[j][Y_AXIS]);
202 boxes.push_back (Box (x_ext, y_ext));
205 extract_grob_set (stem, "note-heads", heads);
206 for (vsize i = 0; i < heads.size (); i++)
208 if (find (bounds.begin (), bounds.end (), dynamic_cast<Item *> (heads[i])) == bounds.end ())
211 other untied notes in the same chord.
214 Interval y = Staff_symbol_referencer::extent_in_staff (heads[i]);
215 Interval x = heads[i]->extent (x_refpoint_, X_AXIS);
216 boxes.push_back (Box (x, y));
219 Grob *acc = unsmob<Grob> (heads[i]->get_object ("accidental-grob"));
221 acc->get_property ("after-line-breaking"); /* trigger tie-related suicide */
223 if (acc && acc->is_live () && dir == RIGHT)
225 boxes.push_back (Box (acc->extent (x_refpoint_, X_AXIS),
226 Staff_symbol_referencer::extent_in_staff (acc)));
229 head_positions_[column_rank].add_point (int (Staff_symbol_referencer::get_position (heads[i])));
234 for (DOWN_and_UP (updowndir))
238 if (head_boxes.size ())
240 Box b = boundary (head_boxes, updowndir, 0);
242 x[-dir] = b[X_AXIS].linear_combination (-dir / 2);
243 y[-updowndir] = b[Y_AXIS][updowndir];
244 y[updowndir] = updowndir * infinity_f;
248 boxes.push_back (Box (x, y));
251 chord_outlines_[key] = Skyline (boxes, Y_AXIS, -dir).padded (details_.skyline_padding_);
252 if (bounds[0]->break_status_dir ())
254 Interval iv (Axis_group_interface::staff_extent (bounds[0], x_refpoint_, X_AXIS, y_refpoint_, Y_AXIS));
256 iv.add_point (bounds[0]->relative_coordinate (x_refpoint_, X_AXIS));
258 chord_outlines_[key].set_minimum_height (iv[-dir]);
263 for (vsize j = 0; j < head_boxes.size (); j++)
265 x.unite (head_boxes[j][X_AXIS]);
268 chord_outlines_[key].set_minimum_height (x[dir]);
271 head_extents_[key].set_empty ();
272 for (vsize i = 0; i < head_boxes.size (); i++)
274 head_extents_[key].unite (head_boxes[i]);
279 Tie_formatting_problem::set_chord_outline (vector<Item *> bounds,
284 for (vsize i = 0; i < bounds.size (); i++)
285 ranks.push_back (bounds[i]->get_column ()->get_rank ());
287 vector_sort (ranks, std::less<int> ());
290 for (vsize i = 0; i < ranks.size (); i++)
292 vector<Item *> col_items;
293 for (vsize j = 0; j < bounds.size (); j++)
295 if (bounds[j]->get_column ()->get_rank () == ranks[i])
296 col_items.push_back (bounds[j]);
299 set_column_chord_outline (col_items, dir, ranks[i]);
304 Tie_formatting_problem::from_tie (Grob *tie)
307 ties.push_back (tie);
310 details_.from_grob (tie);
314 Tie_formatting_problem::common_x_refpoint () const
320 Tie_formatting_problem::from_ties (vector<Grob *> const &ties)
325 x_refpoint_ = ties[0];
326 y_refpoint_ = ties[0];
327 for (vsize i = 0; i < ties.size (); i++)
329 Spanner *tie = dynamic_cast<Spanner *> (ties[i]);
330 Item *l = tie->get_bound (LEFT);
331 Item *r = tie->get_bound (RIGHT);
333 x_refpoint_ = l->common_refpoint (x_refpoint_, X_AXIS);
334 x_refpoint_ = r->common_refpoint (x_refpoint_, X_AXIS);
336 if (!l->break_status_dir ())
337 y_refpoint_ = l->common_refpoint (y_refpoint_, Y_AXIS);
338 if (!r->break_status_dir ())
339 y_refpoint_ = r->common_refpoint (y_refpoint_, Y_AXIS);
342 details_.from_grob (ties[0]);
344 for (LEFT_and_RIGHT (d))
346 vector<Item *> bounds;
348 for (vsize i = 0; i < ties.size (); i++)
350 Spanner *tie = dynamic_cast<Spanner *> (ties[i]);
351 Item *it = tie->get_bound (d);
352 if (it->break_status_dir ())
353 it = it->get_column ();
355 bounds.push_back (it);
358 set_chord_outline (bounds, d);
361 for (vsize i = 0; i < ties.size (); i++)
363 Spanner *tie = dynamic_cast<Spanner *> (ties[i]);
364 Tie_specification spec;
365 spec.from_grob (tie);
367 for (LEFT_and_RIGHT (d))
369 spec.note_head_drul_[d] = Tie::head (tie, d);
370 spec.column_ranks_[d] = Tie::get_column_rank (tie, d);
372 specifications_.push_back (spec);
377 Tie_formatting_problem::from_semi_ties (vector<Grob *> const &semi_ties, Direction head_dir)
379 if (semi_ties.empty ())
382 use_horizontal_spacing_ = false;
383 details_.from_grob (semi_ties[0]);
384 vector<Item *> heads;
386 int column_rank = -1;
387 for (vsize i = 0; i < semi_ties.size (); i++)
389 Item *semi_tie = dynamic_cast<Item *> (semi_ties[i]);
390 Tie_specification spec;
391 Item *head = Semi_tie::head (semi_tie);
394 programming_error ("LV tie without head?!");
398 spec.position_ = int (Staff_symbol_referencer::get_position (head));
401 spec.from_grob (semi_tie);
403 spec.note_head_drul_[head_dir] = head;
404 column_rank = Semi_tie::get_column_rank (semi_tie);
405 spec.column_ranks_ = Drul_array<int> (column_rank, column_rank);
406 heads.push_back (head);
407 specifications_.push_back (spec);
410 x_refpoint_ = semi_ties[0];
411 y_refpoint_ = semi_ties[0];
413 for (vsize i = 0; i < semi_ties.size (); i++)
415 x_refpoint_ = semi_ties[i]->common_refpoint (x_refpoint_, X_AXIS);
416 y_refpoint_ = semi_ties[i]->common_refpoint (y_refpoint_, Y_AXIS);
418 for (vsize i = 0; i < heads.size (); i++)
420 x_refpoint_ = heads[i]->common_refpoint (x_refpoint_, X_AXIS);
421 y_refpoint_ = heads[i]->common_refpoint (y_refpoint_, Y_AXIS);
424 set_chord_outline (heads, head_dir);
426 Tuple2<int> head_key (column_rank, head_dir);
427 Tuple2<int> open_key (column_rank, -head_dir);
428 Real extremal = chord_outlines_[head_key].max_height ();
430 chord_outlines_[open_key] = Skyline (head_dir);
431 chord_outlines_[open_key].set_minimum_height (extremal - head_dir * 1.5);
435 Tie_formatting_problem::get_tie_specification (int i) const
437 return specifications_[i];
441 Return configuration, create it if necessary.
444 Tie_formatting_problem::get_configuration (int pos, Direction dir, Drul_array<int> columns,
450 pos, dir, columns[LEFT], columns[RIGHT]
452 Tuple<int, 4> key (key_components);
454 Tie_configuration_map::const_iterator f = possibilities_.find (key);
455 if (f != possibilities_.end ())
460 Tie_configuration *conf = generate_configuration (pos, dir, columns, tune_dy);
461 ((Tie_formatting_problem *) this)->possibilities_[key] = conf;
466 Tie_formatting_problem::generate_configuration (int pos, Direction dir,
467 Drul_array<int> columns, bool y_tune) const
469 Tie_configuration *conf = new Tie_configuration;
470 conf->position_ = pos;
473 conf->column_ranks_ = columns;
475 Real y = conf->position_ * 0.5 * details_.staff_space_;
477 if (dot_positions_.find (pos) != dot_positions_.end ())
479 conf->delta_y_ += dir * 0.25 * details_.staff_space_;
484 && std::max (fabs (get_head_extent (columns[LEFT], LEFT, Y_AXIS)[dir] - y),
485 fabs (get_head_extent (columns[RIGHT], RIGHT, Y_AXIS)[dir] - y)) < 0.25
486 && !Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos))
489 = (get_head_extent (columns[LEFT], LEFT, Y_AXIS)[dir] - y)
490 + dir * details_.outer_tie_vertical_gap_;
495 conf->attachment_x_ = get_attachment (y + conf->delta_y_, conf->column_ranks_);
496 Real h = conf->height (details_);
501 - should make sliding criterion, should flatten ties if
503 - they're just the wrong (ie. touching line at top & bottom)
508 = Staff_symbol_referencer::staff_span (details_.staff_symbol_referencer_);
509 staff_span.widen (-1);
510 bool const within_staff = staff_span.contains (pos);
511 if (head_positions_slice (columns[LEFT]).contains (pos)
512 || head_positions_slice (columns[RIGHT]).contains (pos)
515 if (h < details_.intra_space_threshold_ * 0.5 * details_.staff_space_)
517 if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos))
519 conf->delta_y_ += dir *
520 details_.tip_staff_line_clearance_ * 0.5 * details_.staff_space_;
522 else if (within_staff)
524 conf->center_tie_vertically (details_);
529 Real top_y = y + conf->delta_y_ + conf->dir_ * h;
530 Real top_pos = top_y / (0.5 * details_.staff_space_);
531 int round_pos = int (my_round (top_pos));
533 /* TODO: should use other variable? */
534 Real clearance = details_.center_staff_line_clearance_;
535 if (fabs (top_pos - round_pos) < clearance
536 && Staff_symbol_referencer::on_staff_line (details_.staff_symbol_referencer_,
539 Real new_y = (round_pos + clearance * conf->dir_) * 0.5 * details_.staff_space_;
540 conf->delta_y_ = (new_y - top_y);
545 conf->attachment_x_ = get_attachment (y + conf->delta_y_, conf->column_ranks_);
546 if (conf->height (details_) < details_.intra_space_threshold_ * 0.5 * details_.staff_space_)
549 This is less sensible for long ties, since those are more
552 Interval close_by = get_attachment (y
554 + (dir * details_.intra_space_threshold_ * 0.25
555 * details_.staff_space_),
556 conf->column_ranks_);
558 conf->attachment_x_.intersect (close_by);
561 conf->attachment_x_.widen ( - details_.x_gap_);
563 if (conf->column_span_length ())
566 avoid the stems that we attach to as well. We don't do this
567 for semities (span length = 0)
569 It would be better to check D against HEAD-DIRECTION if
572 for (LEFT_and_RIGHT (d))
574 Real y = conf->position_ * details_.staff_space_ * 0.5 + conf->delta_y_;
575 if (get_stem_extent (conf->column_ranks_[d], d, X_AXIS).is_empty ()
576 || !get_stem_extent (conf->column_ranks_[d], d, Y_AXIS).contains (y))
579 conf->attachment_x_[d]
580 = d * std::min (d * conf->attachment_x_[d],
581 d * (get_stem_extent (conf->column_ranks_[d], d, X_AXIS)[-d] - d * details_.stem_gap_));
588 Tie_formatting_problem::get_head_extent (int col, Direction d, Axis a) const
590 Column_extent_map::const_iterator i = head_extents_.find (Tuple2<int> (col, int (d)));
591 if (i != head_extents_.end ())
592 return (*i).second[a];
598 Tie_formatting_problem::get_stem_extent (int col, Direction d, Axis a) const
600 Column_extent_map::const_iterator i = stem_extents_.find (Tuple2<int> (col, int (d)));
601 if (i != stem_extents_.end ())
602 return (*i).second[a];
608 TIE_IDX and TIES_CONF are optional.
611 Tie_formatting_problem::score_aptitude (Tie_configuration *conf,
612 Tie_specification const &spec,
613 Ties_configuration *ties_conf, int tie_idx) const
616 Real curve_y = conf->position_ * details_.staff_space_ * 0.5 + conf->delta_y_;
617 Real tie_y = spec.position_ * details_.staff_space_ * 0.5;
618 if (sign (curve_y - tie_y) != conf->dir_)
620 Real p = details_.wrong_direction_offset_penalty_;
622 ties_conf->add_tie_score (p, tie_idx, "wrong dir");
628 Real relevant_dist = std::max (fabs (curve_y - tie_y) - 0.5, 0.0);
629 Real p = details_.vertical_distance_penalty_factor_ * convex_amplifier (1.0, 0.9, relevant_dist);
631 ties_conf->add_tie_score (p, tie_idx, "vdist");
636 for (LEFT_and_RIGHT (d))
638 if (!spec.note_head_drul_[d])
641 Interval head_x = spec.note_head_drul_[d]->extent (x_refpoint_, X_AXIS);
642 Real dist = head_x.distance (conf->attachment_x_[d]);
645 TODO: flatten with log or sqrt.
647 Real p = details_.horizontal_distance_penalty_factor_
648 * convex_amplifier (1.25, 1.0, dist);
650 ties_conf->add_tie_score (p, tie_idx,
651 (d == LEFT) ? "lhdist" : "rhdist");
658 && ties_conf->size () == 1)
660 Drul_array<Grob *> stems (0, 0);
661 for (LEFT_and_RIGHT (d))
663 if (!spec.note_head_drul_[d])
666 Grob *stem = unsmob<Grob> (spec.note_head_drul_[d]->get_object ("stem"));
668 && Stem::is_normal_stem (stem))
672 bool tie_stem_dir_ok = true;
673 bool tie_position_dir_ok = true;
674 if (stems[LEFT] && !stems[RIGHT])
675 tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[LEFT]);
676 else if (!stems[LEFT] && stems[RIGHT])
677 tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[RIGHT]);
678 else if (stems[LEFT] && stems[RIGHT]
679 && get_grob_direction (stems[LEFT]) == get_grob_direction (stems[RIGHT]))
680 tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[LEFT]);
681 else if (spec.position_)
682 tie_position_dir_ok = conf->dir_ == sign (spec.position_);
684 if (!tie_stem_dir_ok)
685 ties_conf->add_score (details_.same_dir_as_stem_penalty_, "tie/stem dir");
686 if (!tie_position_dir_ok)
687 ties_conf->add_score (details_.same_dir_as_stem_penalty_, "tie/pos dir");
694 Tie_formatting_problem::head_positions_slice (int rank) const
696 Position_extent_map::const_iterator i (head_positions_.find (rank));
697 if (i != head_positions_.end ())
706 Score a configuration, ie. how well these ties looks without regard
707 to the note heads that they should connect to.
710 Tie_formatting_problem::score_configuration (Tie_configuration *conf) const
717 Real length = conf->attachment_x_.length ();
720 = peak_around (0.33 * details_.min_length_, details_.min_length_, length);
721 conf->add_score (details_.min_length_penalty_factor_
722 * length_penalty, "minlength");
724 Real tip_pos = conf->position_ + conf->delta_y_ / 0.5 * details_.staff_space_;
725 Real tip_y = tip_pos * details_.staff_space_ * 0.5;
726 Real height = conf->height (details_);
728 Real top_y = tip_y + conf->dir_ * height;
729 Real top_pos = 2 * top_y / details_.staff_space_;
730 Real round_top_pos = rint (top_pos);
732 = Staff_symbol_referencer::staff_span (details_.staff_symbol_referencer_);
733 if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_,
735 && staff_span[UP] * 0.5 > top_y)
737 conf->add_score (details_.staff_line_collision_penalty_
738 * peak_around (0.1 * details_.center_staff_line_clearance_,
739 details_.center_staff_line_clearance_,
740 fabs (top_pos - round_top_pos)),
744 int rounded_tip_pos = int (rint (tip_pos));
745 staff_span.widen (-1);
746 if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, rounded_tip_pos)
747 && (head_positions_slice (conf->column_ranks_[LEFT]).contains (rounded_tip_pos)
748 || head_positions_slice (conf->column_ranks_[RIGHT]).contains (rounded_tip_pos)
749 || staff_span.contains (rounded_tip_pos))
752 conf->add_score (details_.staff_line_collision_penalty_
753 * peak_around (0.1 * details_.tip_staff_line_clearance_,
754 details_.tip_staff_line_clearance_,
755 fabs (tip_pos - rint (tip_pos))),
759 if (!dot_x_.is_empty ())
762 Real x = dot_x_.center ();
764 Bezier b = conf->get_transformed_bezier (details_);
765 if (b.control_point_extent (X_AXIS).contains (x))
767 Real y = b.get_other_coordinate (X_AXIS, x);
769 for (set<int>::const_iterator i (dot_positions_.begin ());
770 i != dot_positions_.end (); i++)
773 conf->add_score (details_.dot_collision_penalty_
774 * peak_around (.1 * details_.dot_collision_clearance_,
775 details_.dot_collision_clearance_,
776 fabs (dot_pos * details_.staff_space_ * 0.5 - y)),
782 conf->scored_ = true;
786 Tie_formatting_problem::score_ties_aptitude (Ties_configuration *ties) const
788 if (ties->size () != specifications_.size ())
790 programming_error ("Huh? Mismatch between sizes.");
794 for (vsize i = 0; i < ties->size (); i++)
795 score_aptitude (&ties->at (i), specifications_[i],
800 Tie_formatting_problem::score_ties (Ties_configuration *ties) const
805 score_ties_configuration (ties);
806 score_ties_aptitude (ties);
807 ties->scored_ = true;
811 Tie_formatting_problem::score_ties_configuration (Ties_configuration *ties) const
813 for (vsize i = 0; i < ties->size (); i++)
815 score_configuration (&ties->at (i));
816 ties->add_tie_score (ties->at (i).score (), i, "conf");
819 Real last_edge = 0.0;
820 Real last_center = 0.0;
821 for (vsize i = 0; i < ties->size (); i++)
823 Bezier b (ties->at (i).get_transformed_bezier (details_));
825 Real center = b.curve_point (0.5)[Y_AXIS];
826 Real edge = b.curve_point (0.0)[Y_AXIS];
830 if (edge <= last_edge)
831 ties->add_score (details_.tie_column_monotonicity_penalty_, "monoton edge");
832 if (center <= last_center)
833 ties->add_score (details_.tie_column_monotonicity_penalty_, "monoton cent");
835 ties->add_score (details_.tie_tie_collision_penalty_ *
836 peak_around (0.1 * details_.tie_tie_collision_distance_,
837 details_.tie_tie_collision_distance_,
838 fabs (center - last_center)),
840 ties->add_score (details_.tie_tie_collision_penalty_ *
841 peak_around (0.1 * details_.tie_tie_collision_distance_,
842 details_.tie_tie_collision_distance_,
843 fabs (edge - last_edge)), "tietie edge");
847 last_center = center;
850 if (ties->size () > 1)
852 ties->add_score (details_.outer_tie_length_symmetry_penalty_factor_
853 * fabs (ties->at (0).attachment_x_.length () - ties->back ().attachment_x_.length ()),
856 ties->add_score (details_.outer_tie_vertical_distance_symmetry_penalty_factor_
857 * fabs (fabs (specifications_[0].position_ * 0.5 * details_.staff_space_
858 - (ties->at (0).position_ * 0.5 * details_.staff_space_
859 + ties->at (0).delta_y_))
861 fabs (specifications_.back ().position_ * 0.5 * details_.staff_space_
862 - (ties->back ().position_ * 0.5 * details_.staff_space_
863 + ties->back ().delta_y_))),
869 Generate with correct X-attachments and beziers, copying delta_y_
870 from TIES_CONFIG if necessary.
873 Tie_formatting_problem::generate_ties_configuration (Ties_configuration const &ties_config)
875 Ties_configuration copy;
876 for (vsize i = 0; i < ties_config.size (); i++)
878 Tie_configuration *ptr = get_configuration (ties_config[i].position_, ties_config[i].dir_,
879 ties_config[i].column_ranks_,
880 !specifications_[i].has_manual_delta_y_);
881 if (specifications_[i].has_manual_delta_y_)
884 = (specifications_[i].manual_position_ - ties_config[i].position_)
885 * 0.5 * details_.staff_space_;
887 copy.push_back (*ptr);
894 Tie_formatting_problem::generate_base_chord_configuration ()
896 Ties_configuration ties_config;
897 for (vsize i = 0; i < specifications_.size (); i++)
899 Tie_configuration conf;
900 if (specifications_[i].has_manual_dir_)
901 conf.dir_ = specifications_[i].manual_dir_;
902 if (specifications_[i].has_manual_position_)
904 conf.position_ = (int) my_round (specifications_[i].manual_position_);
905 if (specifications_[i].has_manual_delta_y_)
906 conf.delta_y_ = (specifications_[i].manual_position_ - conf.position_)
907 * 0.5 * details_.staff_space_;
911 conf.position_ = specifications_[i].position_;
914 conf.column_ranks_ = specifications_[i].column_ranks_;
915 ties_config.push_back (conf);
918 set_ties_config_standard_directions (&ties_config);
919 for (vsize i = 0; i < ties_config.size (); i++)
920 if (!specifications_[i].manual_position_)
921 ties_config[i].position_ += ties_config[i].dir_;
923 ties_config = generate_ties_configuration (ties_config);
929 Tie_formatting_problem::find_best_variation (Ties_configuration const &base,
930 vector<Tie_configuration_variation> const &vars)
932 Ties_configuration best = base;
935 This simply is 1-opt: we have K substitions, and we try applying
936 exactly every one for each.
938 for (vsize i = 0; i < vars.size (); i++)
940 Ties_configuration variant (base);
941 for (vsize j = 0; j < vars[i].index_suggestion_pairs_.size (); j++)
942 variant[vars[i].index_suggestion_pairs_[j].first] = *vars[i].index_suggestion_pairs_[j].second;
944 variant.reset_score ();
945 score_ties (&variant);
947 if (variant.score () < best.score ())
957 Tie_formatting_problem::generate_optimal_configuration ()
959 Ties_configuration base = generate_base_chord_configuration ();
962 vector<Tie_configuration_variation> vars;
963 if (specifications_.size () > 1)
964 vars = generate_collision_variations (base);
966 vars = generate_single_tie_variations (base);
968 Ties_configuration best = find_best_variation (base, vars);
970 if (specifications_.size () > 1)
972 vars = generate_extremal_tie_variations (best);
973 best = find_best_variation (best, vars);
979 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration *tie_configs)
981 if (tie_configs->empty ())
984 if (!tie_configs->at (0).dir_)
986 if (tie_configs->size () == 1)
987 tie_configs->at (0).dir_ = Direction (sign (tie_configs->at (0).position_));
989 if (!tie_configs->at (0).dir_)
990 tie_configs->at (0).dir_
991 = (tie_configs->size () > 1) ? DOWN : details_.neutral_direction_;
994 if (!tie_configs->back ().dir_)
995 tie_configs->back ().dir_ = UP;
1000 for (vsize i = 1; i < tie_configs->size (); i++)
1002 Real diff = (tie_configs->at (i).position_
1003 - tie_configs->at (i - 1).position_);
1006 = specifications_[i].column_span () - specifications_[i - 1].column_span ();
1007 if (span_diff && fabs (diff) <= 2)
1010 tie_configs->at (i).dir_ = UP;
1011 else if (span_diff < 0)
1012 tie_configs->at (i - 1).dir_ = DOWN;
1014 else if (fabs (diff) <= 1)
1016 if (!tie_configs->at (i - 1).dir_)
1017 tie_configs->at (i - 1).dir_ = DOWN;
1018 if (!tie_configs->at (i).dir_)
1019 tie_configs->at (i).dir_ = UP;
1023 for (vsize i = 1; i + 1 < tie_configs->size (); i++)
1025 Tie_configuration &conf = tie_configs->at (i);
1029 Direction position_dir
1030 = Direction (sign (conf.position_));
1032 position_dir = DOWN;
1034 conf.dir_ = position_dir;
1038 vector<Tie_configuration_variation>
1039 Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration const &ties) const
1041 vector<Tie_configuration_variation> vars;
1042 for (int i = 1; i <= details_.multi_tie_region_size_; i++)
1044 Drul_array<Tie_configuration *> configs (0, 0);
1045 for (DOWN_and_UP (d))
1047 const Tie_configuration &config = boundary (ties, d, 0);
1048 if (config.dir_ == d
1049 && !boundary (specifications_, d, 0).has_manual_position_)
1051 Tie_configuration_variation var;
1052 configs[d] = get_configuration (config.position_ + d * i, d,
1053 config.column_ranks_,
1055 var.add_suggestion ((d == DOWN) ? 0 : ties.size () - 1,
1057 vars.push_back (var);
1060 if (configs[LEFT] && configs[RIGHT])
1062 Tie_configuration_variation var;
1063 var.add_suggestion (0, configs[DOWN]);
1064 var.add_suggestion (ties.size () - 1, configs[UP]);
1065 vars.push_back (var);
1072 vector<Tie_configuration_variation>
1073 Tie_formatting_problem::generate_single_tie_variations (Ties_configuration const &ties) const
1075 vector<Tie_configuration_variation> vars;
1077 int sz = details_.single_tie_region_size_;
1078 if (specifications_[0].has_manual_position_)
1080 for (int i = 0; i < sz; i++)
1082 for (LEFT_and_RIGHT (d))
1085 && ties[0].dir_ == d)
1088 int p = ties[0].position_ + i * d;
1090 if (!specifications_[0].has_manual_dir_
1091 || d == specifications_[0].manual_dir_)
1093 Tie_configuration_variation var;
1094 var.add_suggestion (0,
1095 get_configuration (p,
1096 d, specifications_[0].column_ranks_,
1097 !specifications_[0].has_manual_delta_y_));
1098 vars.push_back (var);
1105 vector<Tie_configuration_variation>
1106 Tie_formatting_problem::generate_collision_variations (Ties_configuration const &ties) const
1108 Real center_distance_tolerance = 0.25;
1110 vector<Tie_configuration_variation> vars;
1111 Real last_center = 0.0;
1112 for (vsize i = 0; i < ties.size (); i++)
1114 Bezier b (ties[i].get_transformed_bezier (details_));
1116 Real center = b.curve_point (0.5)[Y_AXIS];
1120 if (center <= last_center + center_distance_tolerance)
1122 if (!specifications_[i].has_manual_dir_)
1124 Tie_configuration_variation var;
1125 var.add_suggestion (i,
1126 get_configuration (specifications_[i].position_
1130 ties[i].column_ranks_,
1131 !specifications_[i].has_manual_delta_y_
1134 vars.push_back (var);
1137 if (!specifications_[i - 1].has_manual_dir_)
1139 Tie_configuration_variation var;
1140 var.add_suggestion (i - 1,
1141 get_configuration (specifications_[i - 1].position_
1144 specifications_[i - 1].column_ranks_,
1145 !specifications_[i - 1].has_manual_delta_y_));
1147 vars.push_back (var);
1150 if (i == 1 && !specifications_[i - 1].has_manual_position_
1151 && ties[i - 1].dir_ == DOWN)
1153 Tie_configuration_variation var;
1154 var.add_suggestion (i - 1,
1155 get_configuration (specifications_[i - 1].position_ - 1, DOWN,
1156 specifications_[i - 1].column_ranks_,
1157 !specifications_[i - 1].has_manual_delta_y_
1159 vars.push_back (var);
1161 if (i == ties.size () && !specifications_[i].has_manual_position_
1162 && ties[i].dir_ == UP)
1164 Tie_configuration_variation var;
1165 var.add_suggestion (i,
1166 get_configuration (specifications_[i].position_
1168 specifications_[i].column_ranks_,
1169 !specifications_[i].has_manual_delta_y_
1171 vars.push_back (var);
1174 else if (dot_positions_.find (ties[i].position_) != dot_positions_.end ()
1175 && !specifications_[i].has_manual_position_)
1177 Tie_configuration_variation var;
1178 var.add_suggestion (i,
1179 get_configuration (ties[i].position_ + ties[i].dir_,
1181 ties[i].column_ranks_,
1182 !specifications_[i].has_manual_delta_y_
1184 vars.push_back (var);
1189 last_center = center;
1196 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs)
1199 for (SCM s = manual_configs;
1200 scm_is_pair (s) && k < specifications_.size (); s = scm_cdr (s))
1202 SCM entry = scm_car (s);
1203 if (scm_is_pair (entry))
1205 Tie_specification &spec = specifications_[k];
1207 if (scm_is_number (scm_car (entry)))
1209 spec.has_manual_position_ = true;
1210 spec.manual_position_ = scm_to_double (scm_car (entry));
1211 /* TODO: check whether inexact? is an appropriate condition here */
1212 spec.has_manual_delta_y_ = (scm_is_true (scm_inexact_p (scm_car (entry))));
1215 if (scm_is_number (scm_cdr (entry)))
1217 spec.has_manual_dir_ = true;
1218 spec.manual_dir_ = Direction (scm_to_int (scm_cdr (entry)));
1226 Tie_formatting_problem::set_debug_scoring (Ties_configuration const &base)
1228 #if DEBUG_TIE_SCORING
1229 if (to_boolean (x_refpoint_->layout ()
1230 ->lookup_variable (ly_symbol2scm ("debug-tie-scoring"))))
1232 for (vsize i = 0; i < base.size (); i++)
1234 string card = base.complete_tie_card (i);
1235 specifications_[i].tie_grob_->set_property ("annotation",
1236 ly_string2scm (card));