]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie-formatting-problem.cc
* lily/tie-formatting-problem.cc (set_chord_outline): initialize
[lilypond.git] / lily / tie-formatting-problem.cc
1 /*
2   tie-formatting-problem.cc -- implement Tie_formatting_problem
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2005--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7
8 */
9
10 #include "tie-formatting-problem.hh"
11
12 #include "bezier.hh" 
13 #include "directional-element-interface.hh"
14 #include "item.hh"
15 #include "libc-extension.hh"
16 #include "misc.hh"
17 #include "note-head.hh"
18 #include "rhythmic-head.hh"
19 #include "spanner.hh" 
20 #include "staff-symbol-referencer.hh"
21 #include "stem.hh"
22 #include "tie-configuration.hh"
23 #include "tie.hh"
24 #include "warn.hh"
25
26
27 void
28 Tie_formatting_problem::print_ties_configuration (Ties_configuration const *ties)
29 {
30   for (vsize i = 0; i < ties->size (); i++)
31     {
32       char const *man_pos = (specifications_[i].has_manual_position_) ? "(M)" : "";
33       char const *man_dir = (specifications_[i].has_manual_dir_) ? "(M)" : "";
34       char const *dir = (ties->at (i).dir_ == UP) ? "up" : "dn";
35       
36       printf ("(P%d%s, %s%s) ", ties->at (i).position_, man_pos, dir, man_dir);
37     }
38   printf ("\n");
39 }
40
41 Interval
42 Tie_formatting_problem::get_attachment (Real y) const
43 {
44   Interval attachments;
45   Direction d = LEFT;
46   do
47     {
48       attachments[d] = skyline_height (chord_outlines_[d], y, -d);
49     }
50   while (flip (&d) != LEFT);
51   
52   return attachments;
53 }
54
55 Tie_formatting_problem::Tie_formatting_problem()
56 {
57   x_refpoint_ = 0;
58 }
59
60 Tie_formatting_problem::~Tie_formatting_problem ()
61 {
62   for (Tie_configuration_map::const_iterator i (possibilities_.begin ());
63        i != possibilities_.end (); i++)
64     delete (*i).second;
65 }
66
67 void
68 Tie_formatting_problem::set_chord_outline (vector<Item*> bounds,
69                                            Direction dir)
70 {
71   Real staff_space = Staff_symbol_referencer::staff_space (bounds[0]);
72
73   vector<Box> boxes;
74   vector<Box> head_boxes;
75
76   Grob *stem = 0;
77   for (vsize i = 0; i < bounds.size (); i++)
78     {
79       Grob *head = bounds[i];
80       if (!Note_head::has_interface (head))
81         continue;
82       
83       if (!stem)
84         stem = unsmob_grob (head->get_object ("stem"));
85           
86       Real p = Staff_symbol_referencer::get_position (head);
87       Interval y ((p-1) * 0.5 * staff_space,
88                   (p+1) * 0.5 * staff_space);
89
90       Interval x = head->extent (x_refpoint_, X_AXIS);
91       head_boxes.push_back (Box (x, y));
92       boxes.push_back (Box (x, y));
93
94       Grob *dots = Rhythmic_head::get_dots (head);
95       if (dir == LEFT && dots)
96         {
97           Interval x = dots->extent (x_refpoint_, X_AXIS);
98           int p = int (Staff_symbol_referencer::get_position (dots));
99
100           dot_positions_.insert (p);
101           dot_x_.unite (x);
102
103           Interval y (dots->extent (dots, Y_AXIS));
104           y.translate (p * staff_space * 0.5);
105           
106           boxes.push_back (Box (x, y));
107         }
108     }
109
110   chord_outlines_[dir] = empty_skyline (-dir);
111   if (bounds[0]->break_status_dir ())
112     {
113       Real x = robust_relative_extent (bounds[0],  x_refpoint_, X_AXIS)[-dir];
114       chord_outlines_[dir].at (0).height_ = x; 
115     }
116   else
117     {
118       Interval x;      
119       for (vsize i = 0; i < head_boxes.size (); i++)
120         {
121           x.unite (head_boxes[i][X_AXIS]);
122         }
123
124       chord_outlines_[dir].at (0).height_ = x[dir];
125     }
126
127   for (vsize i = 0; i < boxes.size (); i++)
128     insert_extent_into_skyline (&chord_outlines_[dir]  ,
129                                 boxes[i], Y_AXIS, -dir);
130
131   if (stem
132       && !Stem::is_invisible (stem))
133     {
134       Interval x;
135       x.add_point (stem->relative_coordinate (x_refpoint_, X_AXIS));
136       x.widen (staff_space / 20); // ugh.
137       Interval y;
138       y.add_point (Stem::stem_end_position (stem) * staff_space * .5);
139
140       Direction stemdir = get_grob_direction (stem);
141       y.add_point (Stem::head_positions (stem)[-stemdir]
142                    * staff_space * .5);
143           
144       insert_extent_into_skyline (&chord_outlines_[dir], Box (x,y), Y_AXIS, -dir);
145
146       stem_extents_[dir].unite (Box (x,y));
147
148       if (dir == LEFT)
149         {
150           Box flag_box = Stem::get_translated_flag (stem).extent_box ();
151           flag_box.translate( Offset (x[RIGHT], X_AXIS));
152           insert_extent_into_skyline (&chord_outlines_[dir], flag_box,
153                                       Y_AXIS, -dir);
154         }
155     }
156   
157   Direction updowndir = DOWN;
158   do
159     {
160       Interval x;
161       Interval y;
162       if (head_boxes.size())
163         {
164           Box b = boundary (head_boxes, updowndir, 0);
165           x = b[X_AXIS];
166           x[-dir] =  b[X_AXIS].linear_combination (-dir / 2);
167           y[-updowndir] = b[Y_AXIS][updowndir];
168           y[updowndir] = updowndir * infinity_f;
169         }
170
171       if (!x.is_empty ())
172         insert_extent_into_skyline (&chord_outlines_[dir],
173                                     Box (x,y),
174                                     Y_AXIS, -dir);
175     }
176   while (flip (&updowndir) != DOWN);
177   
178   head_extents_[dir].set_empty ();
179   for (vsize i = 0; i < head_boxes.size (); i++)
180     {
181       head_extents_[dir].unite (head_boxes[i]);
182     }
183 }
184
185
186 void
187 Tie_formatting_problem::from_tie (Grob *tie)
188 {
189   vector<Grob*> ties;
190   ties.push_back (tie);
191   from_ties (ties);
192
193   details_.from_grob (tie);
194 }
195
196 Grob *
197 Tie_formatting_problem::common_x_refpoint () const
198 {
199   return x_refpoint_;
200 }
201
202 void
203 Tie_formatting_problem::from_ties (vector<Grob*> const &ties)
204 {
205   if (ties.empty ())
206     return;
207   
208   x_refpoint_ = ties[0];
209   for (vsize i = 0; i < ties.size (); i++)
210     {
211       x_refpoint_ = dynamic_cast<Spanner*> (ties[i])->get_bound (LEFT)->common_refpoint (x_refpoint_, X_AXIS); 
212       x_refpoint_ = dynamic_cast<Spanner*> (ties[i])->get_bound (RIGHT)->common_refpoint (x_refpoint_, X_AXIS); 
213     }
214
215   details_.from_grob (ties[0]);
216   
217   Direction d = LEFT;
218   do
219     {
220       vector<Item*> bounds;
221       
222       for (vsize i = 0; i < ties.size (); i++)
223         {
224           Item *it = dynamic_cast<Spanner*> (ties[i])->get_bound (d);
225                                              
226           bounds.push_back (it);
227         }
228       
229       set_chord_outline (bounds, d);
230     }
231   while (flip (&d) != LEFT);
232
233
234   for (vsize i = 0; i < ties.size (); i++)
235     {
236       Tie_specification spec;
237       
238       if (scm_is_number (ties[i]->get_property_data (ly_symbol2scm ("direction"))))
239         {
240           spec.manual_dir_ = to_dir (ties[i]->get_property ("direction"));
241           spec.has_manual_dir_ = true;
242         }
243           
244       spec.position_ = Tie::get_position (ties[i]);
245
246       do
247         {
248           spec.note_head_drul_[d] = Tie::head (ties[i], d);
249         }
250       while (flip (&d) != LEFT);
251       
252       specifications_.push_back (spec);
253     }
254 }
255
256 void
257 Tie_formatting_problem::from_semi_ties (vector<Grob*> const &lv_ties, Direction head_dir)
258 {
259   if (lv_ties.empty ())
260     return;
261   
262   details_.from_grob (lv_ties[0]);
263   vector<Item*> heads;
264   
265   for (vsize i = 0; i < lv_ties.size (); i++)
266     {
267       Tie_specification spec;
268       Item *head = unsmob_item (lv_ties[i]->get_object ("note-head"));
269        
270       if (!head)
271         programming_error ("LV tie without head?!");
272
273       if (head)
274         {
275           spec.position_ = int (Staff_symbol_referencer::get_position (head));
276         }
277
278       spec.note_head_drul_[head_dir] = head;
279       heads.push_back (head);
280       specifications_.push_back (spec);
281     }
282
283   x_refpoint_ = lv_ties [0];
284   for (vsize i = 0; i < lv_ties.size (); i++)
285     x_refpoint_ = lv_ties[i]->common_refpoint (x_refpoint_, X_AXIS); 
286   for (vsize i = 0; i < heads.size (); i++)
287     x_refpoint_ = heads[i]->common_refpoint (x_refpoint_, X_AXIS); 
288
289   set_chord_outline (heads, head_dir);
290
291   Real extremal = head_dir * infinity_f;   
292
293   for (vsize i = 0; i < chord_outlines_[head_dir].size (); i++)
294     {
295       extremal = head_dir * min (head_dir * extremal,
296                                    head_dir * chord_outlines_[head_dir][i].height_);
297     }
298
299   Skyline_entry right_entry;
300   right_entry.width_.set_full ();
301   right_entry.height_ = extremal - head_dir * 1.5;
302   
303   chord_outlines_[-head_dir].push_back (right_entry);
304 }
305
306
307 Tie_specification
308 Tie_formatting_problem::get_tie_specification (int i) const
309 {
310   return specifications_[i];
311 }
312
313
314 Tie_configuration*
315 Tie_formatting_problem::get_configuration (int pos, Direction dir) const
316 {
317   pair<int,int> key (pos, dir);
318   Tie_configuration_map::const_iterator f = possibilities_.find (key);
319                                                               
320   if (f != possibilities_.end ())
321     {
322       return (*f).second;
323     }
324
325   
326   Tie_configuration *conf = generate_configuration (pos, dir);
327   ((Tie_formatting_problem*) this)->possibilities_[key] = conf;
328   return conf;
329 }
330
331 Tie_configuration*
332 Tie_formatting_problem::generate_configuration (int pos, Direction dir) const
333 {
334   Tie_configuration *conf = new Tie_configuration;
335   conf->position_ = pos;
336   conf->dir_ = dir;
337   Real y = conf->position_ * 0.5 * details_.staff_space_;
338
339
340   bool y_tune = true;
341   if (dot_positions_.find (pos) != dot_positions_.end ())
342     {
343       conf->delta_y_ += 0.25 * details_.staff_space_;
344       y_tune = false;
345     }
346                 
347            
348   
349   if (y_tune
350       && max (fabs (head_extents_[LEFT][Y_AXIS][dir] - y),
351               fabs (head_extents_[RIGHT][Y_AXIS][dir] - y)) < 0.25
352       && !Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos))
353     {
354       conf->delta_y_ =
355         (head_extents_[LEFT][Y_AXIS][dir] - y)
356         + dir * details_.outer_tie_vertical_gap_;
357     }
358
359   if (y_tune)
360     {
361       conf->attachment_x_ = get_attachment (y + conf->delta_y_);
362       Real h =  conf->height (details_);
363       
364       /*
365         TODO:
366
367         - should make sliding criterion, should flatten ties if
368
369         - they're just the wrong (ie. touching line at top & bottom)
370         size.
371         
372        */
373       if (h < details_.intra_space_threshold_ * 0.5 * details_.staff_space_)
374         {
375           if (!Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos)
376               && abs (pos) < 2 * Staff_symbol_referencer::staff_radius (details_.staff_symbol_referencer_))
377             {
378               conf->center_tie_vertically (details_);
379             }
380           else if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos))
381             {
382               conf->delta_y_ += dir *
383                 details_.tip_staff_line_clearance_ * 0.5 *  details_.staff_space_;
384             }
385         }
386       else 
387         {
388           Real top_y = y + conf->delta_y_ + conf->dir_ * h;
389           Real top_pos = top_y / (0.5*details_.staff_space_);
390           int round_pos = int (my_round (top_pos));
391
392           /* TODO: should use other variable? */
393           Real clearance = details_.center_staff_line_clearance_;
394           if (fabs (top_pos - round_pos) < clearance
395               && Staff_symbol_referencer::on_staff_line (details_.staff_symbol_referencer_,
396                                                          round_pos))
397             {
398               Real new_y = (round_pos + clearance * conf->dir_) * 0.5 * details_.staff_space_;
399               conf->delta_y_ = (new_y - top_y);
400             }
401         }
402     }
403   
404   conf->attachment_x_ = get_attachment (y + conf->delta_y_);
405   if (conf->height (details_) < details_.intra_space_threshold_ * 0.5 * details_.staff_space_)
406     {
407       /*
408         This is less sensible for long ties, since those are more
409         horizontal.
410       */
411       Interval close_by = get_attachment (y
412                                           + conf->delta_y_
413                                           + (dir * details_.intra_space_threshold_ * 0.25
414                                              * details_.staff_space_));
415       
416       conf->attachment_x_.intersect (close_by);
417     }
418
419   conf->attachment_x_.widen ( - details_.x_gap_);
420
421   Direction d = LEFT;
422   do
423     {
424       Real y = conf->position_ * details_.staff_space_ * 0.5 + conf->delta_y_;
425       if (stem_extents_[d][X_AXIS].is_empty ()
426           || !stem_extents_[d][Y_AXIS].contains (y))
427         continue;
428
429       conf->attachment_x_[d] =
430         d* min (d * conf->attachment_x_[d],
431                 d * (stem_extents_[d][X_AXIS][-d] - d * details_.stem_gap_));
432     }
433   while (flip (&d) != LEFT);
434   return conf;
435 }
436
437 /**
438    TIE_IDX and TIES_CONF are optional.
439  */
440 Real
441 Tie_formatting_problem::score_aptitude (Tie_configuration *conf,
442                                         Tie_specification const &spec,
443                                         Ties_configuration *ties_conf, int tie_idx) const
444 {
445   Real penalty = 0.0;
446   Real curve_y = conf->position_ * details_.staff_space_ * 0.5 + conf->delta_y_;
447   Real tie_y = spec.position_ * details_.staff_space_ * 0.5;
448   if (sign (curve_y - tie_y) != conf->dir_)
449     {
450       Real p =  details_.wrong_direction_offset_penalty_;
451       if (ties_conf)
452         ties_conf->add_tie_score (p, tie_idx, "wrong dir");
453       else
454         penalty += p;
455     }
456
457   {
458     Real p = details_.vertical_distance_penalty_factor_ * fabs (curve_y - tie_y);
459     if (ties_conf)
460       ties_conf->add_tie_score (p, tie_idx, "vdist");
461     else
462       penalty += p; 
463   }
464   
465   Direction d = LEFT;
466   do
467     {
468       if (!spec.note_head_drul_[d])
469         continue;
470       
471       Interval head_x = spec.note_head_drul_[d]->extent (x_refpoint_, X_AXIS);
472       Real dist = head_x.distance (conf->attachment_x_[d]);
473
474       /*
475         TODO: flatten with log or sqrt.
476        */
477       Real p = details_.horizontal_distance_penalty_factor_ * dist;
478       if (ties_conf)
479         ties_conf->add_tie_score (p, tie_idx,
480                                   (d == LEFT) ? "lhdist" : "rhdist");
481       else
482         penalty += p;
483     }
484   while (flip (&d) != LEFT);
485
486   return penalty;
487 }
488
489 void
490 Tie_formatting_problem::score_configuration (Tie_configuration *conf) const
491 {
492   if (conf->scored_)
493     {
494       return ;
495     }
496   
497   Real length = conf->attachment_x_.length ();
498
499   conf->add_score (details_.min_length_penalty_factor_
500                    * peak_around (0.33 * details_.min_length_, details_.min_length_, length),
501                    "minlength");
502   
503   Real tip_pos = conf->position_ + conf->delta_y_ / 0.5 * details_.staff_space_;
504   Real tip_y = tip_pos * details_.staff_space_ * 0.5;
505   Real height =  conf->height (details_);
506
507   Real top_y = tip_y + conf->dir_ * height;
508   Real top_pos = 2 * top_y / details_.staff_space_;
509   Real round_top_pos = rint (top_pos);
510   if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_,
511                                                 int (round_top_pos))
512       && Staff_symbol_referencer::staff_radius (details_.staff_symbol_referencer_) > top_y)
513     {
514       conf->add_score (
515         details_.staff_line_collision_penalty_
516         * peak_around (0.1 * details_.center_staff_line_clearance_,
517                      details_.center_staff_line_clearance_,
518                        fabs (top_pos - round_top_pos)),
519         "line center");
520     }
521   
522   if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_,
523                                         int (rint (tip_pos))))
524     {
525       conf->add_score (details_.staff_line_collision_penalty_
526                        * peak_around (0.1 * details_.tip_staff_line_clearance_,
527                                       details_.tip_staff_line_clearance_,
528                                       fabs (tip_pos - rint (tip_pos))),
529                        "tipline");
530     }
531
532   if (!dot_x_.is_empty ())
533     {
534       /* use left edge? */
535       Real x = dot_x_.center ();
536       
537       Bezier b = conf->get_transformed_bezier (details_);
538       if (b.control_point_extent (X_AXIS).contains (x))
539         {
540           Real y = b.get_other_coordinate (X_AXIS, x);
541
542           for (set<int>::const_iterator i (dot_positions_.begin ());
543                i != dot_positions_.end (); i ++)
544             {
545               int dot_pos = (*i);
546               conf->add_score (details_.dot_collision_penalty_
547                 * peak_around (.1 * details_.dot_collision_clearance_,
548                                details_.dot_collision_clearance_,
549                                fabs (dot_pos * details_.staff_space_ * 0.5 - y)),
550                                "dot collision");
551             }
552         }
553     }
554
555   conf->scored_ = true;
556 }
557
558 Tie_configuration
559 Tie_formatting_problem::find_optimal_tie_configuration (Tie_specification const &spec) const
560 {
561   vector<Tie_configuration*> confs;
562
563   int pos = spec.position_;
564   Direction dir = spec.manual_dir_;
565
566   for (int i = 0; i < details_.single_tie_region_size_; i ++)
567     {
568       confs.push_back (generate_configuration (pos + i * dir, dir));
569     }
570
571   vector<Real> scores;
572
573   int best_idx = -1;
574   Real best_score = 1e6;
575   for (vsize i = 0; i < confs.size (); i ++)
576     {
577       score_configuration (confs[i]);
578       Real score = score_aptitude (confs[i], spec, 0, 0)
579         + confs[i]->score ();
580
581       if (score < best_score)
582         {
583           best_score = score;
584           best_idx = i;
585         }
586     }
587
588   Tie_configuration best = *confs[best_idx];
589   for (vsize i = 0; i < confs.size (); i++)
590     delete confs[i];
591
592   return best;
593 }
594
595 Tie_specification::Tie_specification ()
596 {
597   has_manual_position_ = false;
598   has_manual_dir_ = false;
599   position_ = 0;
600   manual_position_ = 0;
601   manual_dir_ = CENTER;
602   note_head_drul_[LEFT] =
603     note_head_drul_[RIGHT] = 0;
604 }
605
606
607 void
608 Tie_formatting_problem::score_ties_aptitude (Ties_configuration *ties) const
609 {
610   if  (ties->size () != specifications_.size ())
611     {
612       programming_error ("Huh? Mismatch between sizes.");
613       return;
614     }
615
616   for (vsize i = 0; i < ties->size (); i++)
617     score_aptitude (&ties->at (i), specifications_[i],
618                     ties, i);
619 }
620
621 void
622 Tie_formatting_problem::score_ties (Ties_configuration *ties) const
623 {
624   if (ties->scored_)
625     return;
626   
627   score_ties_configuration (ties);
628   score_ties_aptitude (ties);
629   ties->scored_ = true;
630 }
631
632 void
633 Tie_formatting_problem::score_ties_configuration (Ties_configuration *ties) const
634 {
635   for (vsize i = 0; i < ties->size (); i++)
636     {
637       score_configuration (&ties->at (i));
638       ties->add_tie_score (ties->at (i).score (), i, "conf");
639     }
640   
641   Real last_edge = 0.0;
642   Real last_center = 0.0;
643   for (vsize i = 0; i < ties->size (); i++)
644     {
645       Bezier b (ties->at (i).get_transformed_bezier (details_));
646         
647       Real center = b.curve_point (0.5)[Y_AXIS];
648       Real edge = b.curve_point (0.0)[Y_AXIS];
649       
650       if (i)
651         {
652           if (edge <= last_edge)
653             ties->add_score (details_.tie_column_monotonicity_penalty_, "monoton edge");
654           if (center <= last_center)
655             ties->add_score (details_.tie_column_monotonicity_penalty_, "monoton cent");
656
657           ties->add_score (details_.tie_tie_collision_penalty_ *
658                            peak_around (0.1 * details_.tie_tie_collision_distance_,
659                                         details_.tie_tie_collision_distance_,
660                                         fabs (center - last_center)),
661                            "tietie center");
662           ties->add_score (details_.tie_tie_collision_penalty_ *
663                            peak_around (0.1 * details_.tie_tie_collision_distance_,
664                                         details_.tie_tie_collision_distance_,
665                                         fabs (edge - last_edge)), "tietie edge");
666         }
667
668       last_edge = edge;
669       last_center = center;
670     }
671
672   ties->add_score (details_.outer_tie_length_symmetry_penalty_factor_
673                    * fabs (ties->at (0).attachment_x_.length () - ties->back ().attachment_x_.length ()),
674                    "length symm");
675   
676   ties->add_score (details_.outer_tie_vertical_distance_symmetry_penalty_factor_
677                    * fabs (fabs (specifications_[0].position_ * 0.5 * details_.staff_space_
678                                  - (ties->at (0).position_ * 0.5 * details_.staff_space_
679                                     + ties->at (0).delta_y_))
680                            -
681                            fabs (specifications_.back ().position_ * 0.5 * details_.staff_space_
682                                  - (ties->back ().position_ * 0.5 * details_.staff_space_
683                                     + ties->back ().delta_y_))),
684                    "pos symmetry");
685 }
686
687 /*
688   Generate with correct X-attachments and beziers, copying delta_y_
689   from TIES_CONFIG if necessary.
690 */
691 Ties_configuration
692 Tie_formatting_problem::generate_ties_configuration (Ties_configuration const &ties_config)
693 {
694   Ties_configuration copy;
695   for (vsize i = 0; i < ties_config.size (); i++)
696     {
697       Tie_configuration * ptr = get_configuration (ties_config[i].position_, ties_config[i].dir_);
698       if (specifications_[i].has_manual_position_)
699         {
700           ptr->delta_y_
701             = (specifications_[i].manual_position_ - ties_config[i].position_)
702             * 0.5 * details_.staff_space_;
703         }
704       copy.push_back (*ptr);
705     }
706   
707   return copy;
708 }
709
710 Ties_configuration
711 Tie_formatting_problem::generate_base_chord_configuration () 
712 {
713   Ties_configuration ties_config;
714   for (vsize i = 0;  i < specifications_.size (); i ++)
715     {
716       Tie_configuration conf;
717       if (specifications_[i].has_manual_dir_)
718         conf.dir_ = specifications_[i].manual_dir_;
719       if (specifications_[i].has_manual_position_)
720         {
721           conf.position_ = (int) my_round (specifications_[i].manual_position_);
722           conf.delta_y_ = (specifications_[i].manual_position_ - conf.position_)
723             * 0.5 * details_.staff_space_;
724         }
725       else
726         {
727           conf.position_ = specifications_[i].position_;
728         }
729       ties_config.push_back (conf);
730     }
731
732   set_ties_config_standard_directions (&ties_config);
733   for (vsize i = 0; i < ties_config.size (); i++)
734     if (!specifications_[i].manual_position_)
735       ties_config[i].position_ += ties_config[i].dir_;
736
737   ties_config = generate_ties_configuration (ties_config);
738   
739   return ties_config;
740 }
741
742 Ties_configuration
743 Tie_formatting_problem::find_best_variation (Ties_configuration const &base,
744                                              vector<Tie_configuration_variation> vars)
745 {
746   Ties_configuration best = base;
747   
748   /*
749     This simply is 1-opt: we have K substitions, and we try applying
750     exactly every one for each.
751   */
752   for (vsize i = 0; i < vars.size (); i++)
753     {
754       Ties_configuration variant (base);
755       variant[vars[i].index_] = *vars[i].suggestion_;
756
757       variant.reset_score ();
758       score_ties (&variant);
759       
760       if (variant.score () < best.score ())
761         {
762           best = variant;
763         }
764     }
765
766   return best;
767 }
768   
769                                        
770
771 Ties_configuration
772 Tie_formatting_problem::generate_optimal_chord_configuration ()
773 {
774   
775   Ties_configuration base = generate_base_chord_configuration ();
776   vector<Tie_configuration_variation> vars = generate_collision_variations (base);
777   
778   score_ties (&base);
779   Ties_configuration best = find_best_variation (base, vars);
780   vars = generate_extremal_tie_variations (best);
781   best = find_best_variation (best, vars);
782
783   return best;
784 }
785
786 void
787 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration *tie_configs)
788 {
789   if (tie_configs->empty ())
790     return ;
791   
792   if (!tie_configs->at (0).dir_)
793     tie_configs->at (0).dir_ = DOWN;
794   if (!tie_configs->back ().dir_)
795     tie_configs->back ().dir_ = UP;
796
797   /*
798     Seconds
799    */
800   for (vsize i = 1; i < tie_configs->size (); i++)
801     {
802       Real diff = (tie_configs->at (i-1).position_
803                    - tie_configs->at (i).position_);
804
805       if (fabs (diff) <= 1)
806         {
807           if (!tie_configs->at (i-1).dir_)
808             tie_configs->at (i-1).dir_ = DOWN;
809           if (!tie_configs->at (i).dir_)
810             tie_configs->at (i).dir_ = UP;
811         }
812     }
813
814   for (vsize i = 1; i < tie_configs->size() - 1; i++)
815     {
816       Tie_configuration &conf = tie_configs->at (i);
817       if (conf.dir_)
818         continue;
819
820       Direction position_dir =
821         Direction (sign (conf.position_));
822       if (!position_dir)
823         position_dir = DOWN;
824
825       conf.dir_ = position_dir;
826     }
827 }
828
829 Tie_configuration_variation::Tie_configuration_variation ()
830 {
831   index_ = 0;
832   suggestion_ = 0;
833 }
834
835 vector<Tie_configuration_variation>
836 Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration const &ties) const
837 {
838   vector<Tie_configuration_variation> vars;
839   Direction d = DOWN;
840   do
841     {
842       if (boundary (ties, d, 0).dir_ == d
843           && !boundary (specifications_, d, 0).has_manual_position_)
844         for (int i = 1; i <= details_.multi_tie_region_size_; i++)
845           {
846             Tie_configuration_variation var;
847             var.index_ = (d == DOWN) ? 0 : ties.size () - 1;
848             var.suggestion_ = get_configuration (boundary (ties, d, 0).position_
849                                                  + d * i, d);
850             vars.push_back (var);
851           }
852     }
853   while (flip (&d) !=  DOWN);
854
855   return vars;
856 }
857
858
859 vector<Tie_configuration_variation>
860 Tie_formatting_problem::generate_collision_variations (Ties_configuration const &ties) const
861 {
862   Real center_distance_tolerance = 0.25;
863   
864   vector<Tie_configuration_variation> vars;
865   Real last_center = 0.0;
866   for (vsize i = 0; i < ties.size (); i++)
867     {
868       Bezier b (ties[i].get_transformed_bezier (details_));
869         
870       Real center = b.curve_point (0.5)[Y_AXIS];
871       
872       if (i)
873         {
874           if (center <= last_center + center_distance_tolerance)
875             {
876               if (!specifications_[i].has_manual_dir_)
877                 {
878                   Tie_configuration_variation var;
879                   var.index_ = i;
880                   var.suggestion_ = get_configuration (specifications_[i].position_
881                                                        - ties[i].dir_,
882                                                        -ties[i].dir_);
883
884                   vars.push_back (var);
885                 }
886
887               if (!specifications_[i-1].has_manual_dir_)
888                 {
889                   Tie_configuration_variation var;
890                   var.index_ = i-1;
891                   var.suggestion_ = get_configuration (specifications_[i-1].position_
892                                                        - ties[i-1].dir_,
893                                                        - ties[i-1].dir_);
894
895                   vars.push_back (var);
896                 }
897
898               if (i == 1 && !specifications_[i-1].has_manual_position_
899                   && ties[i-1].dir_ == DOWN)
900                 {
901                   Tie_configuration_variation var;
902                   var.index_ = i-1;
903                   var.suggestion_ = get_configuration (specifications_[i-1].position_
904                                                        - 1, DOWN);
905                   vars.push_back (var);
906                 }
907               if (i == ties.size() && !specifications_[i].has_manual_position_
908                   && ties[i].dir_ == UP)
909                 {
910                   Tie_configuration_variation var;
911                   var.index_ = i;
912                   var.suggestion_ = get_configuration (specifications_[i].position_
913                                                        + 1, UP);
914                   vars.push_back (var);
915                 }
916             }
917           else if (dot_positions_.find (ties[i].position_) != dot_positions_.end ()
918                    && !specifications_[i].has_manual_position_)
919             {
920               Tie_configuration_variation var;
921               var.index_ = i;
922               var.suggestion_ = get_configuration (ties[i].position_  + ties[i].dir_,
923                                                    ties[i].dir_);
924               vars.push_back (var);
925             }
926           
927         }
928
929       last_center = center;
930     }
931
932
933   return vars;
934 }
935
936 void
937 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs)
938 {
939   vsize k = 0;
940   for (SCM s = manual_configs;
941        scm_is_pair (s) && k < specifications_.size (); s = scm_cdr (s))
942     {
943       SCM entry = scm_car (s);
944       if (scm_is_pair (entry))
945         {
946           Tie_specification &spec = specifications_[k];
947
948           if (scm_is_number (scm_car (entry)))
949             {
950               spec.has_manual_position_ = true;
951               spec.manual_position_ = scm_to_double (scm_car (entry));
952             }
953           if (scm_is_number (scm_cdr (entry)))
954             {
955               spec.has_manual_dir_ = true;
956               spec.manual_dir_ = Direction (scm_to_int (scm_cdr (entry)));
957             }
958         }         
959       k ++;
960     }
961 }
962