2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
22 #include "tie-formatting-problem.hh"
24 #include "axis-group-interface.hh"
25 #include "paper-column.hh"
27 #include "directional-element-interface.hh"
28 #include "libc-extension.hh"
30 #include "note-head.hh"
31 #include "rhythmic-head.hh"
33 #include "staff-symbol-referencer.hh"
35 #include "tie-configuration.hh"
38 #include "pointer-group-interface.hh"
39 #include "output-def.hh"
42 Tie_formatting_problem::print_ties_configuration (Ties_configuration
const *ties
)
44 for (vsize i
= 0; i
< ties
->size (); i
++)
46 char const *man_pos
= (specifications_
[i
].has_manual_position_
) ? "(M)" : "";
47 char const *man_dir
= (specifications_
[i
].has_manual_dir_
) ? "(M)" : "";
48 char const *dir
= (ties
->at (i
).dir_
== UP
) ? "up" : "dn";
50 printf ("(P%d%s, %s%s) ", ties
->at (i
).position_
, man_pos
, dir
, man_dir
);
56 Tie_formatting_problem::get_attachment (Real y
, Drul_array
<int> columns
) const
58 Interval
attachments (0,0);
62 Tuple2
<int> key (columns
[d
], int (d
));
63 Chord_outline_map::const_iterator
i (chord_outlines_
.find (key
));
64 if (i
== chord_outlines_
.end ())
65 programming_error ("Cannot find chord outline");
67 attachments
[d
] = i
->second
.height (y
);
69 while (flip (&d
) != LEFT
);
74 Tie_formatting_problem::Tie_formatting_problem ()
78 use_horizontal_spacing_
= true;
81 Tie_formatting_problem::~Tie_formatting_problem ()
83 for (Tie_configuration_map::const_iterator
i (possibilities_
.begin ());
84 i
!= possibilities_
.end (); i
++)
89 Tie_formatting_problem::set_column_chord_outline (vector
<Item
*> bounds
,
93 Real staff_space
= Staff_symbol_referencer::staff_space (bounds
[0]);
96 vector
<Box
> head_boxes
;
99 for (vsize i
= 0; i
< bounds
.size (); i
++)
101 Grob
*head
= bounds
[i
];
102 if (!Note_head::has_interface (head
))
106 stem
= unsmob_grob (head
->get_object ("stem"));
108 Real p
= Staff_symbol_referencer::get_position (head
);
109 Interval
y ((p
-1) * 0.5 * staff_space
,
110 (p
+1) * 0.5 * staff_space
);
112 Interval x
= head
->extent (x_refpoint_
, X_AXIS
);
113 head_boxes
.push_back (Box (x
, y
));
114 boxes
.push_back (Box (x
, y
));
116 Grob
*dots
= Rhythmic_head::get_dots (head
);
117 if (dir
== LEFT
&& dots
)
119 Interval x
= dots
->extent (x_refpoint_
, X_AXIS
);
120 int p
= int (Staff_symbol_referencer::get_position (dots
));
123 TODO: shouldn't this use column-rank dependent key?
125 dot_positions_
.insert (p
);
128 Interval
y (dots
->extent (dots
, Y_AXIS
));
129 y
.translate (p
* staff_space
* 0.5);
131 boxes
.push_back (Box (x
, y
));
135 Tuple2
<int> key (column_rank
, int (dir
));
139 if (Stem::is_normal_stem (stem
))
142 x
.add_point (stem
->relative_coordinate (x_refpoint_
, X_AXIS
));
143 x
.widen (staff_space
/ 20); // ugh.
146 Real stem_end_position
= 0.0;
147 if (Stem::is_cross_staff (stem
))
148 stem_end_position
= get_grob_direction (stem
) * infinity_f
;
151 if (use_horizontal_spacing_
|| !Stem::get_beam (stem
))
152 stem_end_position
= Stem::stem_end_position (stem
) * staff_space
* .5;
154 stem_end_position
= Stem::note_head_positions (stem
)[get_grob_direction (stem
)]
158 y
.add_point (stem_end_position
);
160 Direction stemdir
= get_grob_direction (stem
);
161 y
.add_point (Stem::head_positions (stem
)[-stemdir
]
167 boxes
.push_back (Box (x
, y
));
169 stem_extents_
[key
].unite (Box (x
, y
));
173 Box flag_box
= Stem::get_translated_flag (stem
).extent_box ();
174 flag_box
.translate ( Offset (x
[RIGHT
], X_AXIS
));
175 boxes
.push_back (flag_box
);
180 Grob
*head
= Stem::support_head (stem
);
183 In case of invisible stem, don't pass x-center of heads.
185 Real x_center
= head
->extent (x_refpoint_
, X_AXIS
).center ();
187 x_ext
[-dir
] = x_center
;
188 x_ext
[dir
] = infinity_f
* dir
;
190 for (vsize j
= 0; j
< head_boxes
.size (); j
++)
191 y_ext
.unite (head_boxes
[j
][Y_AXIS
]);
193 boxes
.push_back (Box (x_ext
, y_ext
));
196 extract_grob_set (stem
, "note-heads", heads
);
197 for (vsize i
= 0; i
< heads
.size (); i
++)
199 if (find (bounds
.begin (), bounds
.end (), dynamic_cast<Item
*> (heads
[i
])) == bounds
.end ())
202 other untied notes in the same chord.
205 Interval y
= Staff_symbol_referencer::extent_in_staff (heads
[i
]);
206 Interval x
= heads
[i
]->extent (x_refpoint_
, X_AXIS
);
207 boxes
.push_back (Box (x
, y
));
210 Grob
*acc
= unsmob_grob (heads
[i
]->get_object ("accidental-grob"));
212 acc
->get_property ("stencil"); /* trigger tie-related suicide */
214 if (acc
&& acc
->is_live () && dir
== RIGHT
)
216 boxes
.push_back (Box (acc
->extent (x_refpoint_
, X_AXIS
),
217 Staff_symbol_referencer::extent_in_staff (acc
)));
220 head_positions_
[column_rank
].add_point (int (Staff_symbol_referencer::get_position (heads
[i
])));
225 Direction updowndir
= DOWN
;
230 if (head_boxes
.size ())
232 Box b
= boundary (head_boxes
, updowndir
, 0);
234 x
[-dir
] = b
[X_AXIS
].linear_combination (-dir
/ 2);
235 y
[-updowndir
] = b
[Y_AXIS
][updowndir
];
236 y
[updowndir
] = updowndir
* infinity_f
;
240 boxes
.push_back (Box (x
, y
));
242 while (flip (&updowndir
) != DOWN
);
244 /* todo: the horizon_padding is somewhat arbitrary */
245 chord_outlines_
[key
] = Skyline (boxes
, details_
.skyline_padding_
, Y_AXIS
, -dir
);
246 if (bounds
[0]->break_status_dir ())
248 Interval
iv (Axis_group_interface::staff_extent (bounds
[0], x_refpoint_
, X_AXIS
, y_refpoint_
, Y_AXIS
));
250 iv
.add_point (bounds
[0]->relative_coordinate (x_refpoint_
, X_AXIS
));
252 chord_outlines_
[key
].set_minimum_height (iv
[-dir
]);
257 for (vsize j
= 0; j
< head_boxes
.size (); j
++)
259 x
.unite (head_boxes
[j
][X_AXIS
]);
262 chord_outlines_
[key
].set_minimum_height (x
[dir
]);
265 head_extents_
[key
].set_empty ();
266 for (vsize i
= 0; i
< head_boxes
.size (); i
++)
268 head_extents_
[key
].unite (head_boxes
[i
]);
273 Tie_formatting_problem::set_chord_outline (vector
<Item
*> bounds
,
278 for (vsize i
= 0; i
< bounds
.size (); i
++)
279 ranks
.push_back (bounds
[i
]->get_column ()->get_rank ());
281 vector_sort (ranks
, less
<int> ());
284 for (vsize i
= 0; i
< ranks
.size (); i
++)
286 vector
<Item
*> col_items
;
287 for (vsize j
= 0; j
< bounds
.size (); j
++)
289 if (bounds
[j
]->get_column ()->get_rank () == ranks
[i
])
290 col_items
.push_back (bounds
[j
]);
293 set_column_chord_outline (col_items
, dir
, ranks
[i
]);
300 Tie_formatting_problem::from_tie (Grob
*tie
)
303 ties
.push_back (tie
);
306 details_
.from_grob (tie
);
310 Tie_formatting_problem::common_x_refpoint () const
316 Tie_formatting_problem::from_ties (vector
<Grob
*> const &ties
)
321 x_refpoint_
= ties
[0];
322 y_refpoint_
= ties
[0];
323 for (vsize i
= 0; i
< ties
.size (); i
++)
325 Spanner
*tie
= dynamic_cast<Spanner
*> (ties
[i
]);
326 Item
*l
= tie
->get_bound (LEFT
);
327 Item
*r
= tie
->get_bound (RIGHT
);
329 x_refpoint_
= l
->common_refpoint (x_refpoint_
, X_AXIS
);
330 x_refpoint_
= r
->common_refpoint (x_refpoint_
, X_AXIS
);
332 if (!l
->break_status_dir ())
333 y_refpoint_
= l
->common_refpoint (y_refpoint_
, Y_AXIS
);
334 if (!r
->break_status_dir ())
335 y_refpoint_
= r
->common_refpoint (y_refpoint_
, Y_AXIS
);
338 details_
.from_grob (ties
[0]);
343 vector
<Item
*> bounds
;
345 for (vsize i
= 0; i
< ties
.size (); i
++)
347 Item
*it
= dynamic_cast<Spanner
*> (ties
[i
])->get_bound (d
);
348 if (it
->break_status_dir ())
349 it
= it
->get_column ();
351 bounds
.push_back (it
);
354 set_chord_outline (bounds
, d
);
356 while (flip (&d
) != LEFT
);
359 for (vsize i
= 0; i
< ties
.size (); i
++)
361 Tie_specification spec
;
362 spec
.from_grob (ties
[i
]);
366 spec
.note_head_drul_
[d
] = Tie::head (ties
[i
], d
);
367 spec
.column_ranks_
[d
] = Tie::get_column_rank (ties
[i
], d
);
369 while (flip (&d
) != LEFT
);
370 specifications_
.push_back (spec
);
375 Tie_formatting_problem::from_semi_ties (vector
<Grob
*> const &semi_ties
, Direction head_dir
)
377 if (semi_ties
.empty ())
380 use_horizontal_spacing_
= false;
381 details_
.from_grob (semi_ties
[0]);
384 int column_rank
= -1;
385 for (vsize i
= 0; i
< semi_ties
.size (); i
++)
387 Tie_specification spec
;
388 Item
*head
= unsmob_item (semi_ties
[i
]->get_object ("note-head"));
391 programming_error ("LV tie without head?!");
395 spec
.position_
= int (Staff_symbol_referencer::get_position (head
));
398 spec
.from_grob (semi_ties
[i
]);
400 spec
.note_head_drul_
[head_dir
] = head
;
401 column_rank
= Tie::get_column_rank (semi_ties
[i
], head_dir
);
402 spec
.column_ranks_
= Drul_array
<int> (column_rank
, column_rank
);
403 heads
.push_back (head
);
404 specifications_
.push_back (spec
);
407 x_refpoint_
= semi_ties
[0];
408 y_refpoint_
= semi_ties
[0];
410 for (vsize i
= 0; i
< semi_ties
.size (); i
++)
412 x_refpoint_
= semi_ties
[i
]->common_refpoint (x_refpoint_
, X_AXIS
);
413 y_refpoint_
= semi_ties
[i
]->common_refpoint (y_refpoint_
, Y_AXIS
);
415 for (vsize i
= 0; i
< heads
.size (); i
++)
417 x_refpoint_
= heads
[i
]->common_refpoint (x_refpoint_
, X_AXIS
);
418 y_refpoint_
= heads
[i
]->common_refpoint (y_refpoint_
, Y_AXIS
) ;
421 set_chord_outline (heads
, head_dir
);
423 Tuple2
<int> head_key (column_rank
, head_dir
);
424 Tuple2
<int> open_key (column_rank
, -head_dir
);
425 Real extremal
= chord_outlines_
[head_key
].max_height ();
427 chord_outlines_
[open_key
] = Skyline (head_dir
);
428 chord_outlines_
[open_key
].set_minimum_height (extremal
- head_dir
* 1.5);
433 Tie_formatting_problem::get_tie_specification (int i
) const
435 return specifications_
[i
];
440 Return configuration, create it if necessary.
443 Tie_formatting_problem::get_configuration (int pos
, Direction dir
, Drul_array
<int> columns
,
446 int key_components
[] = {
447 pos
, dir
, columns
[LEFT
], columns
[RIGHT
]
449 Tuple
<int,4> key (key_components
);
451 Tie_configuration_map::const_iterator f
= possibilities_
.find (key
);
452 if (f
!= possibilities_
.end ())
458 Tie_configuration
*conf
= generate_configuration (pos
, dir
, columns
, tune_dy
);
459 ((Tie_formatting_problem
*) this)->possibilities_
[key
] = conf
;
464 Tie_formatting_problem::generate_configuration (int pos
, Direction dir
,
465 Drul_array
<int> columns
, bool y_tune
) const
467 Tie_configuration
*conf
= new Tie_configuration
;
468 conf
->position_
= pos
;
471 conf
->column_ranks_
= columns
;
473 Real y
= conf
->position_
* 0.5 * details_
.staff_space_
;
475 if (dot_positions_
.find (pos
) != dot_positions_
.end ())
477 conf
->delta_y_
+= dir
* 0.25 * details_
.staff_space_
;
482 && max (fabs (get_head_extent (columns
[LEFT
], LEFT
, Y_AXIS
)[dir
] - y
),
483 fabs (get_head_extent (columns
[RIGHT
], RIGHT
, Y_AXIS
)[dir
] - y
)) < 0.25
484 && !Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
))
487 (get_head_extent (columns
[LEFT
], LEFT
, Y_AXIS
)[dir
] - y
)
488 + dir
* details_
.outer_tie_vertical_gap_
;
493 conf
->attachment_x_
= get_attachment (y
+ conf
->delta_y_
, conf
->column_ranks_
);
494 Real h
= conf
->height (details_
);
499 - should make sliding criterion, should flatten ties if
501 - they're just the wrong (ie. touching line at top & bottom)
505 if (head_positions_slice (columns
[LEFT
]).contains (pos
)
506 || head_positions_slice (columns
[RIGHT
]).contains (pos
)
507 || abs (pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
509 if (h
< details_
.intra_space_threshold_
* 0.5 * details_
.staff_space_
)
511 if (!Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
)
512 && abs (pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
514 conf
->center_tie_vertically (details_
);
516 else if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, pos
))
518 conf
->delta_y_
+= dir
*
519 details_
.tip_staff_line_clearance_
* 0.5 * details_
.staff_space_
;
524 Real top_y
= y
+ conf
->delta_y_
+ conf
->dir_
* h
;
525 Real top_pos
= top_y
/ (0.5*details_
.staff_space_
);
526 int round_pos
= int (my_round (top_pos
));
528 /* TODO: should use other variable? */
529 Real clearance
= details_
.center_staff_line_clearance_
;
530 if (fabs (top_pos
- round_pos
) < clearance
531 && Staff_symbol_referencer::on_staff_line (details_
.staff_symbol_referencer_
,
534 Real new_y
= (round_pos
+ clearance
* conf
->dir_
) * 0.5 * details_
.staff_space_
;
535 conf
->delta_y_
= (new_y
- top_y
);
540 conf
->attachment_x_
= get_attachment (y
+ conf
->delta_y_
, conf
->column_ranks_
);
541 if (conf
->height (details_
) < details_
.intra_space_threshold_
* 0.5 * details_
.staff_space_
)
544 This is less sensible for long ties, since those are more
547 Interval close_by
= get_attachment (y
549 + (dir
* details_
.intra_space_threshold_
* 0.25
550 * details_
.staff_space_
),
551 conf
->column_ranks_
);
553 conf
->attachment_x_
.intersect (close_by
);
556 conf
->attachment_x_
.widen ( - details_
.x_gap_
);
558 if (conf
->column_span_length ())
561 avoid the stems that we attach to as well. We don't do this
562 for semities (span length = 0)
564 It would be better to check D against HEAD-DIRECTION if
570 Real y
= conf
->position_
* details_
.staff_space_
* 0.5 + conf
->delta_y_
;
571 if (get_stem_extent (conf
->column_ranks_
[d
], d
, X_AXIS
).is_empty ()
572 || !get_stem_extent (conf
->column_ranks_
[d
], d
, Y_AXIS
).contains (y
))
575 conf
->attachment_x_
[d
] =
576 d
* min (d
* conf
->attachment_x_
[d
],
577 d
* (get_stem_extent (conf
->column_ranks_
[d
], d
, X_AXIS
)[-d
] - d
* details_
.stem_gap_
));
579 while (flip (&d
) != LEFT
);
585 Tie_formatting_problem::get_head_extent (int col
, Direction d
, Axis a
) const
587 Column_extent_map::const_iterator i
= head_extents_
.find (Tuple2
<int> (col
, int (d
)));
588 if (i
!= head_extents_
.end ())
589 return (*i
).second
[a
];
595 Tie_formatting_problem::get_stem_extent (int col
, Direction d
, Axis a
) const
597 Column_extent_map::const_iterator i
= stem_extents_
.find (Tuple2
<int> (col
, int (d
)));
598 if (i
!= stem_extents_
.end ())
599 return (*i
).second
[a
];
605 TIE_IDX and TIES_CONF are optional.
608 Tie_formatting_problem::score_aptitude (Tie_configuration
*conf
,
609 Tie_specification
const &spec
,
610 Ties_configuration
*ties_conf
, int tie_idx
) const
613 Real curve_y
= conf
->position_
* details_
.staff_space_
* 0.5 + conf
->delta_y_
;
614 Real tie_y
= spec
.position_
* details_
.staff_space_
* 0.5;
615 if (sign (curve_y
- tie_y
) != conf
->dir_
)
617 Real p
= details_
.wrong_direction_offset_penalty_
;
619 ties_conf
->add_tie_score (p
, tie_idx
, "wrong dir");
625 Real relevant_dist
= max (fabs (curve_y
- tie_y
) - 0.5, 0.0);
626 Real p
= details_
.vertical_distance_penalty_factor_
* convex_amplifier (1.0, 0.9, relevant_dist
);
628 ties_conf
->add_tie_score (p
, tie_idx
, "vdist");
636 if (!spec
.note_head_drul_
[d
])
639 Interval head_x
= spec
.note_head_drul_
[d
]->extent (x_refpoint_
, X_AXIS
);
640 Real dist
= head_x
.distance (conf
->attachment_x_
[d
]);
644 TODO: flatten with log or sqrt.
646 Real p
= details_
.horizontal_distance_penalty_factor_
647 * convex_amplifier (1.25, 1.0, dist
);
649 ties_conf
->add_tie_score (p
, tie_idx
,
650 (d
== LEFT
) ? "lhdist" : "rhdist");
655 while (flip (&d
) != LEFT
);
658 && ties_conf
->size () == 1)
661 Drul_array
<Grob
*> stems (0, 0);
664 if (!spec
.note_head_drul_
[d
])
667 Grob
*stem
= unsmob_grob (spec
.note_head_drul_
[d
]->get_object ("stem"));
669 && Stem::is_normal_stem (stem
))
672 while (flip (&d
) != LEFT
);
674 bool tie_stem_dir_ok
= true;
675 bool tie_position_dir_ok
= true;
676 if (stems
[LEFT
] && !stems
[RIGHT
])
677 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[LEFT
]);
678 else if (!stems
[LEFT
] && stems
[RIGHT
])
679 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[RIGHT
]);
680 else if (stems
[LEFT
] && stems
[RIGHT
]
681 && get_grob_direction (stems
[LEFT
]) == get_grob_direction (stems
[RIGHT
]))
682 tie_stem_dir_ok
= conf
->dir_
!= get_grob_direction (stems
[LEFT
]);
683 else if (spec
.position_
)
684 tie_position_dir_ok
= conf
->dir_
== sign (spec
.position_
);
686 if (!tie_stem_dir_ok
)
687 ties_conf
->add_score (details_
.same_dir_as_stem_penalty_
, "tie/stem dir");
688 if (!tie_position_dir_ok
)
689 ties_conf
->add_score (details_
.same_dir_as_stem_penalty_
, "tie/pos dir");
697 Tie_formatting_problem::head_positions_slice (int rank
) const
699 Position_extent_map::const_iterator
i (head_positions_
.find (rank
));
700 if (i
!= head_positions_
.end ())
709 Score a configuration, ie. how well these ties looks without regard
710 to the note heads that they should connect to.
713 Tie_formatting_problem::score_configuration (Tie_configuration
*conf
) const
720 Real length
= conf
->attachment_x_
.length ();
723 = peak_around (0.33 * details_
.min_length_
, details_
.min_length_
, length
);
724 conf
->add_score (details_
.min_length_penalty_factor_
725 * length_penalty
, "minlength");
727 Real tip_pos
= conf
->position_
+ conf
->delta_y_
/ 0.5 * details_
.staff_space_
;
728 Real tip_y
= tip_pos
* details_
.staff_space_
* 0.5;
729 Real height
= conf
->height (details_
);
731 Real top_y
= tip_y
+ conf
->dir_
* height
;
732 Real top_pos
= 2 * top_y
/ details_
.staff_space_
;
733 Real round_top_pos
= rint (top_pos
);
734 if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
,
736 && Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
) > top_y
)
739 details_
.staff_line_collision_penalty_
740 * peak_around (0.1 * details_
.center_staff_line_clearance_
,
741 details_
.center_staff_line_clearance_
,
742 fabs (top_pos
- round_top_pos
)),
746 int rounded_tip_pos
= int (rint (tip_pos
));
747 if (Staff_symbol_referencer::on_line (details_
.staff_symbol_referencer_
, rounded_tip_pos
)
748 && (head_positions_slice (conf
->column_ranks_
[LEFT
]).contains (rounded_tip_pos
)
749 || head_positions_slice (conf
->column_ranks_
[RIGHT
]).contains (rounded_tip_pos
)
750 || abs (rounded_tip_pos
) < 2 * Staff_symbol_referencer::staff_radius (details_
.staff_symbol_referencer_
))
753 conf
->add_score (details_
.staff_line_collision_penalty_
754 * peak_around (0.1 * details_
.tip_staff_line_clearance_
,
755 details_
.tip_staff_line_clearance_
,
756 fabs (tip_pos
- rint (tip_pos
))),
760 if (!dot_x_
.is_empty ())
763 Real x
= dot_x_
.center ();
765 Bezier b
= conf
->get_transformed_bezier (details_
);
766 if (b
.control_point_extent (X_AXIS
).contains (x
))
768 Real y
= b
.get_other_coordinate (X_AXIS
, x
);
770 for (set
<int>::const_iterator
i (dot_positions_
.begin ());
771 i
!= dot_positions_
.end (); i
++)
774 conf
->add_score (details_
.dot_collision_penalty_
775 * peak_around (.1 * details_
.dot_collision_clearance_
,
776 details_
.dot_collision_clearance_
,
777 fabs (dot_pos
* details_
.staff_space_
* 0.5 - y
)),
783 conf
->scored_
= true;
787 Tie_formatting_problem::score_ties_aptitude (Ties_configuration
*ties
) const
789 if (ties
->size () != specifications_
.size ())
791 programming_error ("Huh? Mismatch between sizes.");
795 for (vsize i
= 0; i
< ties
->size (); i
++)
796 score_aptitude (&ties
->at (i
), specifications_
[i
],
801 Tie_formatting_problem::score_ties (Ties_configuration
*ties
) const
806 score_ties_configuration (ties
);
807 score_ties_aptitude (ties
);
808 ties
->scored_
= true;
812 Tie_formatting_problem::score_ties_configuration (Ties_configuration
*ties
) const
814 for (vsize i
= 0; i
< ties
->size (); i
++)
816 score_configuration (&ties
->at (i
));
817 ties
->add_tie_score (ties
->at (i
).score (), i
, "conf");
820 Real last_edge
= 0.0;
821 Real last_center
= 0.0;
822 for (vsize i
= 0; i
< ties
->size (); i
++)
824 Bezier
b (ties
->at (i
).get_transformed_bezier (details_
));
826 Real center
= b
.curve_point (0.5)[Y_AXIS
];
827 Real edge
= b
.curve_point (0.0)[Y_AXIS
];
831 if (edge
<= last_edge
)
832 ties
->add_score (details_
.tie_column_monotonicity_penalty_
, "monoton edge");
833 if (center
<= last_center
)
834 ties
->add_score (details_
.tie_column_monotonicity_penalty_
, "monoton cent");
836 ties
->add_score (details_
.tie_tie_collision_penalty_
*
837 peak_around (0.1 * details_
.tie_tie_collision_distance_
,
838 details_
.tie_tie_collision_distance_
,
839 fabs (center
- last_center
)),
841 ties
->add_score (details_
.tie_tie_collision_penalty_
*
842 peak_around (0.1 * details_
.tie_tie_collision_distance_
,
843 details_
.tie_tie_collision_distance_
,
844 fabs (edge
- last_edge
)), "tietie edge");
848 last_center
= center
;
851 if (ties
->size () > 1)
853 ties
->add_score (details_
.outer_tie_length_symmetry_penalty_factor_
854 * fabs (ties
->at (0).attachment_x_
.length () - ties
->back ().attachment_x_
.length ()),
857 ties
->add_score (details_
.outer_tie_vertical_distance_symmetry_penalty_factor_
858 * fabs (fabs (specifications_
[0].position_
* 0.5 * details_
.staff_space_
859 - (ties
->at (0).position_
* 0.5 * details_
.staff_space_
860 + ties
->at (0).delta_y_
))
862 fabs (specifications_
.back ().position_
* 0.5 * details_
.staff_space_
863 - (ties
->back ().position_
* 0.5 * details_
.staff_space_
864 + ties
->back ().delta_y_
))),
870 Generate with correct X-attachments and beziers, copying delta_y_
871 from TIES_CONFIG if necessary.
874 Tie_formatting_problem::generate_ties_configuration (Ties_configuration
const &ties_config
)
876 Ties_configuration copy
;
877 for (vsize i
= 0; i
< ties_config
.size (); i
++)
879 Tie_configuration
* ptr
= get_configuration (ties_config
[i
].position_
, ties_config
[i
].dir_
,
880 ties_config
[i
].column_ranks_
,
881 !specifications_
[i
].has_manual_delta_y_
);
882 if (specifications_
[i
].has_manual_delta_y_
)
885 = (specifications_
[i
].manual_position_
- ties_config
[i
].position_
)
886 * 0.5 * details_
.staff_space_
;
888 copy
.push_back (*ptr
);
895 Tie_formatting_problem::generate_base_chord_configuration ()
897 Ties_configuration ties_config
;
898 for (vsize i
= 0; i
< specifications_
.size (); i
++)
900 Tie_configuration conf
;
901 if (specifications_
[i
].has_manual_dir_
)
902 conf
.dir_
= specifications_
[i
].manual_dir_
;
903 if (specifications_
[i
].has_manual_position_
)
905 conf
.position_
= (int) my_round (specifications_
[i
].manual_position_
);
906 if (specifications_
[i
].has_manual_delta_y_
)
907 conf
.delta_y_
= (specifications_
[i
].manual_position_
- conf
.position_
)
908 * 0.5 * details_
.staff_space_
;
912 conf
.position_
= specifications_
[i
].position_
;
915 conf
.column_ranks_
= specifications_
[i
].column_ranks_
;
916 ties_config
.push_back (conf
);
919 set_ties_config_standard_directions (&ties_config
);
920 for (vsize i
= 0; i
< ties_config
.size (); i
++)
921 if (!specifications_
[i
].manual_position_
)
922 ties_config
[i
].position_
+= ties_config
[i
].dir_
;
924 ties_config
= generate_ties_configuration (ties_config
);
930 Tie_formatting_problem::find_best_variation (Ties_configuration
const &base
,
931 vector
<Tie_configuration_variation
> const &vars
)
933 Ties_configuration best
= base
;
936 This simply is 1-opt: we have K substitions, and we try applying
937 exactly every one for each.
939 for (vsize i
= 0; i
< vars
.size (); i
++)
941 Ties_configuration
variant (base
);
942 for (vsize j
= 0; j
< vars
[i
].index_suggestion_pairs_
.size(); j
++)
943 variant
[vars
[i
].index_suggestion_pairs_
[j
].first
] = *vars
[i
].index_suggestion_pairs_
[j
].second
;
945 variant
.reset_score ();
946 score_ties (&variant
);
948 if (variant
.score () < best
.score ())
960 Tie_formatting_problem::generate_optimal_configuration ()
962 Ties_configuration base
= generate_base_chord_configuration ();
965 vector
<Tie_configuration_variation
> vars
;
966 if (specifications_
.size () > 1)
967 vars
= generate_collision_variations (base
);
969 vars
= generate_single_tie_variations (base
);
971 Ties_configuration best
= find_best_variation (base
, vars
);
973 if (specifications_
.size () > 1)
975 vars
= generate_extremal_tie_variations (best
);
976 best
= find_best_variation (best
, vars
);
982 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration
*tie_configs
)
984 if (tie_configs
->empty ())
987 if (!tie_configs
->at (0).dir_
)
989 if (tie_configs
->size () == 1)
990 tie_configs
->at (0).dir_
= Direction (sign (tie_configs
->at (0).position_
));
992 if (!tie_configs
->at (0).dir_
)
993 tie_configs
->at (0).dir_
994 = (tie_configs
->size() > 1) ? DOWN
: details_
.neutral_direction_
;
997 if (!tie_configs
->back ().dir_
)
998 tie_configs
->back ().dir_
= UP
;
1003 for (vsize i
= 1; i
< tie_configs
->size (); i
++)
1005 Real diff
= (tie_configs
->at (i
).position_
1006 -tie_configs
->at (i
-1).position_
);
1009 = specifications_
[i
].column_span () - specifications_
[i
-1].column_span ();
1010 if (span_diff
&& fabs (diff
) <= 2)
1013 tie_configs
->at (i
).dir_
= UP
;
1014 else if (span_diff
< 0)
1015 tie_configs
->at (i
-1).dir_
= DOWN
;
1017 else if (fabs (diff
) <= 1)
1019 if (!tie_configs
->at (i
-1).dir_
)
1020 tie_configs
->at (i
-1).dir_
= DOWN
;
1021 if (!tie_configs
->at (i
).dir_
)
1022 tie_configs
->at (i
).dir_
= UP
;
1026 for (vsize i
= 1; i
+ 1 < tie_configs
->size (); i
++)
1028 Tie_configuration
&conf
= tie_configs
->at (i
);
1032 Direction position_dir
=
1033 Direction (sign (conf
.position_
));
1035 position_dir
= DOWN
;
1037 conf
.dir_
= position_dir
;
1041 vector
<Tie_configuration_variation
>
1042 Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration
const &ties
) const
1044 vector
<Tie_configuration_variation
> vars
;
1046 for (int i
= 1; i
<= details_
.multi_tie_region_size_
; i
++)
1048 Drul_array
<Tie_configuration
*> configs (0, 0);
1051 const Tie_configuration
&config
= boundary (ties
, d
, 0);
1052 if (config
.dir_
== d
1053 && !boundary (specifications_
, d
, 0).has_manual_position_
)
1055 Tie_configuration_variation var
;
1056 configs
[d
] = get_configuration (config
.position_
+ d
* i
, d
,
1057 config
.column_ranks_
,
1059 var
.add_suggestion((d
== DOWN
) ? 0 : ties
.size () - 1,
1061 vars
.push_back (var
);
1064 while (flip (&d
) != DOWN
);
1065 if (configs
[LEFT
] && configs
[RIGHT
])
1067 Tie_configuration_variation var
;
1068 var
.add_suggestion(0, configs
[DOWN
]);
1069 var
.add_suggestion(ties
.size() - 1, configs
[UP
]);
1070 vars
.push_back (var
);
1077 vector
<Tie_configuration_variation
>
1078 Tie_formatting_problem::generate_single_tie_variations (Ties_configuration
const &ties
) const
1080 vector
<Tie_configuration_variation
> vars
;
1082 int sz
= details_
.single_tie_region_size_
;
1083 if (specifications_
[0].has_manual_position_
)
1085 for (int i
= 0; i
< sz
; i
++)
1091 && ties
[0].dir_
== d
)
1094 int p
= ties
[0].position_
+ i
* d
;
1096 if (!specifications_
[0].has_manual_dir_
1097 || d
== specifications_
[0].manual_dir_
)
1099 Tie_configuration_variation var
;
1100 var
.add_suggestion(0,
1101 get_configuration (p
,
1102 d
, specifications_
[0].column_ranks_
,
1103 !specifications_
[0].has_manual_delta_y_
));
1104 vars
.push_back (var
);
1107 while (flip (&d
) != LEFT
);
1113 vector
<Tie_configuration_variation
>
1114 Tie_formatting_problem::generate_collision_variations (Ties_configuration
const &ties
) const
1116 Real center_distance_tolerance
= 0.25;
1118 vector
<Tie_configuration_variation
> vars
;
1119 Real last_center
= 0.0;
1120 for (vsize i
= 0; i
< ties
.size (); i
++)
1122 Bezier
b (ties
[i
].get_transformed_bezier (details_
));
1124 Real center
= b
.curve_point (0.5)[Y_AXIS
];
1128 if (center
<= last_center
+ center_distance_tolerance
)
1130 if (!specifications_
[i
].has_manual_dir_
)
1132 Tie_configuration_variation var
;
1133 var
.add_suggestion(i
,
1134 get_configuration (specifications_
[i
].position_
1138 ties
[i
].column_ranks_
,
1139 !specifications_
[i
].has_manual_delta_y_
1142 vars
.push_back (var
);
1145 if (!specifications_
[i
-1].has_manual_dir_
)
1147 Tie_configuration_variation var
;
1148 var
.add_suggestion(i
-1,
1149 get_configuration (specifications_
[i
-1].position_
1152 specifications_
[i
-1].column_ranks_
,
1153 !specifications_
[i
-1].has_manual_delta_y_
));
1155 vars
.push_back (var
);
1158 if (i
== 1 && !specifications_
[i
-1].has_manual_position_
1159 && ties
[i
-1].dir_
== DOWN
)
1161 Tie_configuration_variation var
;
1162 var
.add_suggestion(i
-1,
1163 get_configuration (specifications_
[i
-1].position_
- 1, DOWN
,
1164 specifications_
[i
-1].column_ranks_
,
1165 !specifications_
[i
-1].has_manual_delta_y_
1167 vars
.push_back (var
);
1169 if (i
== ties
.size () && !specifications_
[i
].has_manual_position_
1170 && ties
[i
].dir_
== UP
)
1172 Tie_configuration_variation var
;
1173 var
.add_suggestion(i
,
1174 get_configuration (specifications_
[i
].position_
1176 specifications_
[i
].column_ranks_
,
1177 !specifications_
[i
].has_manual_delta_y_
1179 vars
.push_back (var
);
1182 else if (dot_positions_
.find (ties
[i
].position_
) != dot_positions_
.end ()
1183 && !specifications_
[i
].has_manual_position_
)
1185 Tie_configuration_variation var
;
1186 var
.add_suggestion(i
,
1187 get_configuration (ties
[i
].position_
+ ties
[i
].dir_
,
1189 ties
[i
].column_ranks_
,
1190 !specifications_
[i
].has_manual_delta_y_
1192 vars
.push_back (var
);
1197 last_center
= center
;
1205 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs
)
1208 for (SCM s
= manual_configs
;
1209 scm_is_pair (s
) && k
< specifications_
.size (); s
= scm_cdr (s
))
1211 SCM entry
= scm_car (s
);
1212 if (scm_is_pair (entry
))
1214 Tie_specification
&spec
= specifications_
[k
];
1216 if (scm_is_number (scm_car (entry
)))
1218 spec
.has_manual_position_
= true;
1219 spec
.manual_position_
= scm_to_double (scm_car (entry
));
1220 spec
.has_manual_delta_y_
= (scm_inexact_p (scm_car (entry
)) == SCM_BOOL_T
);
1223 if (scm_is_number (scm_cdr (entry
)))
1225 spec
.has_manual_dir_
= true;
1226 spec
.manual_dir_
= Direction (scm_to_int (scm_cdr (entry
)));
1235 Tie_formatting_problem::set_debug_scoring (Ties_configuration
const &base
)
1237 #if DEBUG_TIE_SCORING
1238 if (to_boolean (x_refpoint_
->layout ()
1239 ->lookup_variable (ly_symbol2scm ("debug-tie-scoring"))))
1241 for (vsize i
= 0; i
< base
.size (); i
++)
1243 string card
= base
.complete_tie_card (i
);
1244 specifications_
[i
].tie_grob_
->set_property ("quant-score",
1245 ly_string2scm (card
));