2 tie-formatting-problem.cc -- implement Tie_formatting_problem
4 source file of the GNU LilyPond music typesetter
6 (c) 2005--2008 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");
685 Tie_formatting_problem::head_positions_slice (int rank
) const
687 Position_extent_map::const_iterator
i (head_positions_
.find (rank
));
688 if (i
!= head_positions_
.end ())
697 Score a configuration, ie. how well these ties looks without regard
698 to the note heads that they should connect to.
701 Tie_formatting_problem::score_configuration (Tie_configuration
*conf
) const
708 Real length
= conf
->attachment_x_
.length ();
711 = peak_around (0.33 * details_
.min_length_
, details_
.min_length_
, length
);
712 conf
->add_score (details_
.min_length_penalty_factor_
713 * length_penalty
, "minlength");
715 Real tip_pos
= conf
->position_
+ conf
->delta_y_
/ 0.5 * details_
.staff_space_
;
716 Real tip_y
= tip_pos
* details_
.staff_space_
* 0.5;
717 Real height
= conf
->height (details_
);
719 Real top_y
= tip_y
+ conf
->dir_
* height
;
720 Real top_pos
= 2 * top_y
/ details_
.staff_space_
;
721 Real round_top_pos
= rint (top_pos
);
722 if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
,
724 && Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
) > top_y
)
727 details_
.staff_line_collision_penalty_
728 * peak_around (0.1 * details_
.center_staff_line_clearance_
,
729 details_
.center_staff_line_clearance_
,
730 fabs (top_pos
- round_top_pos
)),
734 int rounded_tip_pos
= int (rint (tip_pos
));
735 if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, rounded_tip_pos
)
736 && (head_positions_slice (conf
->column_ranks_
[LEFT
]).contains (rounded_tip_pos
)
737 || head_positions_slice (conf
->column_ranks_
[RIGHT
]).contains (rounded_tip_pos
)
738 || abs (rounded_tip_pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
741 conf
->add_score (details_
.staff_line_collision_penalty_
742 * peak_around (0.1 * details_
.tip_staff_line_clearance_
,
743 details_
.tip_staff_line_clearance_
,
744 fabs (tip_pos
- rint (tip_pos
))),
748 if (!dot_x_
.is_empty ())
751 Real x
= dot_x_
.center ();
753 Bezier b
= conf
->get_transformed_bezier (details_
);
754 if (b
.control_point_extent (X_AXIS
).contains (x
))
756 Real y
= b
.get_other_coordinate (X_AXIS
, x
);
758 for (set
<int>::const_iterator
i (dot_positions_
.begin ());
759 i
!= dot_positions_
.end (); i
++)
762 conf
->add_score (details_
.dot_collision_penalty_
763 * peak_around (.1 * details_
.dot_collision_clearance_
,
764 details_
.dot_collision_clearance_
,
765 fabs (dot_pos
* details_
.staff_space_
* 0.5 - y
)),
771 conf
->scored_
= true;
775 Tie_formatting_problem::score_ties_aptitude (Ties_configuration
*ties
) const
777 if (ties
->size () != specifications_
.size ())
779 programming_error ("Huh? Mismatch between sizes.");
783 for (vsize i
= 0; i
< ties
->size (); i
++)
784 score_aptitude (&ties
->at (i
), specifications_
[i
],
789 Tie_formatting_problem::score_ties (Ties_configuration
*ties
) const
794 score_ties_configuration (ties
);
795 score_ties_aptitude (ties
);
796 ties
->scored_
= true;
800 Tie_formatting_problem::score_ties_configuration (Ties_configuration
*ties
) const
802 for (vsize i
= 0; i
< ties
->size (); i
++)
804 score_configuration (&ties
->at (i
));
805 ties
->add_tie_score (ties
->at (i
).score (), i
, "conf");
808 Real last_edge
= 0.0;
809 Real last_center
= 0.0;
810 for (vsize i
= 0; i
< ties
->size (); i
++)
812 Bezier
b (ties
->at (i
).get_transformed_bezier (details_
));
814 Real center
= b
.curve_point (0.5)[Y_AXIS
];
815 Real edge
= b
.curve_point (0.0)[Y_AXIS
];
819 if (edge
<= last_edge
)
820 ties
->add_score (details_
.tie_column_monotonicity_penalty_
, "monoton edge");
821 if (center
<= last_center
)
822 ties
->add_score (details_
.tie_column_monotonicity_penalty_
, "monoton cent");
824 ties
->add_score (details_
.tie_tie_collision_penalty_
*
825 peak_around (0.1 * details_
.tie_tie_collision_distance_
,
826 details_
.tie_tie_collision_distance_
,
827 fabs (center
- last_center
)),
829 ties
->add_score (details_
.tie_tie_collision_penalty_
*
830 peak_around (0.1 * details_
.tie_tie_collision_distance_
,
831 details_
.tie_tie_collision_distance_
,
832 fabs (edge
- last_edge
)), "tietie edge");
836 last_center
= center
;
839 if (ties
->size () > 1)
841 ties
->add_score (details_
.outer_tie_length_symmetry_penalty_factor_
842 * fabs (ties
->at (0).attachment_x_
.length () - ties
->back ().attachment_x_
.length ()),
845 ties
->add_score (details_
.outer_tie_vertical_distance_symmetry_penalty_factor_
846 * fabs (fabs (specifications_
[0].position_
* 0.5 * details_
.staff_space_
847 - (ties
->at (0).position_
* 0.5 * details_
.staff_space_
848 + ties
->at (0).delta_y_
))
850 fabs (specifications_
.back ().position_
* 0.5 * details_
.staff_space_
851 - (ties
->back ().position_
* 0.5 * details_
.staff_space_
852 + 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 for (vsize j
= 0; j
< vars
[i
].index_suggestion_pairs_
.size(); j
++)
931 variant
[vars
[i
].index_suggestion_pairs_
[j
].first
] = *vars
[i
].index_suggestion_pairs_
[j
].second
;
933 variant
.reset_score ();
934 score_ties (&variant
);
936 if (variant
.score () < best
.score ())
948 Tie_formatting_problem::generate_optimal_configuration ()
950 Ties_configuration base
= generate_base_chord_configuration ();
953 vector
<Tie_configuration_variation
> vars
;
954 if (specifications_
.size () > 1)
955 vars
= generate_collision_variations (base
);
957 vars
= generate_single_tie_variations (base
);
959 Ties_configuration best
= find_best_variation (base
, vars
);
961 if (specifications_
.size () > 1)
963 vars
= generate_extremal_tie_variations (best
);
964 best
= find_best_variation (best
, vars
);
970 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration
*tie_configs
)
972 if (tie_configs
->empty ())
975 if (!tie_configs
->at (0).dir_
)
977 if (tie_configs
->size () == 1)
978 tie_configs
->at (0).dir_
= Direction (sign (tie_configs
->at (0).position_
));
980 if (!tie_configs
->at (0).dir_
)
981 tie_configs
->at (0).dir_
982 = (tie_configs
->size() > 1) ? DOWN
: details_
.neutral_direction_
;
985 if (!tie_configs
->back ().dir_
)
986 tie_configs
->back ().dir_
= UP
;
991 for (vsize i
= 1; i
< tie_configs
->size (); i
++)
993 Real diff
= (tie_configs
->at (i
).position_
994 -tie_configs
->at (i
-1).position_
);
997 = specifications_
[i
].column_span () - specifications_
[i
-1].column_span ();
998 if (span_diff
&& fabs (diff
) <= 2)
1001 tie_configs
->at (i
).dir_
= UP
;
1002 else if (span_diff
< 0)
1003 tie_configs
->at (i
-1).dir_
= DOWN
;
1005 else if (fabs (diff
) <= 1)
1007 if (!tie_configs
->at (i
-1).dir_
)
1008 tie_configs
->at (i
-1).dir_
= DOWN
;
1009 if (!tie_configs
->at (i
).dir_
)
1010 tie_configs
->at (i
).dir_
= UP
;
1014 for (vsize i
= 1; i
+ 1 < tie_configs
->size (); i
++)
1016 Tie_configuration
&conf
= tie_configs
->at (i
);
1020 Direction position_dir
=
1021 Direction (sign (conf
.position_
));
1023 position_dir
= DOWN
;
1025 conf
.dir_
= position_dir
;
1029 vector
<Tie_configuration_variation
>
1030 Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration
const &ties
) const
1032 vector
<Tie_configuration_variation
> vars
;
1034 for (int i
= 1; i
<= details_
.multi_tie_region_size_
; i
++)
1036 Drul_array
<Tie_configuration
*> configs (0, 0);
1039 const Tie_configuration
&config
= boundary (ties
, d
, 0);
1040 if (config
.dir_
== d
1041 && !boundary (specifications_
, d
, 0).has_manual_position_
)
1043 Tie_configuration_variation var
;
1044 configs
[d
] = get_configuration (config
.position_
+ d
* i
, d
,
1045 config
.column_ranks_
,
1047 var
.add_suggestion((d
== DOWN
) ? 0 : ties
.size () - 1,
1049 vars
.push_back (var
);
1052 while (flip (&d
) != DOWN
);
1053 if (configs
[LEFT
] && configs
[RIGHT
])
1055 Tie_configuration_variation var
;
1056 var
.add_suggestion(0, configs
[DOWN
]);
1057 var
.add_suggestion(ties
.size() - 1, configs
[UP
]);
1058 vars
.push_back (var
);
1065 vector
<Tie_configuration_variation
>
1066 Tie_formatting_problem::generate_single_tie_variations (Ties_configuration
const &ties
) const
1068 vector
<Tie_configuration_variation
> vars
;
1070 int sz
= details_
.single_tie_region_size_
;
1071 if (specifications_
[0].has_manual_position_
)
1073 for (int i
= 0; i
< sz
; i
++)
1079 && ties
[0].dir_
== d
)
1082 int p
= ties
[0].position_
+ i
* d
;
1084 if (!specifications_
[0].has_manual_dir_
1085 || d
== specifications_
[0].manual_dir_
)
1087 Tie_configuration_variation var
;
1088 var
.add_suggestion(0,
1089 get_configuration (p
,
1090 d
, specifications_
[0].column_ranks_
,
1091 !specifications_
[0].has_manual_delta_y_
));
1092 vars
.push_back (var
);
1095 while (flip (&d
) != LEFT
);
1101 vector
<Tie_configuration_variation
>
1102 Tie_formatting_problem::generate_collision_variations (Ties_configuration
const &ties
) const
1104 Real center_distance_tolerance
= 0.25;
1106 vector
<Tie_configuration_variation
> vars
;
1107 Real last_center
= 0.0;
1108 for (vsize i
= 0; i
< ties
.size (); i
++)
1110 Bezier
b (ties
[i
].get_transformed_bezier (details_
));
1112 Real center
= b
.curve_point (0.5)[Y_AXIS
];
1116 if (center
<= last_center
+ center_distance_tolerance
)
1118 if (!specifications_
[i
].has_manual_dir_
)
1120 Tie_configuration_variation var
;
1121 var
.add_suggestion(i
,
1122 get_configuration (specifications_
[i
].position_
1126 ties
[i
].column_ranks_
,
1127 !specifications_
[i
].has_manual_delta_y_
1130 vars
.push_back (var
);
1133 if (!specifications_
[i
-1].has_manual_dir_
)
1135 Tie_configuration_variation var
;
1136 var
.add_suggestion(i
-1,
1137 get_configuration (specifications_
[i
-1].position_
1140 specifications_
[i
-1].column_ranks_
,
1141 !specifications_
[i
-1].has_manual_delta_y_
));
1143 vars
.push_back (var
);
1146 if (i
== 1 && !specifications_
[i
-1].has_manual_position_
1147 && ties
[i
-1].dir_
== DOWN
)
1149 Tie_configuration_variation var
;
1150 var
.add_suggestion(i
-1,
1151 get_configuration (specifications_
[i
-1].position_
- 1, DOWN
,
1152 specifications_
[i
-1].column_ranks_
,
1153 !specifications_
[i
-1].has_manual_delta_y_
1155 vars
.push_back (var
);
1157 if (i
== ties
.size () && !specifications_
[i
].has_manual_position_
1158 && ties
[i
].dir_
== UP
)
1160 Tie_configuration_variation var
;
1161 var
.add_suggestion(i
,
1162 get_configuration (specifications_
[i
].position_
1164 specifications_
[i
].column_ranks_
,
1165 !specifications_
[i
].has_manual_delta_y_
1167 vars
.push_back (var
);
1170 else if (dot_positions_
.find (ties
[i
].position_
) != dot_positions_
.end ()
1171 && !specifications_
[i
].has_manual_position_
)
1173 Tie_configuration_variation var
;
1174 var
.add_suggestion(i
,
1175 get_configuration (ties
[i
].position_
+ ties
[i
].dir_
,
1177 ties
[i
].column_ranks_
,
1178 !specifications_
[i
].has_manual_delta_y_
1180 vars
.push_back (var
);
1185 last_center
= center
;
1193 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs
)
1196 for (SCM s
= manual_configs
;
1197 scm_is_pair (s
) && k
< specifications_
.size (); s
= scm_cdr (s
))
1199 SCM entry
= scm_car (s
);
1200 if (scm_is_pair (entry
))
1202 Tie_specification
&spec
= specifications_
[k
];
1204 if (scm_is_number (scm_car (entry
)))
1206 spec
.has_manual_position_
= true;
1207 spec
.manual_position_
= scm_to_double (scm_car (entry
));
1208 spec
.has_manual_delta_y_
= (scm_inexact_p (scm_car (entry
)) == SCM_BOOL_T
);
1211 if (scm_is_number (scm_cdr (entry
)))
1213 spec
.has_manual_dir_
= true;
1214 spec
.manual_dir_
= Direction (scm_to_int (scm_cdr (entry
)));
1223 Tie_formatting_problem::set_debug_scoring (Ties_configuration
const &base
)
1225 #if DEBUG_TIE_SCORING
1226 if (to_boolean (x_refpoint_
->layout ()
1227 ->lookup_variable (ly_symbol2scm ("debug-tie-scoring"))))
1229 for (vsize i
= 0; i
< base
.size (); i
++)
1231 string card
= base
.complete_tie_card (i
);
1232 specifications_
[i
].tie_grob_
->set_property ("quant-score",
1233 ly_string2scm (card
));