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>
11 #include "tie-formatting-problem.hh"
13 #include "axis-group-interface.hh"
14 #include "paper-column.hh"
16 #include "directional-element-interface.hh"
17 #include "libc-extension.hh"
19 #include "note-head.hh"
20 #include "rhythmic-head.hh"
22 #include "staff-symbol-referencer.hh"
24 #include "tie-configuration.hh"
27 #include "pointer-group-interface.hh"
28 #include "output-def.hh"
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
);
45 Tie_formatting_problem::get_attachment (Real y
, Drul_array
<int> columns
) const
47 Interval
attachments (0,0);
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");
56 attachments
[d
] = i
->second
.height (y
);
58 while (flip (&d
) != LEFT
);
63 Tie_formatting_problem::Tie_formatting_problem ()
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
++)
78 Tie_formatting_problem::set_column_chord_outline (vector
<Item
*> bounds
,
82 Real staff_space
= Staff_symbol_referencer::staff_space (bounds
[0]);
85 vector
<Box
> head_boxes
;
88 for (vsize i
= 0; i
< bounds
.size (); i
++)
90 Grob
*head
= bounds
[i
];
91 if (!Note_head::has_interface (head
))
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
);
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
));
128 if (Stem::is_normal_stem (stem
))
131 x
.add_point (stem
->relative_coordinate (x_refpoint_
, X_AXIS
));
132 x
.widen (staff_space
/ 20); // ugh.
135 Real stem_end_position
= 0.0;
136 if (Stem::is_cross_staff (stem
))
137 stem_end_position
= get_grob_direction (stem
) * infinity_f
;
140 if (use_horizontal_spacing_
|| !Stem::get_beam (stem
))
141 stem_end_position
= Stem::stem_end_position (stem
) * staff_space
* .5;
143 stem_end_position
= Stem::note_head_positions (stem
)[get_grob_direction (stem
)]
147 y
.add_point (stem_end_position
);
149 Direction stemdir
= get_grob_direction (stem
);
150 y
.add_point (Stem::head_positions (stem
)[-stemdir
]
156 boxes
.push_back (Box (x
, y
));
158 stem_extents_
[key
].unite (Box (x
, y
));
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
);
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 ();
176 x_ext
[-dir
] = x_center
;
177 x_ext
[dir
] = infinity_f
* dir
;
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"));
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
;
219 if (head_boxes
.size ())
221 Box b
= boundary (head_boxes
, updowndir
, 0);
223 x
[-dir
] = b
[X_AXIS
].linear_combination (-dir
/ 2);
224 y
[-updowndir
] = b
[Y_AXIS
][updowndir
];
225 y
[updowndir
] = updowndir
* infinity_f
;
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
));
239 iv
.add_point (bounds
[0]->relative_coordinate (x_refpoint_
, X_AXIS
));
241 chord_outlines_
[key
].set_minimum_height (iv
[-dir
]);
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
]);
262 Tie_formatting_problem::set_chord_outline (vector
<Item
*> bounds
,
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> ());
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
]);
289 Tie_formatting_problem::from_tie (Grob
*tie
)
292 ties
.push_back (tie
);
295 details_
.from_grob (tie
);
299 Tie_formatting_problem::common_x_refpoint () const
305 Tie_formatting_problem::from_ties (vector
<Grob
*> const &ties
)
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]);
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
);
364 Tie_formatting_problem::from_semi_ties (vector
<Grob
*> const &semi_ties
, Direction head_dir
)
366 if (semi_ties
.empty ())
369 use_horizontal_spacing_
= false;
370 details_
.from_grob (semi_ties
[0]);
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"));
380 programming_error ("LV tie without 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);
422 Tie_formatting_problem::get_tie_specification (int i
) const
424 return specifications_
[i
];
429 Return configuration, create it if necessary.
432 Tie_formatting_problem::get_configuration (int pos
, Direction dir
, Drul_array
<int> columns
,
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 ())
447 Tie_configuration
*conf
= generate_configuration (pos
, dir
, columns
, tune_dy
);
448 ((Tie_formatting_problem
*) this)->possibilities_
[key
] = conf
;
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
;
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_
;
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
))
476 (get_head_extent (columns
[LEFT
], LEFT
, Y_AXIS
)[dir
] - y
)
477 + dir
* details_
.outer_tie_vertical_gap_
;
482 conf
->attachment_x_
= get_attachment (y
+ conf
->delta_y_
, conf
->column_ranks_
);
483 Real h
= conf
->height (details_
);
488 - should make sliding criterion, should flatten ties if
490 - they're just the wrong (ie. touching line at top & bottom)
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_
;
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_
,
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
536 Interval close_by
= get_attachment (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
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
))
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
);
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
];
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
];
594 TIE_IDX and TIES_CONF are optional.
597 Tie_formatting_problem::score_aptitude (Tie_configuration
*conf
,
598 Tie_specification
const &spec
,
599 Ties_configuration
*ties_conf
, int tie_idx
) const
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_
;
608 ties_conf
->add_tie_score (p
, tie_idx
, "wrong dir");
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
);
617 ties_conf
->add_tie_score (p
, tie_idx
, "vdist");
625 if (!spec
.note_head_drul_
[d
])
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
);
638 ties_conf
->add_tie_score (p
, tie_idx
,
639 (d
== LEFT
) ? "lhdist" : "rhdist");
644 while (flip (&d
) != LEFT
);
647 && ties_conf
->size () == 1)
650 Drul_array
<Grob
*> stems (0, 0);
653 if (!spec
.note_head_drul_
[d
])
656 Grob
*stem
= unsmob_grob (spec
.note_head_drul_
[d
]->get_object ("stem"));
658 && Stem::is_normal_stem (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");
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 ())
698 Score a configuration, ie. how well these ties looks without regard
699 to the note heads that they should connect to.
702 Tie_formatting_problem::score_configuration (Tie_configuration
*conf
) const
709 Real length
= conf
->attachment_x_
.length ();
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_
,
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_
))),
859 Generate with correct X-attachments and beziers, copying delta_y_
860 from TIES_CONFIG if necessary.
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_
)
874 = (specifications_
[i
].manual_position_
- ties_config
[i
].position_
)
875 * 0.5 * details_
.staff_space_
;
877 copy
.push_back (*ptr
);
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_
;
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
);
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 ())
949 Tie_formatting_problem::generate_optimal_configuration ()
951 Ties_configuration base
= generate_base_chord_configuration ();
954 vector
<Tie_configuration_variation
> vars
;
955 if (specifications_
.size () > 1)
956 vars
= generate_collision_variations (base
);
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
);
971 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration
*tie_configs
)
973 if (tie_configs
->empty ())
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
;
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_
);
998 = specifications_
[i
].column_span () - specifications_
[i
-1].column_span ();
999 if (span_diff
&& fabs (diff
) <= 2)
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
);
1021 Direction position_dir
=
1022 Direction (sign (conf
.position_
));
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
;
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_
,
1048 var
.add_suggestion((d
== DOWN
) ? 0 : ties
.size () - 1,
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
);
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_
)
1074 for (int i
= 0; i
< sz
; i
++)
1080 && ties
[0].dir_
== d
)
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
);
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
];
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_
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_
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_
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_
,
1178 ties
[i
].column_ranks_
,
1179 !specifications_
[i
].has_manual_delta_y_
1181 vars
.push_back (var
);
1186 last_center
= center
;
1194 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs
)
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
)));
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
));