]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie-column.cc
* lily/tie-column.cc (set_directions): set directions only once.
[lilypond.git] / lily / tie-column.cc
1 /*
2   tie-column.cc -- implement Tie_column
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2000--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9
10 #include <math.h>
11 #include <map>
12
13 #include "staff-symbol-referencer.hh"
14 #include "warn.hh"
15 #include "tie-column.hh"
16 #include "paper-column.hh"
17 #include "spanner.hh"
18 #include "pointer-group-interface.hh"
19 #include "tie.hh"
20 #include "directional-element-interface.hh"
21 #include "rhythmic-head.hh"
22
23 void
24 Tie_column::add_tie (Grob *me, Grob *tie)
25 {
26   if (tie->get_parent (Y_AXIS)
27       && Tie_column::has_interface (tie->get_parent (Y_AXIS)))
28     return;
29
30   if (!Pointer_group_interface::count (me, ly_symbol2scm ("ties")))
31     {
32       dynamic_cast<Spanner *> (me)->set_bound (LEFT, Tie::head (tie, LEFT));
33       dynamic_cast<Spanner *> (me)->set_bound (RIGHT, Tie::head (tie, RIGHT));
34     }
35
36   tie->set_parent (me, Y_AXIS);
37   Pointer_group_interface::add_grob (me, ly_symbol2scm ("ties"), tie);
38   tie->add_dependency (me);
39 }
40
41 void
42 Tie_column::set_directions (Grob *me)
43 {
44   if (!to_boolean (me->get_property ("positioning-done")))
45     {
46       me->set_property ("positioning-done", SCM_BOOL_T); 
47       new_directions (me);
48     }
49 }
50
51 int
52 Tie::compare (Grob *const &s1,
53               Grob *const &s2)
54 {
55   return sign (Tie::get_position (s1) - Tie::get_position (s2));
56 }
57
58 /*
59   Werner:
60
61   . The algorithm to choose the direction of the ties doesn't work
62   properly.  I suggest the following for applying ties sequentially
63   from top to bottom:
64
65   + The topmost tie is always `up'.
66
67   + If there is a vertical gap to the last note above larger than
68   or equal to a fifth (or sixth?), the tie is `up', otherwise it
69   is `down'.
70
71   + The bottommost tie is always `down'.
72 */
73 void
74 Tie_column::werner_directions (Grob *me)
75 {
76   extract_grob_set (me, "ties", ro_ties);
77   Link_array<Grob> ties (ro_ties);
78   if (!ties.size ())
79     return;
80
81   ties.sort (&Tie::compare);
82
83   Direction d = get_grob_direction (me);
84   if (d)
85     {
86       for (int i = ties.size (); i--;)
87         {
88           Grob *t = ties[i];
89           if (!get_grob_direction (t))
90             set_grob_direction (t, d);
91         }
92       return;
93     }
94
95   if (ties.size () == 1)
96     {
97       Grob *t = ties[0];
98       if (t->is_live ()
99           && !get_grob_direction (t))
100         set_grob_direction (t, Tie::get_default_dir (t));
101       return;
102     }
103
104   Real last_down_pos = 10000;
105   if (!get_grob_direction (ties[0]))
106     set_grob_direction (ties[0], DOWN);
107
108   /*
109     Go downward.
110   */
111   Grob *last_tie = 0;
112   for (int i = ties.size (); i--;)
113     {
114       Grob *t = ties[i];
115
116       Direction d = get_grob_direction (t);
117       Real p = Tie::get_position (t);
118       if (!d)
119         {
120           if (last_tie
121               && Tie::get_column_rank (t, LEFT)
122               < Tie::get_column_rank (last_tie, LEFT))
123             d = DOWN;
124           else if (last_down_pos - p > 5)
125             d = UP;
126           else
127             d = DOWN;
128
129           set_grob_direction (t, d);
130         }
131
132       if (d == DOWN)
133         last_down_pos = p;
134
135       last_tie = t;
136     }
137
138   return;
139 }
140
141 MAKE_SCHEME_CALLBACK (Tie_column, after_line_breaking, 1);
142 SCM
143 Tie_column::after_line_breaking (SCM smob)
144 {
145   set_directions (unsmob_grob (smob));
146   return SCM_UNSPECIFIED;
147 }
148
149 /*
150   Extend the spanner over its Tie constituents.
151 */
152 MAKE_SCHEME_CALLBACK (Tie_column, before_line_breaking, 1);
153 SCM
154 Tie_column::before_line_breaking (SCM smob)
155 {
156   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
157   for (SCM s = me->get_property ("ties"); scm_is_pair (s); s = scm_cdr (s))
158     {
159       Spanner *tie = dynamic_cast<Spanner *> (unsmob_grob (scm_car (s)));
160       Direction dir = LEFT;
161       do
162         {
163           if (dir * tie->get_bound (dir)->get_column ()->get_rank ()
164               > dir * me->get_bound (dir)->get_column ()->get_rank ())
165             me->set_bound (dir, Tie::head (tie, dir));
166         }
167       while (flip (&dir) != LEFT);
168     }
169   return SCM_UNSPECIFIED;
170 }
171
172 ADD_INTERFACE (Tie_column, "tie-column-interface",
173                "Object that sets directions of multiple ties in a tied chord",
174                "direction "
175                "positioning-done "
176                );
177
178
179
180
181 bool
182 config_allowed (map<Tie_configuration, bool> const &allowed,
183                 Tie_configuration conf)
184 {
185   return allowed.find (conf) == allowed.end ();
186 }
187
188 void
189 add_configuration (map<Tie_configuration, bool> *allowed,
190                    Grob *tie_column,
191                    Tie_configuration new_conf)
192 {
193   bool on_line = Staff_symbol_referencer::on_staffline (tie_column, new_conf.position_);
194   
195   if (allowed->find (new_conf) != allowed->end ()
196       && !(*allowed)[new_conf])
197     {
198       programming_error ("Tie configuration not allowed");
199     }
200         
201
202   if (on_line)
203     {
204       Tie_configuration forbidden;
205
206       forbidden.dir_ = -new_conf.dir_ ;
207       forbidden.position_ = new_conf.position_;
208       (*allowed)[forbidden] = false;
209
210       forbidden.position_ += new_conf.dir_;
211       (*allowed)[forbidden] = false;
212       forbidden.position_ += new_conf.dir_;
213       (*allowed)[forbidden] = false;
214
215       forbidden.dir_ = new_conf.dir_;
216       forbidden.position_ = new_conf.position_ + new_conf.dir_;
217       (*allowed)[forbidden] = false;
218     }
219   else
220     {
221       Tie_configuration forbidden;
222       forbidden.dir_ = - new_conf.dir_;
223       forbidden.position_ = new_conf.position_;
224
225       
226       (*allowed)[forbidden] = false;
227       forbidden.position_ -= new_conf.dir_;
228       forbidden.dir_ = new_conf.dir_;
229       (*allowed)[forbidden] = false;
230
231       forbidden.position_ += 2* new_conf.dir_; 
232       (*allowed)[forbidden] = false;
233     }
234 }
235
236
237 void
238 Tie_column::new_directions (Grob *me)
239 {
240   extract_grob_set (me, "ties", ro_ties);
241   Link_array<Grob> ties (ro_ties);
242   if (!ties.size ())
243     return;
244
245   if (ties.size() == 1)
246     {
247       Tie::set_default_control_points (ties[0]);
248       return ;
249     }
250   
251   ties.sort (&Tie::compare);
252   Array<Tie_configuration> tie_configs;
253   for (int i = 0; i < ties.size (); i++)
254     {
255       Tie_configuration conf;
256       conf.dir_ = get_grob_direction (ties[i]);
257       conf.position_ = (int) rint (Tie::get_position (ties[i]));
258       tie_configs.push (conf);
259     }
260
261     
262   if (!tie_configs[0].dir_)
263     tie_configs[0].dir_ = DOWN;
264   if (!tie_configs.top().dir_)
265     tie_configs.top().dir_ = UP;
266
267
268   /*
269     Seconds
270    */
271   for (int i = 1; i < tie_configs.size(); i++)
272     {
273       if (fabs (tie_configs[i-1].position_ - tie_configs[i].position_) <= 1)
274         {
275           if (!tie_configs[i-1].dir_)
276             tie_configs[i-1].dir_ = DOWN;
277           if (!tie_configs[i].dir_)
278             tie_configs[i].dir_ = UP;
279         }
280     }
281
282   for (int i = 1; i < tie_configs.size() - 1; i++)
283     {
284       if (tie_configs[i].dir_)
285         continue;
286
287       tie_configs[i].dir_ = (Direction) sign (tie_configs[i].position_);
288     }
289
290   Grob *common[NO_AXES] = {
291     me, me
292   };
293   for (int i = 0; i < ties.size (); i++)
294     for (int a = X_AXIS; a < NO_AXES; a++)
295       {
296         Axis ax ((Axis) a);
297         
298         common[ax] = dynamic_cast<Spanner*> (ties[i])->get_bound (LEFT)->common_refpoint (common[a], ax); 
299         common[ax] = dynamic_cast<Spanner*> (ties[i])->get_bound (RIGHT)->common_refpoint (common[a], ax); 
300       }
301   
302   map<Tie_configuration, bool> allowed;
303
304   Tie::get_configuration (ties[0], common, &tie_configs.elem_ref (0));
305   Tie::get_configuration (ties.top (), common,
306                           &tie_configs.elem_ref (tie_configs.size()-1));
307
308   add_configuration (&allowed, me, tie_configs[0]);
309   add_configuration (&allowed, me, tie_configs.top());
310
311   for (int i = 1; i < ties.size(); i++)
312     {
313       Tie_configuration conf = tie_configs[i];
314       Tie::get_configuration (ties[i], common, &conf);
315       if (!config_allowed (allowed, conf))
316         {
317           conf = tie_configs[i];
318
319           Direction d = LEFT;
320           do
321             {
322               conf.attachment_x_[d] = d * 1e6; //  infty
323               for (int j = i - 1; j < i + 2; j++)
324                 {
325                   if (j >= 0 && j < ties.size())
326                     {
327                       Spanner *t = dynamic_cast<Spanner*> (ties[j]);
328                       Interval ext
329                         = robust_relative_extent (t->get_bound (d),
330                                                   common[X_AXIS], X_AXIS);
331                       conf.attachment_x_[d]
332                         = d * min (d * conf.attachment_x_[d], d * ext[-d]);
333                     } 
334                 }
335             }
336           while (flip (&d) != LEFT);
337           tie_configs[i] = conf;
338         }
339       else
340         tie_configs[i] = conf;
341
342       add_configuration (&allowed, me, conf);
343     }
344
345   for (int i = 0; i < ties.size(); i++)
346     {
347       Tie::set_control_points (ties[i], common, tie_configs[i]);
348       set_grob_direction (ties[i], tie_configs[i].dir_);
349     }
350 }
351
352