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 "paper-column.hh"
14 #include "directional-element-interface.hh"
15 #include "libc-extension.hh"
17 #include "note-head.hh"
18 #include "rhythmic-head.hh"
20 #include "staff-symbol-referencer.hh"
22 #include "tie-configuration.hh"
25 #include "pointer-group-interface.hh"
26 #include "output-def.hh"
29 Tie_formatting_problem::print_ties_configuration (Ties_configuration
const *ties
)
31 for (vsize i
= 0; i
< ties
->size (); i
++)
33 char const *man_pos
= (specifications_
[i
].has_manual_position_
) ? "(M)" : "";
34 char const *man_dir
= (specifications_
[i
].has_manual_dir_
) ? "(M)" : "";
35 char const *dir
= (ties
->at (i
).dir_
== UP
) ? "up" : "dn";
37 printf ("(P%d%s, %s%s) ", ties
->at (i
).position_
, man_pos
, dir
, man_dir
);
43 Tie_formatting_problem::get_attachment (Real y
, Drul_array
<int> columns
) const
45 Interval
attachments (0,0);
49 Tuple2
<int> key (columns
[d
], int (d
));
50 Chord_outline_map::const_iterator
i (chord_outlines_
.find (key
));
51 if (i
== chord_outlines_
.end ())
52 programming_error ("Cannot find chord outline");
54 attachments
[d
] = i
->second
.height (y
);
56 while (flip (&d
) != LEFT
);
61 Tie_formatting_problem::Tie_formatting_problem ()
64 use_horizontal_spacing_
= true;
67 Tie_formatting_problem::~Tie_formatting_problem ()
69 for (Tie_configuration_map::const_iterator
i (possibilities_
.begin ());
70 i
!= possibilities_
.end (); i
++)
75 Tie_formatting_problem::set_column_chord_outline (vector
<Item
*> bounds
,
79 Real staff_space
= Staff_symbol_referencer::staff_space (bounds
[0]);
82 vector
<Box
> head_boxes
;
85 for (vsize i
= 0; i
< bounds
.size (); i
++)
87 Grob
*head
= bounds
[i
];
88 if (!Note_head::has_interface (head
))
92 stem
= unsmob_grob (head
->get_object ("stem"));
94 Real p
= Staff_symbol_referencer::get_position (head
);
95 Interval
y ((p
-1) * 0.5 * staff_space
,
96 (p
+1) * 0.5 * staff_space
);
98 Interval x
= head
->extent (x_refpoint_
, X_AXIS
);
99 head_boxes
.push_back (Box (x
, y
));
100 boxes
.push_back (Box (x
, y
));
102 Grob
*dots
= Rhythmic_head::get_dots (head
);
103 if (dir
== LEFT
&& dots
)
105 Interval x
= dots
->extent (x_refpoint_
, X_AXIS
);
106 int p
= int (Staff_symbol_referencer::get_position (dots
));
109 TODO: shouldn't this use column-rank dependent key?
111 dot_positions_
.insert (p
);
114 Interval
y (dots
->extent (dots
, Y_AXIS
));
115 y
.translate (p
* staff_space
* 0.5);
117 boxes
.push_back (Box (x
, y
));
121 Tuple2
<int> key (column_rank
, int (dir
));
125 if (Stem::is_normal_stem (stem
))
128 x
.add_point (stem
->relative_coordinate (x_refpoint_
, X_AXIS
));
129 x
.widen (staff_space
/ 20); // ugh.
132 Real stem_end_position
= 0.0;
133 if (Stem::is_cross_staff (stem
))
134 stem_end_position
= get_grob_direction (stem
) * infinity_f
;
137 if (use_horizontal_spacing_
|| !Stem::get_beam (stem
))
138 stem_end_position
= Stem::stem_end_position (stem
) * staff_space
* .5;
140 stem_end_position
= Stem::note_head_positions (stem
)[get_grob_direction (stem
)]
144 y
.add_point (stem_end_position
);
146 Direction stemdir
= get_grob_direction (stem
);
147 y
.add_point (Stem::head_positions (stem
)[-stemdir
]
153 boxes
.push_back (Box (x
, y
));
155 stem_extents_
[key
].unite (Box (x
, y
));
159 Box flag_box
= Stem::get_translated_flag (stem
).extent_box ();
160 flag_box
.translate ( Offset (x
[RIGHT
], X_AXIS
));
161 boxes
.push_back (flag_box
);
166 Grob
*head
= Stem::support_head (stem
);
169 In case of invisible stem, don't pass x-center of heads.
171 Real x_center
= head
->extent (x_refpoint_
, X_AXIS
).center ();
173 x_ext
[-dir
] = x_center
;
174 x_ext
[dir
] = infinity_f
* dir
;
176 for (vsize j
= 0; j
< head_boxes
.size (); j
++)
177 y_ext
.unite (head_boxes
[j
][Y_AXIS
]);
179 boxes
.push_back (Box (x_ext
, y_ext
));
182 extract_grob_set (stem
, "note-heads", heads
);
183 for (vsize i
= 0; i
< heads
.size (); i
++)
185 if (find (bounds
.begin (), bounds
.end (), dynamic_cast<Item
*> (heads
[i
])) == bounds
.end ())
188 other untied notes in the same chord.
191 Interval y
= Staff_symbol_referencer::extent_in_staff (heads
[i
]);
192 Interval x
= heads
[i
]->extent (x_refpoint_
, X_AXIS
);
193 boxes
.push_back (Box (x
, y
));
196 Grob
*acc
= unsmob_grob (heads
[i
]->get_object ("accidental-grob"));
198 acc
->get_property ("stencil"); /* trigger tie-related suicide */
200 if (acc
&& acc
->is_live () && dir
== RIGHT
)
202 boxes
.push_back (Box (acc
->extent (x_refpoint_
, X_AXIS
),
203 Staff_symbol_referencer::extent_in_staff (acc
)));
206 head_positions_
[column_rank
].add_point (int (Staff_symbol_referencer::get_position (heads
[i
])));
211 Direction updowndir
= DOWN
;
216 if (head_boxes
.size ())
218 Box b
= boundary (head_boxes
, updowndir
, 0);
220 x
[-dir
] = b
[X_AXIS
].linear_combination (-dir
/ 2);
221 y
[-updowndir
] = b
[Y_AXIS
][updowndir
];
222 y
[updowndir
] = updowndir
* infinity_f
;
226 boxes
.push_back (Box (x
, y
));
228 while (flip (&updowndir
) != DOWN
);
230 /* todo: the horizon_padding is somewhat arbitrary */
231 chord_outlines_
[key
] = Skyline (boxes
, details_
.skyline_padding_
, Y_AXIS
, -dir
);
232 if (bounds
[0]->break_status_dir ())
234 Real x
= robust_relative_extent (bounds
[0], x_refpoint_
, X_AXIS
)[-dir
];
236 chord_outlines_
[key
].set_minimum_height (x
);
241 for (vsize j
= 0; j
< head_boxes
.size (); j
++)
243 x
.unite (head_boxes
[j
][X_AXIS
]);
246 chord_outlines_
[key
].set_minimum_height (x
[dir
]);
249 head_extents_
[key
].set_empty ();
250 for (vsize i
= 0; i
< head_boxes
.size (); i
++)
252 head_extents_
[key
].unite (head_boxes
[i
]);
257 Tie_formatting_problem::set_chord_outline (vector
<Item
*> bounds
,
262 for (vsize i
= 0; i
< bounds
.size (); i
++)
263 ranks
.push_back (bounds
[i
]->get_column ()->get_rank ());
265 vector_sort (ranks
, less
<int> ());
268 for (vsize i
= 0; i
< ranks
.size (); i
++)
270 vector
<Item
*> col_items
;
271 for (vsize j
= 0; j
< bounds
.size (); j
++)
273 if (bounds
[j
]->get_column ()->get_rank () == ranks
[i
])
274 col_items
.push_back (bounds
[j
]);
277 set_column_chord_outline (col_items
, dir
, ranks
[i
]);
284 Tie_formatting_problem::from_tie (Grob
*tie
)
287 ties
.push_back (tie
);
290 details_
.from_grob (tie
);
294 Tie_formatting_problem::common_x_refpoint () const
300 Tie_formatting_problem::from_ties (vector
<Grob
*> const &ties
)
305 x_refpoint_
= ties
[0];
306 for (vsize i
= 0; i
< ties
.size (); i
++)
308 x_refpoint_
= dynamic_cast<Spanner
*> (ties
[i
])->get_bound (LEFT
)->common_refpoint (x_refpoint_
, X_AXIS
);
309 x_refpoint_
= dynamic_cast<Spanner
*> (ties
[i
])->get_bound (RIGHT
)->common_refpoint (x_refpoint_
, X_AXIS
);
312 details_
.from_grob (ties
[0]);
317 vector
<Item
*> bounds
;
319 for (vsize i
= 0; i
< ties
.size (); i
++)
321 Item
*it
= dynamic_cast<Spanner
*> (ties
[i
])->get_bound (d
);
322 if (it
->break_status_dir ())
323 it
= it
->get_column ();
325 bounds
.push_back (it
);
328 set_chord_outline (bounds
, d
);
330 while (flip (&d
) != LEFT
);
333 for (vsize i
= 0; i
< ties
.size (); i
++)
335 Tie_specification spec
;
336 spec
.from_grob (ties
[i
]);
340 spec
.note_head_drul_
[d
] = Tie::head (ties
[i
], d
);
341 spec
.column_ranks_
[d
] = Tie::get_column_rank (ties
[i
], d
);
343 while (flip (&d
) != LEFT
);
344 specifications_
.push_back (spec
);
349 Tie_formatting_problem::from_semi_ties (vector
<Grob
*> const &semi_ties
, Direction head_dir
)
351 if (semi_ties
.empty ())
354 use_horizontal_spacing_
= false;
355 details_
.from_grob (semi_ties
[0]);
358 int column_rank
= -1;
359 for (vsize i
= 0; i
< semi_ties
.size (); i
++)
361 Tie_specification spec
;
362 Item
*head
= unsmob_item (semi_ties
[i
]->get_object ("note-head"));
365 programming_error ("LV tie without head?!");
369 spec
.position_
= int (Staff_symbol_referencer::get_position (head
));
372 spec
.from_grob (semi_ties
[i
]);
374 spec
.note_head_drul_
[head_dir
] = head
;
375 column_rank
= Tie::get_column_rank (semi_ties
[i
], head_dir
);
376 spec
.column_ranks_
= Drul_array
<int> (column_rank
, column_rank
);
377 heads
.push_back (head
);
378 specifications_
.push_back (spec
);
381 x_refpoint_
= semi_ties
[0];
382 for (vsize i
= 0; i
< semi_ties
.size (); i
++)
383 x_refpoint_
= semi_ties
[i
]->common_refpoint (x_refpoint_
, X_AXIS
);
384 for (vsize i
= 0; i
< heads
.size (); i
++)
385 x_refpoint_
= heads
[i
]->common_refpoint (x_refpoint_
, X_AXIS
);
387 set_chord_outline (heads
, head_dir
);
389 Tuple2
<int> head_key (column_rank
, head_dir
);
390 Tuple2
<int> open_key (column_rank
, -head_dir
);
391 Real extremal
= chord_outlines_
[head_key
].max_height ();
393 chord_outlines_
[open_key
] = Skyline (head_dir
);
394 chord_outlines_
[open_key
].set_minimum_height (extremal
- head_dir
* 1.5);
399 Tie_formatting_problem::get_tie_specification (int i
) const
401 return specifications_
[i
];
406 Return configuration, create it if necessary.
409 Tie_formatting_problem::get_configuration (int pos
, Direction dir
, Drul_array
<int> columns
,
412 int key_components
[] = {
413 pos
, dir
, columns
[LEFT
], columns
[RIGHT
]
415 Tuple
<int,4> key (key_components
);
417 Tie_configuration_map::const_iterator f
= possibilities_
.find (key
);
418 if (f
!= possibilities_
.end ())
424 Tie_configuration
*conf
= generate_configuration (pos
, dir
, columns
, tune_dy
);
425 ((Tie_formatting_problem
*) this)->possibilities_
[key
] = conf
;
430 Tie_formatting_problem::generate_configuration (int pos
, Direction dir
,
431 Drul_array
<int> columns
, bool y_tune
) const
433 Tie_configuration
*conf
= new Tie_configuration
;
434 conf
->position_
= pos
;
437 conf
->column_ranks_
= columns
;
439 Real y
= conf
->position_
* 0.5 * details_
.staff_space_
;
441 if (dot_positions_
.find (pos
) != dot_positions_
.end ())
443 conf
->delta_y_
+= dir
* 0.25 * details_
.staff_space_
;
448 && max (fabs (get_head_extent (columns
[LEFT
], LEFT
, Y_AXIS
)[dir
] - y
),
449 fabs (get_head_extent (columns
[RIGHT
], RIGHT
, Y_AXIS
)[dir
] - y
)) < 0.25
450 && !Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
))
453 (get_head_extent (columns
[LEFT
], LEFT
, Y_AXIS
)[dir
] - y
)
454 + dir
* details_
.outer_tie_vertical_gap_
;
459 conf
->attachment_x_
= get_attachment (y
+ conf
->delta_y_
, conf
->column_ranks_
);
460 Real h
= conf
->height (details_
);
465 - should make sliding criterion, should flatten ties if
467 - they're just the wrong (ie. touching line at top & bottom)
471 if (head_positions_slice (columns
[LEFT
]).contains (pos
)
472 || head_positions_slice (columns
[RIGHT
]).contains (pos
)
473 || abs (pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
475 if (h
< details_
.intra_space_threshold_
* 0.5 * details_
.staff_space_
)
477 if (!Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
)
478 && abs (pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
480 conf
->center_tie_vertically (details_
);
482 else if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
))
484 conf
->delta_y_
+= dir
*
485 details_
.tip_staff_line_clearance_
* 0.5 * details_
.staff_space_
;
490 Real top_y
= y
+ conf
->delta_y_
+ conf
->dir_
* h
;
491 Real top_pos
= top_y
/ (0.5*details_
.staff_space_
);
492 int round_pos
= int (my_round (top_pos
));
494 /* TODO: should use other variable? */
495 Real clearance
= details_
.center_staff_line_clearance_
;
496 if (fabs (top_pos
- round_pos
) < clearance
497 && Staff_symbol_referencer::on_staff_line (details_
.staff_symbol_referencer_
,
500 Real new_y
= (round_pos
+ clearance
* conf
->dir_
) * 0.5 * details_
.staff_space_
;
501 conf
->delta_y_
= (new_y
- top_y
);
506 conf
->attachment_x_
= get_attachment (y
+ conf
->delta_y_
, conf
->column_ranks_
);
507 if (conf
->height (details_
) < details_
.intra_space_threshold_
* 0.5 * details_
.staff_space_
)
510 This is less sensible for long ties, since those are more
513 Interval close_by
= get_attachment (y
515 + (dir
* details_
.intra_space_threshold_
* 0.25
516 * details_
.staff_space_
),
517 conf
->column_ranks_
);
519 conf
->attachment_x_
.intersect (close_by
);
522 conf
->attachment_x_
.widen ( - details_
.x_gap_
);
524 if (conf
->column_span_length ())
527 avoid the stems that we attach to as well. We don't do this
528 for semities (span length = 0)
530 It would be better to check D against HEAD-DIRECTION if
536 Real y
= conf
->position_
* details_
.staff_space_
* 0.5 + conf
->delta_y_
;
537 if (get_stem_extent (conf
->column_ranks_
[d
], d
, X_AXIS
).is_empty ()
538 || !get_stem_extent (conf
->column_ranks_
[d
], d
, Y_AXIS
).contains (y
))
541 conf
->attachment_x_
[d
] =
542 d
* min (d
* conf
->attachment_x_
[d
],
543 d
* (get_stem_extent (conf
->column_ranks_
[d
], d
, X_AXIS
)[-d
] - d
* details_
.stem_gap_
));
545 while (flip (&d
) != LEFT
);
551 Tie_formatting_problem::get_head_extent (int col
, Direction d
, Axis a
) const
553 Column_extent_map::const_iterator i
= head_extents_
.find (Tuple2
<int> (col
, int (d
)));
554 if (i
!= head_extents_
.end ())
555 return (*i
).second
[a
];
561 Tie_formatting_problem::get_stem_extent (int col
, Direction d
, Axis a
) const
563 Column_extent_map::const_iterator i
= stem_extents_
.find (Tuple2
<int> (col
, int (d
)));
564 if (i
!= stem_extents_
.end ())
565 return (*i
).second
[a
];
571 TIE_IDX and TIES_CONF are optional.
574 Tie_formatting_problem::score_aptitude (Tie_configuration
*conf
,
575 Tie_specification
const &spec
,
576 Ties_configuration
*ties_conf
, int tie_idx
) const
579 Real curve_y
= conf
->position_
* details_
.staff_space_
* 0.5 + conf
->delta_y_
;
580 Real tie_y
= spec
.position_
* details_
.staff_space_
* 0.5;
581 if (sign (curve_y
- tie_y
) != conf
->dir_
)
583 Real p
= details_
.wrong_direction_offset_penalty_
;
585 ties_conf
->add_tie_score (p
, tie_idx
, "wrong dir");
591 Real relevant_dist
= max (fabs (curve_y
- tie_y
) - 0.5, 0.0);
592 Real p
= details_
.vertical_distance_penalty_factor_
* convex_amplifier (1.0, 0.9, relevant_dist
);
594 ties_conf
->add_tie_score (p
, tie_idx
, "vdist");
602 if (!spec
.note_head_drul_
[d
])
605 Interval head_x
= spec
.note_head_drul_
[d
]->extent (x_refpoint_
, X_AXIS
);
606 Real dist
= head_x
.distance (conf
->attachment_x_
[d
]);
610 TODO: flatten with log or sqrt.
612 Real p
= details_
.horizontal_distance_penalty_factor_
613 * convex_amplifier (1.25, 1.0, dist
);
615 ties_conf
->add_tie_score (p
, tie_idx
,
616 (d
== LEFT
) ? "lhdist" : "rhdist");
621 while (flip (&d
) != LEFT
);
624 && ties_conf
->size () == 1)
627 Drul_array
<Grob
*> stems (0, 0);
630 if (!spec
.note_head_drul_
[d
])
633 Grob
*stem
= unsmob_grob (spec
.note_head_drul_
[d
]->get_object ("stem"));
635 && Stem::is_normal_stem (stem
))
638 while (flip (&d
) != LEFT
);
640 bool tie_stem_dir_ok
= true;
641 bool tie_position_dir_ok
= true;
642 if (stems
[LEFT
] && !stems
[RIGHT
])
643 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[LEFT
]);
644 else if (!stems
[LEFT
] && stems
[RIGHT
])
645 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[RIGHT
]);
646 else if (stems
[LEFT
] && stems
[RIGHT
]
647 && get_grob_direction (stems
[LEFT
]) == get_grob_direction (stems
[RIGHT
]))
648 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[LEFT
]);
649 else if (spec
.position_
)
650 tie_position_dir_ok
= conf
->dir_
== sign (spec
.position_
);
652 if (!tie_stem_dir_ok
)
653 ties_conf
->add_score (details_
.same_dir_as_stem_penalty_
, "tie/stem dir");
654 if (!tie_position_dir_ok
)
655 ties_conf
->add_score (details_
.same_dir_as_stem_penalty_
, "tie/pos dir");
657 while (flip (&d
) != LEFT
);
665 Tie_formatting_problem::head_positions_slice (int rank
) const
667 Position_extent_map::const_iterator
i (head_positions_
.find (rank
));
668 if (i
!= head_positions_
.end ())
677 Score a configuration, ie. how well these ties looks without regard
678 to the note heads that they should connect to.
681 Tie_formatting_problem::score_configuration (Tie_configuration
*conf
) const
688 Real length
= conf
->attachment_x_
.length ();
690 conf
->add_score (details_
.min_length_penalty_factor_
691 * peak_around (0.33 * details_
.min_length_
, details_
.min_length_
, length
),
694 Real tip_pos
= conf
->position_
+ conf
->delta_y_
/ 0.5 * details_
.staff_space_
;
695 Real tip_y
= tip_pos
* details_
.staff_space_
* 0.5;
696 Real height
= conf
->height (details_
);
698 Real top_y
= tip_y
+ conf
->dir_
* height
;
699 Real top_pos
= 2 * top_y
/ details_
.staff_space_
;
700 Real round_top_pos
= rint (top_pos
);
701 if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
,
703 && Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
) > top_y
)
706 details_
.staff_line_collision_penalty_
707 * peak_around (0.1 * details_
.center_staff_line_clearance_
,
708 details_
.center_staff_line_clearance_
,
709 fabs (top_pos
- round_top_pos
)),
713 int rounded_tip_pos
= int (rint (tip_pos
));
714 if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, rounded_tip_pos
)
715 && (head_positions_slice (conf
->column_ranks_
[LEFT
]).contains (rounded_tip_pos
)
716 || head_positions_slice (conf
->column_ranks_
[RIGHT
]).contains (rounded_tip_pos
)
717 || abs (rounded_tip_pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
720 conf
->add_score (details_
.staff_line_collision_penalty_
721 * peak_around (0.1 * details_
.tip_staff_line_clearance_
,
722 details_
.tip_staff_line_clearance_
,
723 fabs (tip_pos
- rint (tip_pos
))),
727 if (!dot_x_
.is_empty ())
730 Real x
= dot_x_
.center ();
732 Bezier b
= conf
->get_transformed_bezier (details_
);
733 if (b
.control_point_extent (X_AXIS
).contains (x
))
735 Real y
= b
.get_other_coordinate (X_AXIS
, x
);
737 for (set
<int>::const_iterator
i (dot_positions_
.begin ());
738 i
!= dot_positions_
.end (); i
++)
741 conf
->add_score (details_
.dot_collision_penalty_
742 * peak_around (.1 * details_
.dot_collision_clearance_
,
743 details_
.dot_collision_clearance_
,
744 fabs (dot_pos
* details_
.staff_space_
* 0.5 - y
)),
750 conf
->scored_
= true;
754 Tie_formatting_problem::score_ties_aptitude (Ties_configuration
*ties
) const
756 if (ties
->size () != specifications_
.size ())
758 programming_error ("Huh? Mismatch between sizes.");
762 for (vsize i
= 0; i
< ties
->size (); i
++)
763 score_aptitude (&ties
->at (i
), specifications_
[i
],
768 Tie_formatting_problem::score_ties (Ties_configuration
*ties
) const
773 score_ties_configuration (ties
);
774 score_ties_aptitude (ties
);
775 ties
->scored_
= true;
779 Tie_formatting_problem::score_ties_configuration (Ties_configuration
*ties
) const
781 for (vsize i
= 0; i
< ties
->size (); i
++)
783 score_configuration (&ties
->at (i
));
784 ties
->add_tie_score (ties
->at (i
).score (), i
, "conf");
787 Real last_edge
= 0.0;
788 Real last_center
= 0.0;
789 for (vsize i
= 0; i
< ties
->size (); i
++)
791 Bezier
b (ties
->at (i
).get_transformed_bezier (details_
));
793 Real center
= b
.curve_point (0.5)[Y_AXIS
];
794 Real edge
= b
.curve_point (0.0)[Y_AXIS
];
798 if (edge
<= last_edge
)
799 ties
->add_score (details_
.tie_column_monotonicity_penalty_
, "monoton edge");
800 if (center
<= last_center
)
801 ties
->add_score (details_
.tie_column_monotonicity_penalty_
, "monoton cent");
803 ties
->add_score (details_
.tie_tie_collision_penalty_
*
804 peak_around (0.1 * details_
.tie_tie_collision_distance_
,
805 details_
.tie_tie_collision_distance_
,
806 fabs (center
- last_center
)),
808 ties
->add_score (details_
.tie_tie_collision_penalty_
*
809 peak_around (0.1 * details_
.tie_tie_collision_distance_
,
810 details_
.tie_tie_collision_distance_
,
811 fabs (edge
- last_edge
)), "tietie edge");
815 last_center
= center
;
818 if (ties
->size () > 1)
820 ties
->add_score (details_
.outer_tie_length_symmetry_penalty_factor_
821 * fabs (ties
->at (0).attachment_x_
.length () - ties
->back ().attachment_x_
.length ()),
824 ties
->add_score (details_
.outer_tie_vertical_distance_symmetry_penalty_factor_
825 * fabs (fabs (specifications_
[0].position_
* 0.5 * details_
.staff_space_
826 - (ties
->at (0).position_
* 0.5 * details_
.staff_space_
827 + ties
->at (0).delta_y_
))
829 fabs (specifications_
.back ().position_
* 0.5 * details_
.staff_space_
830 - (ties
->back ().position_
* 0.5 * details_
.staff_space_
831 + ties
->back ().delta_y_
))),
836 Generate with correct X-attachments and beziers, copying delta_y_
837 from TIES_CONFIG if necessary.
840 Tie_formatting_problem::generate_ties_configuration (Ties_configuration
const &ties_config
)
842 Ties_configuration copy
;
843 for (vsize i
= 0; i
< ties_config
.size (); i
++)
845 Tie_configuration
* ptr
= get_configuration (ties_config
[i
].position_
, ties_config
[i
].dir_
,
846 ties_config
[i
].column_ranks_
,
847 !specifications_
[i
].has_manual_delta_y_
);
848 if (specifications_
[i
].has_manual_delta_y_
)
851 = (specifications_
[i
].manual_position_
- ties_config
[i
].position_
)
852 * 0.5 * details_
.staff_space_
;
854 copy
.push_back (*ptr
);
861 Tie_formatting_problem::generate_base_chord_configuration ()
863 Ties_configuration ties_config
;
864 for (vsize i
= 0; i
< specifications_
.size (); i
++)
866 Tie_configuration conf
;
867 if (specifications_
[i
].has_manual_dir_
)
868 conf
.dir_
= specifications_
[i
].manual_dir_
;
869 if (specifications_
[i
].has_manual_position_
)
871 conf
.position_
= (int) my_round (specifications_
[i
].manual_position_
);
872 if (specifications_
[i
].has_manual_delta_y_
)
873 conf
.delta_y_
= (specifications_
[i
].manual_position_
- conf
.position_
)
874 * 0.5 * details_
.staff_space_
;
878 conf
.position_
= specifications_
[i
].position_
;
881 conf
.column_ranks_
= specifications_
[i
].column_ranks_
;
882 ties_config
.push_back (conf
);
885 set_ties_config_standard_directions (&ties_config
);
886 for (vsize i
= 0; i
< ties_config
.size (); i
++)
887 if (!specifications_
[i
].manual_position_
)
888 ties_config
[i
].position_
+= ties_config
[i
].dir_
;
890 ties_config
= generate_ties_configuration (ties_config
);
896 Tie_formatting_problem::find_best_variation (Ties_configuration
const &base
,
897 vector
<Tie_configuration_variation
> const &vars
)
899 Ties_configuration best
= base
;
902 This simply is 1-opt: we have K substitions, and we try applying
903 exactly every one for each.
905 for (vsize i
= 0; i
< vars
.size (); i
++)
907 Ties_configuration
variant (base
);
908 variant
[vars
[i
].index_
] = *vars
[i
].suggestion_
;
910 variant
.reset_score ();
911 score_ties (&variant
);
913 if (variant
.score () < best
.score ())
925 Tie_formatting_problem::generate_optimal_configuration ()
927 Ties_configuration base
= generate_base_chord_configuration ();
930 vector
<Tie_configuration_variation
> vars
;
931 if (specifications_
.size () > 1)
932 vars
= generate_collision_variations (base
);
934 vars
= generate_single_tie_variations (base
);
936 Ties_configuration best
= find_best_variation (base
, vars
);
938 if (specifications_
.size () > 1)
940 vars
= generate_extremal_tie_variations (best
);
941 best
= find_best_variation (best
, vars
);
947 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration
*tie_configs
)
949 if (tie_configs
->empty ())
952 if (!tie_configs
->at (0).dir_
)
954 if (tie_configs
->size () == 1)
955 tie_configs
->at (0).dir_
= Direction (sign (tie_configs
->at (0).position_
));
957 if (!tie_configs
->at (0).dir_
)
958 tie_configs
->at (0).dir_
= DOWN
;
961 if (!tie_configs
->back ().dir_
)
962 tie_configs
->back ().dir_
= UP
;
967 for (vsize i
= 1; i
< tie_configs
->size (); i
++)
969 Real diff
= (tie_configs
->at (i
).position_
970 -tie_configs
->at (i
-1).position_
);
973 = specifications_
[i
].column_span () - specifications_
[i
-1].column_span ();
974 if (span_diff
&& fabs (diff
) <= 2)
977 tie_configs
->at (i
).dir_
= UP
;
978 else if (span_diff
< 0)
979 tie_configs
->at (i
-1).dir_
= DOWN
;
981 else if (fabs (diff
) <= 1)
983 if (!tie_configs
->at (i
-1).dir_
)
984 tie_configs
->at (i
-1).dir_
= DOWN
;
985 if (!tie_configs
->at (i
).dir_
)
986 tie_configs
->at (i
).dir_
= UP
;
990 for (vsize i
= 1; i
+ 1 < tie_configs
->size (); i
++)
992 Tie_configuration
&conf
= tie_configs
->at (i
);
996 Direction position_dir
=
997 Direction (sign (conf
.position_
));
1001 conf
.dir_
= position_dir
;
1005 Tie_configuration_variation::Tie_configuration_variation ()
1011 vector
<Tie_configuration_variation
>
1012 Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration
const &ties
) const
1014 vector
<Tie_configuration_variation
> vars
;
1018 if (boundary (ties
, d
, 0).dir_
== d
1019 && !boundary (specifications_
, d
, 0).has_manual_position_
)
1020 for (int i
= 1; i
<= details_
.multi_tie_region_size_
; i
++)
1022 Tie_configuration_variation var
;
1023 var
.index_
= (d
== DOWN
) ? 0 : ties
.size () - 1;
1024 var
.suggestion_
= get_configuration (boundary (ties
, d
, 0).position_
1026 boundary (ties
, d
, 0).column_ranks_
,
1028 vars
.push_back (var
);
1031 while (flip (&d
) != DOWN
);
1036 vector
<Tie_configuration_variation
>
1037 Tie_formatting_problem::generate_single_tie_variations (Ties_configuration
const &ties
) const
1039 vector
<Tie_configuration_variation
> vars
;
1041 int sz
= details_
.single_tie_region_size_
;
1042 if (specifications_
[0].has_manual_position_
)
1044 for (int i
= 0; i
< sz
; i
++)
1050 && ties
[0].dir_
== d
)
1053 int p
= ties
[0].position_
+ i
* d
;
1055 if (!specifications_
[0].has_manual_dir_
1056 || d
== specifications_
[0].manual_dir_
)
1058 Tie_configuration_variation var
;
1060 var
.suggestion_
= get_configuration (p
,
1061 d
, specifications_
[0].column_ranks_
,
1062 !specifications_
[0].has_manual_delta_y_
);
1063 vars
.push_back (var
);
1066 while (flip (&d
) != LEFT
);
1072 vector
<Tie_configuration_variation
>
1073 Tie_formatting_problem::generate_collision_variations (Ties_configuration
const &ties
) const
1075 Real center_distance_tolerance
= 0.25;
1077 vector
<Tie_configuration_variation
> vars
;
1078 Real last_center
= 0.0;
1079 for (vsize i
= 0; i
< ties
.size (); i
++)
1081 Bezier
b (ties
[i
].get_transformed_bezier (details_
));
1083 Real center
= b
.curve_point (0.5)[Y_AXIS
];
1087 if (center
<= last_center
+ center_distance_tolerance
)
1089 if (!specifications_
[i
].has_manual_dir_
)
1091 Tie_configuration_variation var
;
1093 var
.suggestion_
= get_configuration (specifications_
[i
].position_
1097 ties
[i
].column_ranks_
,
1098 !specifications_
[i
].has_manual_delta_y_
1101 vars
.push_back (var
);
1104 if (!specifications_
[i
-1].has_manual_dir_
)
1106 Tie_configuration_variation var
;
1108 var
.suggestion_
= get_configuration (specifications_
[i
-1].position_
1111 specifications_
[i
-1].column_ranks_
,
1112 !specifications_
[i
-1].has_manual_delta_y_
1115 vars
.push_back (var
);
1118 if (i
== 1 && !specifications_
[i
-1].has_manual_position_
1119 && ties
[i
-1].dir_
== DOWN
)
1121 Tie_configuration_variation var
;
1123 var
.suggestion_
= get_configuration (specifications_
[i
-1].position_
- 1, DOWN
,
1124 specifications_
[i
-1].column_ranks_
,
1125 !specifications_
[i
-1].has_manual_delta_y_
1128 vars
.push_back (var
);
1130 if (i
== ties
.size () && !specifications_
[i
].has_manual_position_
1131 && ties
[i
].dir_
== UP
)
1133 Tie_configuration_variation var
;
1135 var
.suggestion_
= get_configuration (specifications_
[i
].position_
1137 specifications_
[i
].column_ranks_
,
1138 !specifications_
[i
].has_manual_delta_y_
1140 vars
.push_back (var
);
1143 else if (dot_positions_
.find (ties
[i
].position_
) != dot_positions_
.end ()
1144 && !specifications_
[i
].has_manual_position_
)
1146 Tie_configuration_variation var
;
1148 var
.suggestion_
= get_configuration (ties
[i
].position_
+ ties
[i
].dir_
,
1150 ties
[i
].column_ranks_
,
1151 !specifications_
[i
].has_manual_delta_y_
1153 vars
.push_back (var
);
1158 last_center
= center
;
1166 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs
)
1169 for (SCM s
= manual_configs
;
1170 scm_is_pair (s
) && k
< specifications_
.size (); s
= scm_cdr (s
))
1172 SCM entry
= scm_car (s
);
1173 if (scm_is_pair (entry
))
1175 Tie_specification
&spec
= specifications_
[k
];
1177 if (scm_is_number (scm_car (entry
)))
1179 spec
.has_manual_position_
= true;
1180 spec
.manual_position_
= scm_to_double (scm_car (entry
));
1181 spec
.has_manual_delta_y_
= (scm_inexact_p (scm_car (entry
)) == SCM_BOOL_T
);
1184 if (scm_is_number (scm_cdr (entry
)))
1186 spec
.has_manual_dir_
= true;
1187 spec
.manual_dir_
= Direction (scm_to_int (scm_cdr (entry
)));
1196 Tie_formatting_problem::set_debug_scoring (Ties_configuration
const &base
)
1198 #if DEBUG_TIE_SCORING
1199 if (to_boolean (x_refpoint_
->layout ()
1200 ->lookup_variable (ly_symbol2scm ("debug-tie-scoring"))))
1202 for (vsize i
= 0; i
< base
.size (); i
++)
1204 string card
= base
.complete_tie_card (i
);
1205 specifications_
[i
].tie_grob_
->set_property ("quant-score",
1206 ly_string2scm (card
));