Bump version.
[lilypond.git] / lily / tie-formatting-problem.cc
blob7eb20666be348d15d9b856c6d4062de33cca12b6
1 /*
2 tie-formatting-problem.cc -- implement Tie_formatting_problem
4 source file of the GNU LilyPond music typesetter
6 (c) 2005--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
8 */
10 #include <cstdio>
11 #include "tie-formatting-problem.hh"
13 #include "axis-group-interface.hh"
14 #include "paper-column.hh"
15 #include "bezier.hh"
16 #include "directional-element-interface.hh"
17 #include "libc-extension.hh"
18 #include "misc.hh"
19 #include "note-head.hh"
20 #include "rhythmic-head.hh"
21 #include "spanner.hh"
22 #include "staff-symbol-referencer.hh"
23 #include "stem.hh"
24 #include "tie-configuration.hh"
25 #include "tie.hh"
26 #include "warn.hh"
27 #include "pointer-group-interface.hh"
28 #include "output-def.hh"
30 void
31 Tie_formatting_problem::print_ties_configuration (Ties_configuration const *ties)
33 for (vsize i = 0; i < ties->size (); i++)
35 char const *man_pos = (specifications_[i].has_manual_position_) ? "(M)" : "";
36 char const *man_dir = (specifications_[i].has_manual_dir_) ? "(M)" : "";
37 char const *dir = (ties->at (i).dir_ == UP) ? "up" : "dn";
39 printf ("(P%d%s, %s%s) ", ties->at (i).position_, man_pos, dir, man_dir);
41 printf ("\n");
44 Interval
45 Tie_formatting_problem::get_attachment (Real y, Drul_array<int> columns) const
47 Interval attachments (0,0);
48 Direction d = LEFT;
51 Tuple2<int> key (columns[d], int (d));
52 Chord_outline_map::const_iterator i (chord_outlines_.find (key));
53 if (i == chord_outlines_.end ())
54 programming_error ("Cannot find chord outline");
55 else
56 attachments[d] = i->second.height (y);
58 while (flip (&d) != LEFT);
60 return attachments;
63 Tie_formatting_problem::Tie_formatting_problem ()
65 x_refpoint_ = 0;
66 y_refpoint_ = 0;
67 use_horizontal_spacing_ = true;
70 Tie_formatting_problem::~Tie_formatting_problem ()
72 for (Tie_configuration_map::const_iterator i (possibilities_.begin ());
73 i != possibilities_.end (); i++)
74 delete (*i).second;
77 void
78 Tie_formatting_problem::set_column_chord_outline (vector<Item*> bounds,
79 Direction dir,
80 int column_rank)
82 Real staff_space = Staff_symbol_referencer::staff_space (bounds[0]);
84 vector<Box> boxes;
85 vector<Box> head_boxes;
87 Grob *stem = 0;
88 for (vsize i = 0; i < bounds.size (); i++)
90 Grob *head = bounds[i];
91 if (!Note_head::has_interface (head))
92 continue;
94 if (!stem)
95 stem = unsmob_grob (head->get_object ("stem"));
97 Real p = Staff_symbol_referencer::get_position (head);
98 Interval y ((p-1) * 0.5 * staff_space,
99 (p+1) * 0.5 * staff_space);
101 Interval x = head->extent (x_refpoint_, X_AXIS);
102 head_boxes.push_back (Box (x, y));
103 boxes.push_back (Box (x, y));
105 Grob *dots = Rhythmic_head::get_dots (head);
106 if (dir == LEFT && dots)
108 Interval x = dots->extent (x_refpoint_, X_AXIS);
109 int p = int (Staff_symbol_referencer::get_position (dots));
112 TODO: shouldn't this use column-rank dependent key?
114 dot_positions_.insert (p);
115 dot_x_.unite (x);
117 Interval y (dots->extent (dots, Y_AXIS));
118 y.translate (p * staff_space * 0.5);
120 boxes.push_back (Box (x, y));
124 Tuple2<int> key (column_rank, int (dir));
126 if (stem)
128 if (Stem::is_normal_stem (stem))
130 Interval x;
131 x.add_point (stem->relative_coordinate (x_refpoint_, X_AXIS));
132 x.widen (staff_space / 20); // ugh.
133 Interval y;
135 Real stem_end_position = 0.0;
136 if (Stem::is_cross_staff (stem))
137 stem_end_position = get_grob_direction (stem) * infinity_f;
138 else
140 if (use_horizontal_spacing_ || !Stem::get_beam (stem))
141 stem_end_position = Stem::stem_end_position (stem) * staff_space * .5;
142 else
143 stem_end_position = Stem::note_head_positions (stem)[get_grob_direction (stem)]
144 * staff_space * .5;
147 y.add_point (stem_end_position);
149 Direction stemdir = get_grob_direction (stem);
150 y.add_point (Stem::head_positions (stem)[-stemdir]
151 * staff_space * .5);
154 add extents of stem.
156 boxes.push_back (Box (x, y));
158 stem_extents_[key].unite (Box (x, y));
160 if (dir == LEFT)
162 Box flag_box = Stem::get_translated_flag (stem).extent_box ();
163 flag_box.translate ( Offset (x[RIGHT], X_AXIS));
164 boxes.push_back (flag_box);
167 else
169 Grob *head = Stem::support_head (stem);
172 In case of invisible stem, don't pass x-center of heads.
174 Real x_center = head->extent (x_refpoint_, X_AXIS).center ();
175 Interval x_ext;
176 x_ext[-dir] = x_center;
177 x_ext[dir] = infinity_f * dir;
178 Interval y_ext;
179 for (vsize j = 0; j < head_boxes.size (); j++)
180 y_ext.unite (head_boxes[j][Y_AXIS]);
182 boxes.push_back (Box (x_ext, y_ext));
185 extract_grob_set (stem, "note-heads", heads);
186 for (vsize i = 0; i < heads.size (); i ++)
188 if (find (bounds.begin (), bounds.end (), dynamic_cast<Item*> (heads[i])) == bounds.end ())
191 other untied notes in the same chord.
194 Interval y = Staff_symbol_referencer::extent_in_staff (heads[i]);
195 Interval x = heads[i]->extent (x_refpoint_, X_AXIS);
196 boxes.push_back (Box (x, y));
199 Grob *acc = unsmob_grob (heads[i]->get_object ("accidental-grob"));
200 if (acc)
201 acc->get_property ("stencil"); /* trigger tie-related suicide */
203 if (acc && acc->is_live () && dir == RIGHT)
205 boxes.push_back (Box (acc->extent (x_refpoint_, X_AXIS),
206 Staff_symbol_referencer::extent_in_staff (acc)));
209 head_positions_[column_rank].add_point (int (Staff_symbol_referencer::get_position (heads[i])));
214 Direction updowndir = DOWN;
217 Interval x;
218 Interval y;
219 if (head_boxes.size ())
221 Box b = boundary (head_boxes, updowndir, 0);
222 x = b[X_AXIS];
223 x[-dir] = b[X_AXIS].linear_combination (-dir / 2);
224 y[-updowndir] = b[Y_AXIS][updowndir];
225 y[updowndir] = updowndir * infinity_f;
228 if (!x.is_empty ())
229 boxes.push_back (Box (x, y));
231 while (flip (&updowndir) != DOWN);
233 /* todo: the horizon_padding is somewhat arbitrary */
234 chord_outlines_[key] = Skyline (boxes, details_.skyline_padding_, Y_AXIS, -dir);
235 if (bounds[0]->break_status_dir ())
237 Interval iv (Axis_group_interface::staff_extent (bounds[0], x_refpoint_, X_AXIS, y_refpoint_, Y_AXIS));
238 if (iv.is_empty ())
239 iv.add_point (bounds[0]->relative_coordinate (x_refpoint_, X_AXIS));
241 chord_outlines_[key].set_minimum_height (iv[-dir]);
243 else
245 Interval x;
246 for (vsize j = 0; j < head_boxes.size (); j++)
248 x.unite (head_boxes[j][X_AXIS]);
251 chord_outlines_[key].set_minimum_height (x[dir]);
254 head_extents_[key].set_empty ();
255 for (vsize i = 0; i < head_boxes.size (); i++)
257 head_extents_[key].unite (head_boxes[i]);
261 void
262 Tie_formatting_problem::set_chord_outline (vector<Item*> bounds,
263 Direction dir)
266 vector<int> ranks;
267 for (vsize i = 0; i < bounds.size (); i++)
268 ranks.push_back (bounds[i]->get_column ()->get_rank ());
270 vector_sort (ranks, less<int> ());
271 uniq (ranks);
273 for (vsize i = 0; i < ranks.size (); i++)
275 vector<Item*> col_items;
276 for (vsize j = 0; j < bounds.size (); j ++)
278 if (bounds[j]->get_column ()->get_rank () == ranks[i])
279 col_items.push_back (bounds[j]);
282 set_column_chord_outline (col_items, dir, ranks[i]);
288 void
289 Tie_formatting_problem::from_tie (Grob *tie)
291 vector<Grob*> ties;
292 ties.push_back (tie);
293 from_ties (ties);
295 details_.from_grob (tie);
298 Grob *
299 Tie_formatting_problem::common_x_refpoint () const
301 return x_refpoint_;
304 void
305 Tie_formatting_problem::from_ties (vector<Grob*> const &ties)
307 if (ties.empty ())
308 return;
310 x_refpoint_ = ties[0];
311 y_refpoint_ = ties[0];
312 for (vsize i = 0; i < ties.size (); i++)
314 Spanner *tie = dynamic_cast<Spanner*> (ties[i]);
315 Item *l = tie->get_bound (LEFT);
316 Item *r = tie->get_bound (RIGHT);
318 x_refpoint_ = l->common_refpoint (x_refpoint_, X_AXIS);
319 x_refpoint_ = r->common_refpoint (x_refpoint_, X_AXIS);
321 if (!l->break_status_dir ())
322 y_refpoint_ = l->common_refpoint (y_refpoint_, Y_AXIS);
323 if (!r->break_status_dir ())
324 y_refpoint_ = r->common_refpoint (y_refpoint_, Y_AXIS);
327 details_.from_grob (ties[0]);
329 Direction d = LEFT;
332 vector<Item*> bounds;
334 for (vsize i = 0; i < ties.size (); i++)
336 Item *it = dynamic_cast<Spanner*> (ties[i])->get_bound (d);
337 if (it->break_status_dir ())
338 it = it->get_column ();
340 bounds.push_back (it);
343 set_chord_outline (bounds, d);
345 while (flip (&d) != LEFT);
348 for (vsize i = 0; i < ties.size (); i++)
350 Tie_specification spec;
351 spec.from_grob (ties[i]);
355 spec.note_head_drul_[d] = Tie::head (ties[i], d);
356 spec.column_ranks_[d] = Tie::get_column_rank (ties[i], d);
358 while (flip (&d) != LEFT);
359 specifications_.push_back (spec);
363 void
364 Tie_formatting_problem::from_semi_ties (vector<Grob*> const &semi_ties, Direction head_dir)
366 if (semi_ties.empty ())
367 return;
369 use_horizontal_spacing_ = false;
370 details_.from_grob (semi_ties[0]);
371 vector<Item*> heads;
373 int column_rank = -1;
374 for (vsize i = 0; i < semi_ties.size (); i++)
376 Tie_specification spec;
377 Item *head = unsmob_item (semi_ties[i]->get_object ("note-head"));
379 if (!head)
380 programming_error ("LV tie without head?!");
382 if (head)
384 spec.position_ = int (Staff_symbol_referencer::get_position (head));
387 spec.from_grob (semi_ties[i]);
389 spec.note_head_drul_[head_dir] = head;
390 column_rank = Tie::get_column_rank (semi_ties[i], head_dir);
391 spec.column_ranks_ = Drul_array<int> (column_rank, column_rank);
392 heads.push_back (head);
393 specifications_.push_back (spec);
396 x_refpoint_ = semi_ties[0];
397 y_refpoint_ = semi_ties[0];
399 for (vsize i = 0; i < semi_ties.size (); i++)
401 x_refpoint_ = semi_ties[i]->common_refpoint (x_refpoint_, X_AXIS);
402 y_refpoint_ = semi_ties[i]->common_refpoint (y_refpoint_, Y_AXIS);
404 for (vsize i = 0; i < heads.size (); i++)
406 x_refpoint_ = heads[i]->common_refpoint (x_refpoint_, X_AXIS);
407 y_refpoint_ = heads[i]->common_refpoint (y_refpoint_, Y_AXIS) ;
410 set_chord_outline (heads, head_dir);
412 Tuple2<int> head_key (column_rank, head_dir);
413 Tuple2<int> open_key (column_rank, -head_dir);
414 Real extremal = chord_outlines_[head_key].max_height ();
416 chord_outlines_[open_key] = Skyline (head_dir);
417 chord_outlines_[open_key].set_minimum_height (extremal - head_dir * 1.5);
421 Tie_specification
422 Tie_formatting_problem::get_tie_specification (int i) const
424 return specifications_[i];
429 Return configuration, create it if necessary.
431 Tie_configuration*
432 Tie_formatting_problem::get_configuration (int pos, Direction dir, Drul_array<int> columns,
433 bool tune_dy) const
435 int key_components[] = {
436 pos, dir, columns[LEFT], columns[RIGHT]
438 Tuple<int,4> key (key_components);
440 Tie_configuration_map::const_iterator f = possibilities_.find (key);
441 if (f != possibilities_.end ())
443 return (*f).second;
447 Tie_configuration *conf = generate_configuration (pos, dir, columns, tune_dy);
448 ((Tie_formatting_problem*) this)->possibilities_[key] = conf;
449 return conf;
452 Tie_configuration*
453 Tie_formatting_problem::generate_configuration (int pos, Direction dir,
454 Drul_array<int> columns, bool y_tune) const
456 Tie_configuration *conf = new Tie_configuration;
457 conf->position_ = pos;
458 conf->dir_ = dir;
460 conf->column_ranks_ = columns;
462 Real y = conf->position_ * 0.5 * details_.staff_space_;
464 if (dot_positions_.find (pos) != dot_positions_.end ())
466 conf->delta_y_ += dir * 0.25 * details_.staff_space_;
467 y_tune = false;
470 if (y_tune
471 && max (fabs (get_head_extent (columns[LEFT], LEFT, Y_AXIS)[dir] - y),
472 fabs (get_head_extent (columns[RIGHT], RIGHT, Y_AXIS)[dir] - y)) < 0.25
473 && !Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos))
475 conf->delta_y_ =
476 (get_head_extent (columns[LEFT], LEFT, Y_AXIS)[dir] - y)
477 + dir * details_.outer_tie_vertical_gap_;
480 if (y_tune)
482 conf->attachment_x_ = get_attachment (y + conf->delta_y_, conf->column_ranks_);
483 Real h = conf->height (details_);
486 TODO:
488 - should make sliding criterion, should flatten ties if
490 - they're just the wrong (ie. touching line at top & bottom)
491 size.
494 if (head_positions_slice (columns[LEFT]).contains (pos)
495 || head_positions_slice (columns[RIGHT]).contains (pos)
496 || abs (pos) < 2 * Staff_symbol_referencer::staff_radius (details_.staff_symbol_referencer_))
498 if (h < details_.intra_space_threshold_ * 0.5 * details_.staff_space_)
500 if (!Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos)
501 && abs (pos) < 2 * Staff_symbol_referencer::staff_radius (details_.staff_symbol_referencer_))
503 conf->center_tie_vertically (details_);
505 else if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos))
507 conf->delta_y_ += dir *
508 details_.tip_staff_line_clearance_ * 0.5 * details_.staff_space_;
511 else
513 Real top_y = y + conf->delta_y_ + conf->dir_ * h;
514 Real top_pos = top_y / (0.5*details_.staff_space_);
515 int round_pos = int (my_round (top_pos));
517 /* TODO: should use other variable? */
518 Real clearance = details_.center_staff_line_clearance_;
519 if (fabs (top_pos - round_pos) < clearance
520 && Staff_symbol_referencer::on_staff_line (details_.staff_symbol_referencer_,
521 round_pos))
523 Real new_y = (round_pos + clearance * conf->dir_) * 0.5 * details_.staff_space_;
524 conf->delta_y_ = (new_y - top_y);
529 conf->attachment_x_ = get_attachment (y + conf->delta_y_, conf->column_ranks_);
530 if (conf->height (details_) < details_.intra_space_threshold_ * 0.5 * details_.staff_space_)
533 This is less sensible for long ties, since those are more
534 horizontal.
536 Interval close_by = get_attachment (y
537 + conf->delta_y_
538 + (dir * details_.intra_space_threshold_ * 0.25
539 * details_.staff_space_),
540 conf->column_ranks_);
542 conf->attachment_x_.intersect (close_by);
545 conf->attachment_x_.widen ( - details_.x_gap_);
547 if (conf->column_span_length ())
550 avoid the stems that we attach to as well. We don't do this
551 for semities (span length = 0)
553 It would be better to check D against HEAD-DIRECTION if
554 applicable.
556 Direction d = LEFT;
559 Real y = conf->position_ * details_.staff_space_ * 0.5 + conf->delta_y_;
560 if (get_stem_extent (conf->column_ranks_[d], d, X_AXIS).is_empty ()
561 || !get_stem_extent (conf->column_ranks_[d], d, Y_AXIS).contains (y))
562 continue;
564 conf->attachment_x_[d] =
565 d * min (d * conf->attachment_x_[d],
566 d * (get_stem_extent (conf->column_ranks_[d], d, X_AXIS)[-d] - d * details_.stem_gap_));
568 while (flip (&d) != LEFT);
570 return conf;
573 Interval
574 Tie_formatting_problem::get_head_extent (int col, Direction d, Axis a) const
576 Column_extent_map::const_iterator i = head_extents_.find (Tuple2<int> (col, int (d)));
577 if (i != head_extents_.end ())
578 return (*i).second[a];
579 else
580 return Interval ();
583 Interval
584 Tie_formatting_problem::get_stem_extent (int col, Direction d, Axis a) const
586 Column_extent_map::const_iterator i = stem_extents_.find (Tuple2<int> (col, int (d)));
587 if (i != stem_extents_.end ())
588 return (*i).second[a];
589 else
590 return Interval ();
594 TIE_IDX and TIES_CONF are optional.
596 Real
597 Tie_formatting_problem::score_aptitude (Tie_configuration *conf,
598 Tie_specification const &spec,
599 Ties_configuration *ties_conf, int tie_idx) const
601 Real penalty = 0.0;
602 Real curve_y = conf->position_ * details_.staff_space_ * 0.5 + conf->delta_y_;
603 Real tie_y = spec.position_ * details_.staff_space_ * 0.5;
604 if (sign (curve_y - tie_y) != conf->dir_)
606 Real p = details_.wrong_direction_offset_penalty_;
607 if (ties_conf)
608 ties_conf->add_tie_score (p, tie_idx, "wrong dir");
609 else
610 penalty += p;
614 Real relevant_dist = max (fabs (curve_y - tie_y) - 0.5, 0.0);
615 Real p = details_.vertical_distance_penalty_factor_ * convex_amplifier (1.0, 0.9, relevant_dist);
616 if (ties_conf)
617 ties_conf->add_tie_score (p, tie_idx, "vdist");
618 else
619 penalty += p;
622 Direction d = LEFT;
625 if (!spec.note_head_drul_[d])
626 continue;
628 Interval head_x = spec.note_head_drul_[d]->extent (x_refpoint_, X_AXIS);
629 Real dist = head_x.distance (conf->attachment_x_[d]);
633 TODO: flatten with log or sqrt.
635 Real p = details_.horizontal_distance_penalty_factor_
636 * convex_amplifier (1.25, 1.0, dist);
637 if (ties_conf)
638 ties_conf->add_tie_score (p, tie_idx,
639 (d == LEFT) ? "lhdist" : "rhdist");
640 else
641 penalty += p;
644 while (flip (&d) != LEFT);
646 if (ties_conf
647 && ties_conf->size () == 1)
649 Direction d = LEFT;
650 Drul_array<Grob*> stems (0, 0);
653 if (!spec.note_head_drul_[d])
654 continue;
656 Grob *stem = unsmob_grob (spec.note_head_drul_[d]->get_object ("stem"));
657 if (stem
658 && Stem::is_normal_stem (stem))
659 stems[d] = stem;
661 while (flip (&d) != LEFT);
663 bool tie_stem_dir_ok = true;
664 bool tie_position_dir_ok = true;
665 if (stems[LEFT] && !stems[RIGHT])
666 tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[LEFT]);
667 else if (!stems[LEFT] && stems[RIGHT])
668 tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[RIGHT]);
669 else if (stems[LEFT] && stems[RIGHT]
670 && get_grob_direction (stems[LEFT]) == get_grob_direction (stems[RIGHT]))
671 tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[LEFT]);
672 else if (spec.position_)
673 tie_position_dir_ok = conf->dir_ == sign (spec.position_);
675 if (!tie_stem_dir_ok)
676 ties_conf->add_score (details_.same_dir_as_stem_penalty_, "tie/stem dir");
677 if (!tie_position_dir_ok)
678 ties_conf->add_score (details_.same_dir_as_stem_penalty_, "tie/pos dir");
681 return penalty;
685 Slice
686 Tie_formatting_problem::head_positions_slice (int rank) const
688 Position_extent_map::const_iterator i (head_positions_.find (rank));
689 if (i != head_positions_.end ())
691 return (*i).second;
693 Slice empty;
694 return empty;
698 Score a configuration, ie. how well these ties looks without regard
699 to the note heads that they should connect to.
701 void
702 Tie_formatting_problem::score_configuration (Tie_configuration *conf) const
704 if (conf->scored_)
706 return ;
709 Real length = conf->attachment_x_.length ();
711 Real length_penalty
712 = peak_around (0.33 * details_.min_length_, details_.min_length_, length);
713 conf->add_score (details_.min_length_penalty_factor_
714 * length_penalty, "minlength");
716 Real tip_pos = conf->position_ + conf->delta_y_ / 0.5 * details_.staff_space_;
717 Real tip_y = tip_pos * details_.staff_space_ * 0.5;
718 Real height = conf->height (details_);
720 Real top_y = tip_y + conf->dir_ * height;
721 Real top_pos = 2 * top_y / details_.staff_space_;
722 Real round_top_pos = rint (top_pos);
723 if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_,
724 int (round_top_pos))
725 && Staff_symbol_referencer::staff_radius (details_.staff_symbol_referencer_) > top_y)
727 conf->add_score (
728 details_.staff_line_collision_penalty_
729 * peak_around (0.1 * details_.center_staff_line_clearance_,
730 details_.center_staff_line_clearance_,
731 fabs (top_pos - round_top_pos)),
732 "line center");
735 int rounded_tip_pos = int (rint (tip_pos));
736 if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, rounded_tip_pos)
737 && (head_positions_slice (conf->column_ranks_[LEFT]).contains (rounded_tip_pos)
738 || head_positions_slice (conf->column_ranks_[RIGHT]).contains (rounded_tip_pos)
739 || abs (rounded_tip_pos) < 2 * Staff_symbol_referencer::staff_radius (details_.staff_symbol_referencer_))
742 conf->add_score (details_.staff_line_collision_penalty_
743 * peak_around (0.1 * details_.tip_staff_line_clearance_,
744 details_.tip_staff_line_clearance_,
745 fabs (tip_pos - rint (tip_pos))),
746 "tipline");
749 if (!dot_x_.is_empty ())
751 /* use left edge? */
752 Real x = dot_x_.center ();
754 Bezier b = conf->get_transformed_bezier (details_);
755 if (b.control_point_extent (X_AXIS).contains (x))
757 Real y = b.get_other_coordinate (X_AXIS, x);
759 for (set<int>::const_iterator i (dot_positions_.begin ());
760 i != dot_positions_.end (); i ++)
762 int dot_pos = (*i);
763 conf->add_score (details_.dot_collision_penalty_
764 * peak_around (.1 * details_.dot_collision_clearance_,
765 details_.dot_collision_clearance_,
766 fabs (dot_pos * details_.staff_space_ * 0.5 - y)),
767 "dot collision");
772 conf->scored_ = true;
775 void
776 Tie_formatting_problem::score_ties_aptitude (Ties_configuration *ties) const
778 if (ties->size () != specifications_.size ())
780 programming_error ("Huh? Mismatch between sizes.");
781 return;
784 for (vsize i = 0; i < ties->size (); i++)
785 score_aptitude (&ties->at (i), specifications_[i],
786 ties, i);
789 void
790 Tie_formatting_problem::score_ties (Ties_configuration *ties) const
792 if (ties->scored_)
793 return;
795 score_ties_configuration (ties);
796 score_ties_aptitude (ties);
797 ties->scored_ = true;
800 void
801 Tie_formatting_problem::score_ties_configuration (Ties_configuration *ties) const
803 for (vsize i = 0; i < ties->size (); i++)
805 score_configuration (&ties->at (i));
806 ties->add_tie_score (ties->at (i).score (), i, "conf");
809 Real last_edge = 0.0;
810 Real last_center = 0.0;
811 for (vsize i = 0; i < ties->size (); i++)
813 Bezier b (ties->at (i).get_transformed_bezier (details_));
815 Real center = b.curve_point (0.5)[Y_AXIS];
816 Real edge = b.curve_point (0.0)[Y_AXIS];
818 if (i)
820 if (edge <= last_edge)
821 ties->add_score (details_.tie_column_monotonicity_penalty_, "monoton edge");
822 if (center <= last_center)
823 ties->add_score (details_.tie_column_monotonicity_penalty_, "monoton cent");
825 ties->add_score (details_.tie_tie_collision_penalty_ *
826 peak_around (0.1 * details_.tie_tie_collision_distance_,
827 details_.tie_tie_collision_distance_,
828 fabs (center - last_center)),
829 "tietie center");
830 ties->add_score (details_.tie_tie_collision_penalty_ *
831 peak_around (0.1 * details_.tie_tie_collision_distance_,
832 details_.tie_tie_collision_distance_,
833 fabs (edge - last_edge)), "tietie edge");
836 last_edge = edge;
837 last_center = center;
840 if (ties->size () > 1)
842 ties->add_score (details_.outer_tie_length_symmetry_penalty_factor_
843 * fabs (ties->at (0).attachment_x_.length () - ties->back ().attachment_x_.length ()),
844 "length symm");
846 ties->add_score (details_.outer_tie_vertical_distance_symmetry_penalty_factor_
847 * fabs (fabs (specifications_[0].position_ * 0.5 * details_.staff_space_
848 - (ties->at (0).position_ * 0.5 * details_.staff_space_
849 + ties->at (0).delta_y_))
851 fabs (specifications_.back ().position_ * 0.5 * details_.staff_space_
852 - (ties->back ().position_ * 0.5 * details_.staff_space_
853 + ties->back ().delta_y_))),
854 "pos symmetry");
859 Generate with correct X-attachments and beziers, copying delta_y_
860 from TIES_CONFIG if necessary.
862 Ties_configuration
863 Tie_formatting_problem::generate_ties_configuration (Ties_configuration const &ties_config)
865 Ties_configuration copy;
866 for (vsize i = 0; i < ties_config.size (); i++)
868 Tie_configuration * ptr = get_configuration (ties_config[i].position_, ties_config[i].dir_,
869 ties_config[i].column_ranks_,
870 !specifications_[i].has_manual_delta_y_);
871 if (specifications_[i].has_manual_delta_y_)
873 ptr->delta_y_
874 = (specifications_[i].manual_position_ - ties_config[i].position_)
875 * 0.5 * details_.staff_space_;
877 copy.push_back (*ptr);
880 return copy;
883 Ties_configuration
884 Tie_formatting_problem::generate_base_chord_configuration ()
886 Ties_configuration ties_config;
887 for (vsize i = 0; i < specifications_.size (); i ++)
889 Tie_configuration conf;
890 if (specifications_[i].has_manual_dir_)
891 conf.dir_ = specifications_[i].manual_dir_;
892 if (specifications_[i].has_manual_position_)
894 conf.position_ = (int) my_round (specifications_[i].manual_position_);
895 if (specifications_[i].has_manual_delta_y_)
896 conf.delta_y_ = (specifications_[i].manual_position_ - conf.position_)
897 * 0.5 * details_.staff_space_;
899 else
901 conf.position_ = specifications_[i].position_;
904 conf.column_ranks_ = specifications_[i].column_ranks_;
905 ties_config.push_back (conf);
908 set_ties_config_standard_directions (&ties_config);
909 for (vsize i = 0; i < ties_config.size (); i++)
910 if (!specifications_[i].manual_position_)
911 ties_config[i].position_ += ties_config[i].dir_;
913 ties_config = generate_ties_configuration (ties_config);
915 return ties_config;
918 Ties_configuration
919 Tie_formatting_problem::find_best_variation (Ties_configuration const &base,
920 vector<Tie_configuration_variation> const &vars)
922 Ties_configuration best = base;
925 This simply is 1-opt: we have K substitions, and we try applying
926 exactly every one for each.
928 for (vsize i = 0; i < vars.size (); i++)
930 Ties_configuration variant (base);
931 for (vsize j = 0; j < vars[i].index_suggestion_pairs_.size(); j++)
932 variant[vars[i].index_suggestion_pairs_[j].first] = *vars[i].index_suggestion_pairs_[j].second;
934 variant.reset_score ();
935 score_ties (&variant);
937 if (variant.score () < best.score ())
939 best = variant;
943 return best;
948 Ties_configuration
949 Tie_formatting_problem::generate_optimal_configuration ()
951 Ties_configuration base = generate_base_chord_configuration ();
952 score_ties (&base);
954 vector<Tie_configuration_variation> vars;
955 if (specifications_.size () > 1)
956 vars = generate_collision_variations (base);
957 else
958 vars = generate_single_tie_variations (base);
960 Ties_configuration best = find_best_variation (base, vars);
962 if (specifications_.size () > 1)
964 vars = generate_extremal_tie_variations (best);
965 best = find_best_variation (best, vars);
967 return best;
970 void
971 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration *tie_configs)
973 if (tie_configs->empty ())
974 return ;
976 if (!tie_configs->at (0).dir_)
978 if (tie_configs->size () == 1)
979 tie_configs->at (0).dir_ = Direction (sign (tie_configs->at (0).position_));
981 if (!tie_configs->at (0).dir_)
982 tie_configs->at (0).dir_
983 = (tie_configs->size() > 1) ? DOWN : details_.neutral_direction_;
986 if (!tie_configs->back ().dir_)
987 tie_configs->back ().dir_ = UP;
990 Seconds
992 for (vsize i = 1; i < tie_configs->size (); i++)
994 Real diff = (tie_configs->at (i).position_
995 -tie_configs->at (i-1).position_);
997 Real span_diff
998 = specifications_[i].column_span () - specifications_[i-1].column_span ();
999 if (span_diff && fabs (diff) <= 2)
1001 if (span_diff > 0)
1002 tie_configs->at (i).dir_ = UP;
1003 else if (span_diff < 0)
1004 tie_configs->at (i-1).dir_ = DOWN;
1006 else if (fabs (diff) <= 1)
1008 if (!tie_configs->at (i-1).dir_)
1009 tie_configs->at (i-1).dir_ = DOWN;
1010 if (!tie_configs->at (i).dir_)
1011 tie_configs->at (i).dir_ = UP;
1015 for (vsize i = 1; i + 1 < tie_configs->size (); i++)
1017 Tie_configuration &conf = tie_configs->at (i);
1018 if (conf.dir_)
1019 continue;
1021 Direction position_dir =
1022 Direction (sign (conf.position_));
1023 if (!position_dir)
1024 position_dir = DOWN;
1026 conf.dir_ = position_dir;
1030 vector<Tie_configuration_variation>
1031 Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration const &ties) const
1033 vector<Tie_configuration_variation> vars;
1034 Direction d = DOWN;
1035 for (int i = 1; i <= details_.multi_tie_region_size_; i++)
1037 Drul_array<Tie_configuration*> configs (0, 0);
1040 const Tie_configuration &config = boundary (ties, d, 0);
1041 if (config.dir_ == d
1042 && !boundary (specifications_, d, 0).has_manual_position_)
1044 Tie_configuration_variation var;
1045 configs[d] = get_configuration (config.position_ + d * i, d,
1046 config.column_ranks_,
1047 true);
1048 var.add_suggestion((d == DOWN) ? 0 : ties.size () - 1,
1049 configs[d]);
1050 vars.push_back (var);
1053 while (flip (&d) != DOWN);
1054 if (configs[LEFT] && configs[RIGHT])
1056 Tie_configuration_variation var;
1057 var.add_suggestion(0, configs[DOWN]);
1058 var.add_suggestion(ties.size() - 1, configs[UP]);
1059 vars.push_back (var);
1063 return vars;
1066 vector<Tie_configuration_variation>
1067 Tie_formatting_problem::generate_single_tie_variations (Ties_configuration const &ties) const
1069 vector<Tie_configuration_variation> vars;
1071 int sz = details_.single_tie_region_size_;
1072 if (specifications_[0].has_manual_position_)
1073 sz = 1;
1074 for (int i = 0; i < sz; i ++)
1076 Direction d = LEFT;
1079 if (i == 0
1080 && ties[0].dir_ == d)
1081 continue;
1083 int p = ties[0].position_ + i * d;
1085 if (!specifications_[0].has_manual_dir_
1086 || d == specifications_[0].manual_dir_)
1088 Tie_configuration_variation var;
1089 var.add_suggestion(0,
1090 get_configuration (p,
1091 d, specifications_[0].column_ranks_,
1092 !specifications_[0].has_manual_delta_y_));
1093 vars.push_back (var);
1096 while (flip (&d) != LEFT);
1098 return vars;
1102 vector<Tie_configuration_variation>
1103 Tie_formatting_problem::generate_collision_variations (Ties_configuration const &ties) const
1105 Real center_distance_tolerance = 0.25;
1107 vector<Tie_configuration_variation> vars;
1108 Real last_center = 0.0;
1109 for (vsize i = 0; i < ties.size (); i++)
1111 Bezier b (ties[i].get_transformed_bezier (details_));
1113 Real center = b.curve_point (0.5)[Y_AXIS];
1115 if (i)
1117 if (center <= last_center + center_distance_tolerance)
1119 if (!specifications_[i].has_manual_dir_)
1121 Tie_configuration_variation var;
1122 var.add_suggestion(i,
1123 get_configuration (specifications_[i].position_
1124 - ties[i].dir_,
1125 - ties[i].dir_,
1127 ties[i].column_ranks_,
1128 !specifications_[i].has_manual_delta_y_
1131 vars.push_back (var);
1134 if (!specifications_[i-1].has_manual_dir_)
1136 Tie_configuration_variation var;
1137 var.add_suggestion(i-1,
1138 get_configuration (specifications_[i-1].position_
1139 - ties[i-1].dir_,
1140 - ties[i-1].dir_,
1141 specifications_[i-1].column_ranks_,
1142 !specifications_[i-1].has_manual_delta_y_));
1144 vars.push_back (var);
1147 if (i == 1 && !specifications_[i-1].has_manual_position_
1148 && ties[i-1].dir_ == DOWN)
1150 Tie_configuration_variation var;
1151 var.add_suggestion(i-1,
1152 get_configuration (specifications_[i-1].position_ - 1, DOWN,
1153 specifications_[i-1].column_ranks_,
1154 !specifications_[i-1].has_manual_delta_y_
1156 vars.push_back (var);
1158 if (i == ties.size () && !specifications_[i].has_manual_position_
1159 && ties[i].dir_ == UP)
1161 Tie_configuration_variation var;
1162 var.add_suggestion(i,
1163 get_configuration (specifications_[i].position_
1164 + 1, UP,
1165 specifications_[i].column_ranks_,
1166 !specifications_[i].has_manual_delta_y_
1168 vars.push_back (var);
1171 else if (dot_positions_.find (ties[i].position_) != dot_positions_.end ()
1172 && !specifications_[i].has_manual_position_)
1174 Tie_configuration_variation var;
1175 var.add_suggestion(i,
1176 get_configuration (ties[i].position_ + ties[i].dir_,
1177 ties[i].dir_,
1178 ties[i].column_ranks_,
1179 !specifications_[i].has_manual_delta_y_
1181 vars.push_back (var);
1186 last_center = center;
1190 return vars;
1193 void
1194 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs)
1196 vsize k = 0;
1197 for (SCM s = manual_configs;
1198 scm_is_pair (s) && k < specifications_.size (); s = scm_cdr (s))
1200 SCM entry = scm_car (s);
1201 if (scm_is_pair (entry))
1203 Tie_specification &spec = specifications_[k];
1205 if (scm_is_number (scm_car (entry)))
1207 spec.has_manual_position_ = true;
1208 spec.manual_position_ = scm_to_double (scm_car (entry));
1209 spec.has_manual_delta_y_ = (scm_inexact_p (scm_car (entry)) == SCM_BOOL_T);
1212 if (scm_is_number (scm_cdr (entry)))
1214 spec.has_manual_dir_ = true;
1215 spec.manual_dir_ = Direction (scm_to_int (scm_cdr (entry)));
1218 k ++;
1223 void
1224 Tie_formatting_problem::set_debug_scoring (Ties_configuration const &base)
1226 #if DEBUG_TIE_SCORING
1227 if (to_boolean (x_refpoint_->layout ()
1228 ->lookup_variable (ly_symbol2scm ("debug-tie-scoring"))))
1230 for (vsize i = 0; i < base.size (); i++)
1232 string card = base.complete_tie_card (i);
1233 specifications_[i].tie_grob_->set_property ("quant-score",
1234 ly_string2scm (card));
1237 #endif