2 tie-formatting-problem.cc -- implement Tie_formatting_problem
4 source file of the GNU LilyPond music typesetter
6 (c) 2005--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "tie-formatting-problem.hh"
12 #include "axis-group-interface.hh"
13 #include "paper-column.hh"
15 #include "directional-element-interface.hh"
16 #include "libc-extension.hh"
18 #include "note-head.hh"
19 #include "rhythmic-head.hh"
21 #include "staff-symbol-referencer.hh"
23 #include "tie-configuration.hh"
26 #include "pointer-group-interface.hh"
27 #include "output-def.hh"
30 Tie_formatting_problem::print_ties_configuration (Ties_configuration
const *ties
)
32 for (vsize i
= 0; i
< ties
->size (); i
++)
34 char const *man_pos
= (specifications_
[i
].has_manual_position_
) ? "(M)" : "";
35 char const *man_dir
= (specifications_
[i
].has_manual_dir_
) ? "(M)" : "";
36 char const *dir
= (ties
->at (i
).dir_
== UP
) ? "up" : "dn";
38 printf ("(P%d%s, %s%s) ", ties
->at (i
).position_
, man_pos
, dir
, man_dir
);
44 Tie_formatting_problem::get_attachment (Real y
, Drul_array
<int> columns
) const
46 Interval
attachments (0,0);
50 Tuple2
<int> key (columns
[d
], int (d
));
51 Chord_outline_map::const_iterator
i (chord_outlines_
.find (key
));
52 if (i
== chord_outlines_
.end ())
53 programming_error ("Cannot find chord outline");
55 attachments
[d
] = i
->second
.height (y
);
57 while (flip (&d
) != LEFT
);
62 Tie_formatting_problem::Tie_formatting_problem ()
66 use_horizontal_spacing_
= true;
69 Tie_formatting_problem::~Tie_formatting_problem ()
71 for (Tie_configuration_map::const_iterator
i (possibilities_
.begin ());
72 i
!= possibilities_
.end (); i
++)
77 Tie_formatting_problem::set_column_chord_outline (vector
<Item
*> bounds
,
81 Real staff_space
= Staff_symbol_referencer::staff_space (bounds
[0]);
84 vector
<Box
> head_boxes
;
87 for (vsize i
= 0; i
< bounds
.size (); i
++)
89 Grob
*head
= bounds
[i
];
90 if (!Note_head::has_interface (head
))
94 stem
= unsmob_grob (head
->get_object ("stem"));
96 Real p
= Staff_symbol_referencer::get_position (head
);
97 Interval
y ((p
-1) * 0.5 * staff_space
,
98 (p
+1) * 0.5 * staff_space
);
100 Interval x
= head
->extent (x_refpoint_
, X_AXIS
);
101 head_boxes
.push_back (Box (x
, y
));
102 boxes
.push_back (Box (x
, y
));
104 Grob
*dots
= Rhythmic_head::get_dots (head
);
105 if (dir
== LEFT
&& dots
)
107 Interval x
= dots
->extent (x_refpoint_
, X_AXIS
);
108 int p
= int (Staff_symbol_referencer::get_position (dots
));
111 TODO: shouldn't this use column-rank dependent key?
113 dot_positions_
.insert (p
);
116 Interval
y (dots
->extent (dots
, Y_AXIS
));
117 y
.translate (p
* staff_space
* 0.5);
119 boxes
.push_back (Box (x
, y
));
123 Tuple2
<int> key (column_rank
, int (dir
));
127 if (Stem::is_normal_stem (stem
))
130 x
.add_point (stem
->relative_coordinate (x_refpoint_
, X_AXIS
));
131 x
.widen (staff_space
/ 20); // ugh.
134 Real stem_end_position
= 0.0;
135 if (Stem::is_cross_staff (stem
))
136 stem_end_position
= get_grob_direction (stem
) * infinity_f
;
139 if (use_horizontal_spacing_
|| !Stem::get_beam (stem
))
140 stem_end_position
= Stem::stem_end_position (stem
) * staff_space
* .5;
142 stem_end_position
= Stem::note_head_positions (stem
)[get_grob_direction (stem
)]
146 y
.add_point (stem_end_position
);
148 Direction stemdir
= get_grob_direction (stem
);
149 y
.add_point (Stem::head_positions (stem
)[-stemdir
]
155 boxes
.push_back (Box (x
, y
));
157 stem_extents_
[key
].unite (Box (x
, y
));
161 Box flag_box
= Stem::get_translated_flag (stem
).extent_box ();
162 flag_box
.translate ( Offset (x
[RIGHT
], X_AXIS
));
163 boxes
.push_back (flag_box
);
168 Grob
*head
= Stem::support_head (stem
);
171 In case of invisible stem, don't pass x-center of heads.
173 Real x_center
= head
->extent (x_refpoint_
, X_AXIS
).center ();
175 x_ext
[-dir
] = x_center
;
176 x_ext
[dir
] = infinity_f
* dir
;
178 for (vsize j
= 0; j
< head_boxes
.size (); j
++)
179 y_ext
.unite (head_boxes
[j
][Y_AXIS
]);
181 boxes
.push_back (Box (x_ext
, y_ext
));
184 extract_grob_set (stem
, "note-heads", heads
);
185 for (vsize i
= 0; i
< heads
.size (); i
++)
187 if (find (bounds
.begin (), bounds
.end (), dynamic_cast<Item
*> (heads
[i
])) == bounds
.end ())
190 other untied notes in the same chord.
193 Interval y
= Staff_symbol_referencer::extent_in_staff (heads
[i
]);
194 Interval x
= heads
[i
]->extent (x_refpoint_
, X_AXIS
);
195 boxes
.push_back (Box (x
, y
));
198 Grob
*acc
= unsmob_grob (heads
[i
]->get_object ("accidental-grob"));
200 acc
->get_property ("stencil"); /* trigger tie-related suicide */
202 if (acc
&& acc
->is_live () && dir
== RIGHT
)
204 boxes
.push_back (Box (acc
->extent (x_refpoint_
, X_AXIS
),
205 Staff_symbol_referencer::extent_in_staff (acc
)));
208 head_positions_
[column_rank
].add_point (int (Staff_symbol_referencer::get_position (heads
[i
])));
213 Direction updowndir
= DOWN
;
218 if (head_boxes
.size ())
220 Box b
= boundary (head_boxes
, updowndir
, 0);
222 x
[-dir
] = b
[X_AXIS
].linear_combination (-dir
/ 2);
223 y
[-updowndir
] = b
[Y_AXIS
][updowndir
];
224 y
[updowndir
] = updowndir
* infinity_f
;
228 boxes
.push_back (Box (x
, y
));
230 while (flip (&updowndir
) != DOWN
);
232 /* todo: the horizon_padding is somewhat arbitrary */
233 chord_outlines_
[key
] = Skyline (boxes
, details_
.skyline_padding_
, Y_AXIS
, -dir
);
234 if (bounds
[0]->break_status_dir ())
236 Interval
iv (Axis_group_interface::staff_extent (bounds
[0], x_refpoint_
, X_AXIS
, y_refpoint_
, Y_AXIS
));
238 iv
.add_point (bounds
[0]->relative_coordinate (x_refpoint_
, X_AXIS
));
240 chord_outlines_
[key
].set_minimum_height (iv
[-dir
]);
245 for (vsize j
= 0; j
< head_boxes
.size (); j
++)
247 x
.unite (head_boxes
[j
][X_AXIS
]);
250 chord_outlines_
[key
].set_minimum_height (x
[dir
]);
253 head_extents_
[key
].set_empty ();
254 for (vsize i
= 0; i
< head_boxes
.size (); i
++)
256 head_extents_
[key
].unite (head_boxes
[i
]);
261 Tie_formatting_problem::set_chord_outline (vector
<Item
*> bounds
,
266 for (vsize i
= 0; i
< bounds
.size (); i
++)
267 ranks
.push_back (bounds
[i
]->get_column ()->get_rank ());
269 vector_sort (ranks
, less
<int> ());
272 for (vsize i
= 0; i
< ranks
.size (); i
++)
274 vector
<Item
*> col_items
;
275 for (vsize j
= 0; j
< bounds
.size (); j
++)
277 if (bounds
[j
]->get_column ()->get_rank () == ranks
[i
])
278 col_items
.push_back (bounds
[j
]);
281 set_column_chord_outline (col_items
, dir
, ranks
[i
]);
288 Tie_formatting_problem::from_tie (Grob
*tie
)
291 ties
.push_back (tie
);
294 details_
.from_grob (tie
);
298 Tie_formatting_problem::common_x_refpoint () const
304 Tie_formatting_problem::from_ties (vector
<Grob
*> const &ties
)
309 x_refpoint_
= ties
[0];
310 y_refpoint_
= ties
[0];
311 for (vsize i
= 0; i
< ties
.size (); i
++)
313 Spanner
*tie
= dynamic_cast<Spanner
*> (ties
[i
]);
314 Item
*l
= tie
->get_bound (LEFT
);
315 Item
*r
= tie
->get_bound (RIGHT
);
317 x_refpoint_
= l
->common_refpoint (x_refpoint_
, X_AXIS
);
318 x_refpoint_
= r
->common_refpoint (x_refpoint_
, X_AXIS
);
320 if (!l
->break_status_dir ())
321 y_refpoint_
= l
->common_refpoint (y_refpoint_
, Y_AXIS
);
322 if (!r
->break_status_dir ())
323 y_refpoint_
= r
->common_refpoint (y_refpoint_
, Y_AXIS
);
326 details_
.from_grob (ties
[0]);
331 vector
<Item
*> bounds
;
333 for (vsize i
= 0; i
< ties
.size (); i
++)
335 Item
*it
= dynamic_cast<Spanner
*> (ties
[i
])->get_bound (d
);
336 if (it
->break_status_dir ())
337 it
= it
->get_column ();
339 bounds
.push_back (it
);
342 set_chord_outline (bounds
, d
);
344 while (flip (&d
) != LEFT
);
347 for (vsize i
= 0; i
< ties
.size (); i
++)
349 Tie_specification spec
;
350 spec
.from_grob (ties
[i
]);
354 spec
.note_head_drul_
[d
] = Tie::head (ties
[i
], d
);
355 spec
.column_ranks_
[d
] = Tie::get_column_rank (ties
[i
], d
);
357 while (flip (&d
) != LEFT
);
358 specifications_
.push_back (spec
);
363 Tie_formatting_problem::from_semi_ties (vector
<Grob
*> const &semi_ties
, Direction head_dir
)
365 if (semi_ties
.empty ())
368 use_horizontal_spacing_
= false;
369 details_
.from_grob (semi_ties
[0]);
372 int column_rank
= -1;
373 for (vsize i
= 0; i
< semi_ties
.size (); i
++)
375 Tie_specification spec
;
376 Item
*head
= unsmob_item (semi_ties
[i
]->get_object ("note-head"));
379 programming_error ("LV tie without head?!");
383 spec
.position_
= int (Staff_symbol_referencer::get_position (head
));
386 spec
.from_grob (semi_ties
[i
]);
388 spec
.note_head_drul_
[head_dir
] = head
;
389 column_rank
= Tie::get_column_rank (semi_ties
[i
], head_dir
);
390 spec
.column_ranks_
= Drul_array
<int> (column_rank
, column_rank
);
391 heads
.push_back (head
);
392 specifications_
.push_back (spec
);
395 x_refpoint_
= semi_ties
[0];
396 y_refpoint_
= semi_ties
[0];
398 for (vsize i
= 0; i
< semi_ties
.size (); i
++)
400 x_refpoint_
= semi_ties
[i
]->common_refpoint (x_refpoint_
, X_AXIS
);
401 y_refpoint_
= semi_ties
[i
]->common_refpoint (y_refpoint_
, Y_AXIS
);
403 for (vsize i
= 0; i
< heads
.size (); i
++)
405 x_refpoint_
= heads
[i
]->common_refpoint (x_refpoint_
, X_AXIS
);
406 y_refpoint_
= heads
[i
]->common_refpoint (y_refpoint_
, Y_AXIS
) ;
409 set_chord_outline (heads
, head_dir
);
411 Tuple2
<int> head_key (column_rank
, head_dir
);
412 Tuple2
<int> open_key (column_rank
, -head_dir
);
413 Real extremal
= chord_outlines_
[head_key
].max_height ();
415 chord_outlines_
[open_key
] = Skyline (head_dir
);
416 chord_outlines_
[open_key
].set_minimum_height (extremal
- head_dir
* 1.5);
421 Tie_formatting_problem::get_tie_specification (int i
) const
423 return specifications_
[i
];
428 Return configuration, create it if necessary.
431 Tie_formatting_problem::get_configuration (int pos
, Direction dir
, Drul_array
<int> columns
,
434 int key_components
[] = {
435 pos
, dir
, columns
[LEFT
], columns
[RIGHT
]
437 Tuple
<int,4> key (key_components
);
439 Tie_configuration_map::const_iterator f
= possibilities_
.find (key
);
440 if (f
!= possibilities_
.end ())
446 Tie_configuration
*conf
= generate_configuration (pos
, dir
, columns
, tune_dy
);
447 ((Tie_formatting_problem
*) this)->possibilities_
[key
] = conf
;
452 Tie_formatting_problem::generate_configuration (int pos
, Direction dir
,
453 Drul_array
<int> columns
, bool y_tune
) const
455 Tie_configuration
*conf
= new Tie_configuration
;
456 conf
->position_
= pos
;
459 conf
->column_ranks_
= columns
;
461 Real y
= conf
->position_
* 0.5 * details_
.staff_space_
;
463 if (dot_positions_
.find (pos
) != dot_positions_
.end ())
465 conf
->delta_y_
+= dir
* 0.25 * details_
.staff_space_
;
470 && max (fabs (get_head_extent (columns
[LEFT
], LEFT
, Y_AXIS
)[dir
] - y
),
471 fabs (get_head_extent (columns
[RIGHT
], RIGHT
, Y_AXIS
)[dir
] - y
)) < 0.25
472 && !Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
))
475 (get_head_extent (columns
[LEFT
], LEFT
, Y_AXIS
)[dir
] - y
)
476 + dir
* details_
.outer_tie_vertical_gap_
;
481 conf
->attachment_x_
= get_attachment (y
+ conf
->delta_y_
, conf
->column_ranks_
);
482 Real h
= conf
->height (details_
);
487 - should make sliding criterion, should flatten ties if
489 - they're just the wrong (ie. touching line at top & bottom)
493 if (head_positions_slice (columns
[LEFT
]).contains (pos
)
494 || head_positions_slice (columns
[RIGHT
]).contains (pos
)
495 || abs (pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
497 if (h
< details_
.intra_space_threshold_
* 0.5 * details_
.staff_space_
)
499 if (!Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
)
500 && abs (pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
502 conf
->center_tie_vertically (details_
);
504 else if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
))
506 conf
->delta_y_
+= dir
*
507 details_
.tip_staff_line_clearance_
* 0.5 * details_
.staff_space_
;
512 Real top_y
= y
+ conf
->delta_y_
+ conf
->dir_
* h
;
513 Real top_pos
= top_y
/ (0.5*details_
.staff_space_
);
514 int round_pos
= int (my_round (top_pos
));
516 /* TODO: should use other variable? */
517 Real clearance
= details_
.center_staff_line_clearance_
;
518 if (fabs (top_pos
- round_pos
) < clearance
519 && Staff_symbol_referencer::on_staff_line (details_
.staff_symbol_referencer_
,
522 Real new_y
= (round_pos
+ clearance
* conf
->dir_
) * 0.5 * details_
.staff_space_
;
523 conf
->delta_y_
= (new_y
- top_y
);
528 conf
->attachment_x_
= get_attachment (y
+ conf
->delta_y_
, conf
->column_ranks_
);
529 if (conf
->height (details_
) < details_
.intra_space_threshold_
* 0.5 * details_
.staff_space_
)
532 This is less sensible for long ties, since those are more
535 Interval close_by
= get_attachment (y
537 + (dir
* details_
.intra_space_threshold_
* 0.25
538 * details_
.staff_space_
),
539 conf
->column_ranks_
);
541 conf
->attachment_x_
.intersect (close_by
);
544 conf
->attachment_x_
.widen ( - details_
.x_gap_
);
546 if (conf
->column_span_length ())
549 avoid the stems that we attach to as well. We don't do this
550 for semities (span length = 0)
552 It would be better to check D against HEAD-DIRECTION if
558 Real y
= conf
->position_
* details_
.staff_space_
* 0.5 + conf
->delta_y_
;
559 if (get_stem_extent (conf
->column_ranks_
[d
], d
, X_AXIS
).is_empty ()
560 || !get_stem_extent (conf
->column_ranks_
[d
], d
, Y_AXIS
).contains (y
))
563 conf
->attachment_x_
[d
] =
564 d
* min (d
* conf
->attachment_x_
[d
],
565 d
* (get_stem_extent (conf
->column_ranks_
[d
], d
, X_AXIS
)[-d
] - d
* details_
.stem_gap_
));
567 while (flip (&d
) != LEFT
);
573 Tie_formatting_problem::get_head_extent (int col
, Direction d
, Axis a
) const
575 Column_extent_map::const_iterator i
= head_extents_
.find (Tuple2
<int> (col
, int (d
)));
576 if (i
!= head_extents_
.end ())
577 return (*i
).second
[a
];
583 Tie_formatting_problem::get_stem_extent (int col
, Direction d
, Axis a
) const
585 Column_extent_map::const_iterator i
= stem_extents_
.find (Tuple2
<int> (col
, int (d
)));
586 if (i
!= stem_extents_
.end ())
587 return (*i
).second
[a
];
593 TIE_IDX and TIES_CONF are optional.
596 Tie_formatting_problem::score_aptitude (Tie_configuration
*conf
,
597 Tie_specification
const &spec
,
598 Ties_configuration
*ties_conf
, int tie_idx
) const
601 Real curve_y
= conf
->position_
* details_
.staff_space_
* 0.5 + conf
->delta_y_
;
602 Real tie_y
= spec
.position_
* details_
.staff_space_
* 0.5;
603 if (sign (curve_y
- tie_y
) != conf
->dir_
)
605 Real p
= details_
.wrong_direction_offset_penalty_
;
607 ties_conf
->add_tie_score (p
, tie_idx
, "wrong dir");
613 Real relevant_dist
= max (fabs (curve_y
- tie_y
) - 0.5, 0.0);
614 Real p
= details_
.vertical_distance_penalty_factor_
* convex_amplifier (1.0, 0.9, relevant_dist
);
616 ties_conf
->add_tie_score (p
, tie_idx
, "vdist");
624 if (!spec
.note_head_drul_
[d
])
627 Interval head_x
= spec
.note_head_drul_
[d
]->extent (x_refpoint_
, X_AXIS
);
628 Real dist
= head_x
.distance (conf
->attachment_x_
[d
]);
632 TODO: flatten with log or sqrt.
634 Real p
= details_
.horizontal_distance_penalty_factor_
635 * convex_amplifier (1.25, 1.0, dist
);
637 ties_conf
->add_tie_score (p
, tie_idx
,
638 (d
== LEFT
) ? "lhdist" : "rhdist");
643 while (flip (&d
) != LEFT
);
646 && ties_conf
->size () == 1)
649 Drul_array
<Grob
*> stems (0, 0);
652 if (!spec
.note_head_drul_
[d
])
655 Grob
*stem
= unsmob_grob (spec
.note_head_drul_
[d
]->get_object ("stem"));
657 && Stem::is_normal_stem (stem
))
660 while (flip (&d
) != LEFT
);
662 bool tie_stem_dir_ok
= true;
663 bool tie_position_dir_ok
= true;
664 if (stems
[LEFT
] && !stems
[RIGHT
])
665 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[LEFT
]);
666 else if (!stems
[LEFT
] && stems
[RIGHT
])
667 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[RIGHT
]);
668 else if (stems
[LEFT
] && stems
[RIGHT
]
669 && get_grob_direction (stems
[LEFT
]) == get_grob_direction (stems
[RIGHT
]))
670 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[LEFT
]);
671 else if (spec
.position_
)
672 tie_position_dir_ok
= conf
->dir_
== sign (spec
.position_
);
674 if (!tie_stem_dir_ok
)
675 ties_conf
->add_score (details_
.same_dir_as_stem_penalty_
, "tie/stem dir");
676 if (!tie_position_dir_ok
)
677 ties_conf
->add_score (details_
.same_dir_as_stem_penalty_
, "tie/pos dir");
679 while (flip (&d
) != LEFT
);
687 Tie_formatting_problem::head_positions_slice (int rank
) const
689 Position_extent_map::const_iterator
i (head_positions_
.find (rank
));
690 if (i
!= head_positions_
.end ())
699 Score a configuration, ie. how well these ties looks without regard
700 to the note heads that they should connect to.
703 Tie_formatting_problem::score_configuration (Tie_configuration
*conf
) const
710 Real length
= conf
->attachment_x_
.length ();
712 conf
->add_score (details_
.min_length_penalty_factor_
713 * peak_around (0.33 * details_
.min_length_
, details_
.min_length_
, length
),
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_
,
725 && Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
) > top_y
)
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
)),
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
))),
749 if (!dot_x_
.is_empty ())
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
++)
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
)),
772 conf
->scored_
= true;
776 Tie_formatting_problem::score_ties_aptitude (Ties_configuration
*ties
) const
778 if (ties
->size () != specifications_
.size ())
780 programming_error ("Huh? Mismatch between sizes.");
784 for (vsize i
= 0; i
< ties
->size (); i
++)
785 score_aptitude (&ties
->at (i
), specifications_
[i
],
790 Tie_formatting_problem::score_ties (Ties_configuration
*ties
) const
795 score_ties_configuration (ties
);
796 score_ties_aptitude (ties
);
797 ties
->scored_
= true;
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
];
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
)),
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");
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 ()),
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_
))),
858 Generate with correct X-attachments and beziers, copying delta_y_
859 from TIES_CONFIG if necessary.
862 Tie_formatting_problem::generate_ties_configuration (Ties_configuration
const &ties_config
)
864 Ties_configuration copy
;
865 for (vsize i
= 0; i
< ties_config
.size (); i
++)
867 Tie_configuration
* ptr
= get_configuration (ties_config
[i
].position_
, ties_config
[i
].dir_
,
868 ties_config
[i
].column_ranks_
,
869 !specifications_
[i
].has_manual_delta_y_
);
870 if (specifications_
[i
].has_manual_delta_y_
)
873 = (specifications_
[i
].manual_position_
- ties_config
[i
].position_
)
874 * 0.5 * details_
.staff_space_
;
876 copy
.push_back (*ptr
);
883 Tie_formatting_problem::generate_base_chord_configuration ()
885 Ties_configuration ties_config
;
886 for (vsize i
= 0; i
< specifications_
.size (); i
++)
888 Tie_configuration conf
;
889 if (specifications_
[i
].has_manual_dir_
)
890 conf
.dir_
= specifications_
[i
].manual_dir_
;
891 if (specifications_
[i
].has_manual_position_
)
893 conf
.position_
= (int) my_round (specifications_
[i
].manual_position_
);
894 if (specifications_
[i
].has_manual_delta_y_
)
895 conf
.delta_y_
= (specifications_
[i
].manual_position_
- conf
.position_
)
896 * 0.5 * details_
.staff_space_
;
900 conf
.position_
= specifications_
[i
].position_
;
903 conf
.column_ranks_
= specifications_
[i
].column_ranks_
;
904 ties_config
.push_back (conf
);
907 set_ties_config_standard_directions (&ties_config
);
908 for (vsize i
= 0; i
< ties_config
.size (); i
++)
909 if (!specifications_
[i
].manual_position_
)
910 ties_config
[i
].position_
+= ties_config
[i
].dir_
;
912 ties_config
= generate_ties_configuration (ties_config
);
918 Tie_formatting_problem::find_best_variation (Ties_configuration
const &base
,
919 vector
<Tie_configuration_variation
> const &vars
)
921 Ties_configuration best
= base
;
924 This simply is 1-opt: we have K substitions, and we try applying
925 exactly every one for each.
927 for (vsize i
= 0; i
< vars
.size (); i
++)
929 Ties_configuration
variant (base
);
930 variant
[vars
[i
].index_
] = *vars
[i
].suggestion_
;
932 variant
.reset_score ();
933 score_ties (&variant
);
935 if (variant
.score () < best
.score ())
947 Tie_formatting_problem::generate_optimal_configuration ()
949 Ties_configuration base
= generate_base_chord_configuration ();
952 vector
<Tie_configuration_variation
> vars
;
953 if (specifications_
.size () > 1)
954 vars
= generate_collision_variations (base
);
956 vars
= generate_single_tie_variations (base
);
958 Ties_configuration best
= find_best_variation (base
, vars
);
960 if (specifications_
.size () > 1)
962 vars
= generate_extremal_tie_variations (best
);
963 best
= find_best_variation (best
, vars
);
969 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration
*tie_configs
)
971 if (tie_configs
->empty ())
974 if (!tie_configs
->at (0).dir_
)
976 if (tie_configs
->size () == 1)
977 tie_configs
->at (0).dir_
= Direction (sign (tie_configs
->at (0).position_
));
979 if (!tie_configs
->at (0).dir_
)
980 tie_configs
->at (0).dir_
981 = (tie_configs
->size() > 1) ? DOWN
: details_
.neutral_direction_
;
984 if (!tie_configs
->back ().dir_
)
985 tie_configs
->back ().dir_
= UP
;
990 for (vsize i
= 1; i
< tie_configs
->size (); i
++)
992 Real diff
= (tie_configs
->at (i
).position_
993 -tie_configs
->at (i
-1).position_
);
996 = specifications_
[i
].column_span () - specifications_
[i
-1].column_span ();
997 if (span_diff
&& fabs (diff
) <= 2)
1000 tie_configs
->at (i
).dir_
= UP
;
1001 else if (span_diff
< 0)
1002 tie_configs
->at (i
-1).dir_
= DOWN
;
1004 else if (fabs (diff
) <= 1)
1006 if (!tie_configs
->at (i
-1).dir_
)
1007 tie_configs
->at (i
-1).dir_
= DOWN
;
1008 if (!tie_configs
->at (i
).dir_
)
1009 tie_configs
->at (i
).dir_
= UP
;
1013 for (vsize i
= 1; i
+ 1 < tie_configs
->size (); i
++)
1015 Tie_configuration
&conf
= tie_configs
->at (i
);
1019 Direction position_dir
=
1020 Direction (sign (conf
.position_
));
1022 position_dir
= DOWN
;
1024 conf
.dir_
= position_dir
;
1028 Tie_configuration_variation::Tie_configuration_variation ()
1034 vector
<Tie_configuration_variation
>
1035 Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration
const &ties
) const
1037 vector
<Tie_configuration_variation
> vars
;
1041 if (boundary (ties
, d
, 0).dir_
== d
1042 && !boundary (specifications_
, d
, 0).has_manual_position_
)
1043 for (int i
= 1; i
<= details_
.multi_tie_region_size_
; i
++)
1045 Tie_configuration_variation var
;
1046 var
.index_
= (d
== DOWN
) ? 0 : ties
.size () - 1;
1047 var
.suggestion_
= get_configuration (boundary (ties
, d
, 0).position_
1049 boundary (ties
, d
, 0).column_ranks_
,
1051 vars
.push_back (var
);
1054 while (flip (&d
) != DOWN
);
1059 vector
<Tie_configuration_variation
>
1060 Tie_formatting_problem::generate_single_tie_variations (Ties_configuration
const &ties
) const
1062 vector
<Tie_configuration_variation
> vars
;
1064 int sz
= details_
.single_tie_region_size_
;
1065 if (specifications_
[0].has_manual_position_
)
1067 for (int i
= 0; i
< sz
; i
++)
1073 && ties
[0].dir_
== d
)
1076 int p
= ties
[0].position_
+ i
* d
;
1078 if (!specifications_
[0].has_manual_dir_
1079 || d
== specifications_
[0].manual_dir_
)
1081 Tie_configuration_variation var
;
1083 var
.suggestion_
= get_configuration (p
,
1084 d
, specifications_
[0].column_ranks_
,
1085 !specifications_
[0].has_manual_delta_y_
);
1086 vars
.push_back (var
);
1089 while (flip (&d
) != LEFT
);
1095 vector
<Tie_configuration_variation
>
1096 Tie_formatting_problem::generate_collision_variations (Ties_configuration
const &ties
) const
1098 Real center_distance_tolerance
= 0.25;
1100 vector
<Tie_configuration_variation
> vars
;
1101 Real last_center
= 0.0;
1102 for (vsize i
= 0; i
< ties
.size (); i
++)
1104 Bezier
b (ties
[i
].get_transformed_bezier (details_
));
1106 Real center
= b
.curve_point (0.5)[Y_AXIS
];
1110 if (center
<= last_center
+ center_distance_tolerance
)
1112 if (!specifications_
[i
].has_manual_dir_
)
1114 Tie_configuration_variation var
;
1116 var
.suggestion_
= get_configuration (specifications_
[i
].position_
1120 ties
[i
].column_ranks_
,
1121 !specifications_
[i
].has_manual_delta_y_
1124 vars
.push_back (var
);
1127 if (!specifications_
[i
-1].has_manual_dir_
)
1129 Tie_configuration_variation var
;
1131 var
.suggestion_
= get_configuration (specifications_
[i
-1].position_
1134 specifications_
[i
-1].column_ranks_
,
1135 !specifications_
[i
-1].has_manual_delta_y_
1138 vars
.push_back (var
);
1141 if (i
== 1 && !specifications_
[i
-1].has_manual_position_
1142 && ties
[i
-1].dir_
== DOWN
)
1144 Tie_configuration_variation var
;
1146 var
.suggestion_
= get_configuration (specifications_
[i
-1].position_
- 1, DOWN
,
1147 specifications_
[i
-1].column_ranks_
,
1148 !specifications_
[i
-1].has_manual_delta_y_
1151 vars
.push_back (var
);
1153 if (i
== ties
.size () && !specifications_
[i
].has_manual_position_
1154 && ties
[i
].dir_
== UP
)
1156 Tie_configuration_variation var
;
1158 var
.suggestion_
= get_configuration (specifications_
[i
].position_
1160 specifications_
[i
].column_ranks_
,
1161 !specifications_
[i
].has_manual_delta_y_
1163 vars
.push_back (var
);
1166 else if (dot_positions_
.find (ties
[i
].position_
) != dot_positions_
.end ()
1167 && !specifications_
[i
].has_manual_position_
)
1169 Tie_configuration_variation var
;
1171 var
.suggestion_
= get_configuration (ties
[i
].position_
+ ties
[i
].dir_
,
1173 ties
[i
].column_ranks_
,
1174 !specifications_
[i
].has_manual_delta_y_
1176 vars
.push_back (var
);
1181 last_center
= center
;
1189 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs
)
1192 for (SCM s
= manual_configs
;
1193 scm_is_pair (s
) && k
< specifications_
.size (); s
= scm_cdr (s
))
1195 SCM entry
= scm_car (s
);
1196 if (scm_is_pair (entry
))
1198 Tie_specification
&spec
= specifications_
[k
];
1200 if (scm_is_number (scm_car (entry
)))
1202 spec
.has_manual_position_
= true;
1203 spec
.manual_position_
= scm_to_double (scm_car (entry
));
1204 spec
.has_manual_delta_y_
= (scm_inexact_p (scm_car (entry
)) == SCM_BOOL_T
);
1207 if (scm_is_number (scm_cdr (entry
)))
1209 spec
.has_manual_dir_
= true;
1210 spec
.manual_dir_
= Direction (scm_to_int (scm_cdr (entry
)));
1219 Tie_formatting_problem::set_debug_scoring (Ties_configuration
const &base
)
1221 #if DEBUG_TIE_SCORING
1222 if (to_boolean (x_refpoint_
->layout ()
1223 ->lookup_variable (ly_symbol2scm ("debug-tie-scoring"))))
1225 for (vsize i
= 0; i
< base
.size (); i
++)
1227 string card
= base
.complete_tie_card (i
);
1228 specifications_
[i
].tie_grob_
->set_property ("quant-score",
1229 ly_string2scm (card
));