]> git.donarmstrong.com Git - lilypond.git/blob - lily/tie-column.cc
(set_chord_outlines): bugfix: insert complete
[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 #include "tie-column.hh"
10
11 #include <math.h>
12
13 #include <map>
14 #include <set>
15
16
17 #include "stencil.hh"
18 #include "stem.hh"
19 #include "skyline.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "warn.hh"
22 #include "paper-column.hh"
23 #include "spanner.hh"
24 #include "pointer-group-interface.hh"
25 #include "tie.hh"
26 #include "directional-element-interface.hh"
27 #include "rhythmic-head.hh"
28
29 void
30 Tie_column::add_tie (Grob *me, Grob *tie)
31 {
32   if (tie->get_parent (Y_AXIS)
33       && Tie_column::has_interface (tie->get_parent (Y_AXIS)))
34     return;
35
36   if (!Pointer_group_interface::count (me, ly_symbol2scm ("ties")))
37     {
38       dynamic_cast<Spanner *> (me)->set_bound (LEFT, Tie::head (tie, LEFT));
39       dynamic_cast<Spanner *> (me)->set_bound (RIGHT, Tie::head (tie, RIGHT));
40     }
41
42   tie->set_parent (me, Y_AXIS);
43   Pointer_group_interface::add_grob (me, ly_symbol2scm ("ties"), tie);
44   tie->add_dependency (me);
45 }
46
47 void
48 Tie_column::set_directions (Grob *me)
49 {
50   if (!to_boolean (me->get_property ("positioning-done")))
51     {
52       me->set_property ("positioning-done", SCM_BOOL_T); 
53       new_directions (me);
54     }
55 }
56
57 int
58 Tie::compare (Grob *const &s1,
59               Grob *const &s2)
60 {
61   return sign (Tie::get_position (s1) - Tie::get_position (s2));
62 }
63
64 MAKE_SCHEME_CALLBACK (Tie_column, after_line_breaking, 1);
65 SCM
66 Tie_column::after_line_breaking (SCM smob)
67 {
68   set_directions (unsmob_grob (smob));
69   return SCM_UNSPECIFIED;
70 }
71
72 /*
73   Extend the spanner over its Tie constituents.
74 */
75 MAKE_SCHEME_CALLBACK (Tie_column, before_line_breaking, 1);
76 SCM
77 Tie_column::before_line_breaking (SCM smob)
78 {
79   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
80   for (SCM s = me->get_property ("ties"); scm_is_pair (s); s = scm_cdr (s))
81     {
82       Spanner *tie = dynamic_cast<Spanner *> (unsmob_grob (scm_car (s)));
83       Direction dir = LEFT;
84       do
85         {
86           if (dir * tie->get_bound (dir)->get_column ()->get_rank ()
87               > dir * me->get_bound (dir)->get_column ()->get_rank ())
88             me->set_bound (dir, Tie::head (tie, dir));
89         }
90       while (flip (&dir) != LEFT);
91     }
92   return SCM_UNSPECIFIED;
93 }
94
95
96
97 void
98 set_chord_outlines (Drul_array< Array<Skyline_entry> > *skyline_drul,
99                     Link_array<Grob> ties,
100                     Grob *common)
101 {
102   Direction d = LEFT;
103
104   Real staff_space = Staff_symbol_referencer::staff_space (ties[0]);
105   do
106     {
107       Array<Box> boxes;
108       Interval x_union;
109
110       Grob *stem = 0;
111       for (int i = 0; i < ties.size (); i++)
112         {
113           Spanner *tie = dynamic_cast<Spanner*> (ties[i]);
114
115           Grob *head = Tie::head (tie, d);
116           if (!head)
117             continue;
118
119           if (!stem)
120             stem = unsmob_grob (head->get_object ("stem"));
121           
122           Real p = Tie::get_position (tie);
123           Interval y ((p-1) * 0.5 * staff_space,
124                       (p+1) * 0.5 * staff_space);
125
126           Interval x = head->extent (common, X_AXIS);
127           boxes.push (Box (x, y));
128           x_union.unite (x);
129         }
130
131       (*skyline_drul)[d] = empty_skyline (-d);
132       
133       Spanner *tie = dynamic_cast<Spanner*> (ties[0]);
134       if (tie->get_bound (d)->break_status_dir ())
135         {
136           Real x = robust_relative_extent (tie->get_bound (d),
137                                            common,
138                                            X_AXIS)[-d];
139
140           (*skyline_drul)[d].elem_ref (0).height_ = x; 
141         }
142           
143       for (int i = 0; i < boxes.size (); i++)
144         insert_extent_into_skyline (&skyline_drul->elem_ref (d),
145                                     boxes[i], Y_AXIS, -d);
146       if (stem
147           && !Stem::is_invisible (stem))
148         {
149           Interval x;
150           x.add_point (stem->relative_coordinate (common, X_AXIS));
151           x.widen (staff_space / 20); // ugh.
152           Interval y;
153           y.add_point (Stem::stem_end_position (stem) * staff_space * .5);
154
155           Direction stemdir = Stem::get_direction (stem);
156           y.add_point (Stem::head_positions (stem)[-stemdir]
157                        * staff_space * .5);
158           
159           insert_extent_into_skyline (&skyline_drul->elem_ref (d),
160                                       Box (x,y), Y_AXIS, -d);
161
162
163
164           if (d == LEFT)
165             {
166               Box flag_box = Stem::get_translated_flag (stem).extent_box ();
167               flag_box.translate( Offset (x[RIGHT], X_AXIS));
168               insert_extent_into_skyline (&skyline_drul->elem_ref (d),
169                                           flag_box,
170                                           Y_AXIS, -d);
171             }
172         }
173
174       
175
176
177       Direction updowndir = DOWN;
178       do
179         {
180           Interval x ;
181           Interval y;
182           if (boxes.size())
183             {
184               Box b = boxes.boundary (updowndir, 0);
185               x = b[X_AXIS];
186               x[-d] =  b[X_AXIS].linear_combination (-d / 2);
187               y[-updowndir] = b[Y_AXIS][updowndir];
188               y[updowndir] = updowndir * infinity_f;
189             }
190
191           if (!x.is_empty ())
192             insert_extent_into_skyline (&skyline_drul->elem_ref (d),
193                                         Box (x,y),
194                                         Y_AXIS, -d);
195         }
196       while (flip (&updowndir) != DOWN);
197
198       for (int i = 0; i < ties.size (); i++)
199         {
200           Spanner *tie = dynamic_cast<Spanner*> (ties[i]);
201           Grob *head = Tie::head (tie, d);
202           if (!head)
203             continue;
204
205           Grob *dots = unsmob_grob (head->get_object ("dot"));
206           if (dots && d == LEFT)
207             {
208               Interval x = dots->extent (common, X_AXIS);
209               Real p = Staff_symbol_referencer::get_position (dots);
210               
211               Interval y (-1,1);
212               y *= (staff_space /4);
213               y.translate (p * staff_space * .5);
214
215               insert_extent_into_skyline (&skyline_drul->elem_ref (d),
216                                           Box (x,y), Y_AXIS, -d);
217             }
218         }
219       
220     }
221   while (flip (&d) != LEFT);
222 }
223
224
225 void
226 Tie_column::new_directions (Grob *me)
227 {
228   extract_grob_set (me, "ties", ro_ties);
229   Link_array<Grob> ties (ro_ties);
230   if (!ties.size ())
231     return;
232
233   if (ties.size() == 1)
234     {
235       Tie::set_default_control_points (ties[0]);
236       return ;
237     }
238   
239   ties.sort (&Tie::compare);
240
241   Array<Tie_configuration> tie_configs;
242   for (int i = 0; i < ties.size (); i++)
243     {
244       Tie_configuration conf;
245       conf.dir_ = get_grob_direction (ties[i]);
246       conf.position_ = Tie::get_position (ties[i]);
247       tie_configs.push (conf);
248     }
249
250   SCM manual_configs = me->get_property ("tie-configuration");
251   bool manual_override = false;
252   int k = 0;
253   for (SCM s = manual_configs;
254        scm_is_pair (s) && k < tie_configs.size(); s = scm_cdr (s))
255     {
256       SCM entry = scm_car (s);
257       if (!scm_is_pair (entry))
258         continue;
259
260       manual_override = true;
261       tie_configs[k].position_ = robust_scm2double (scm_car (entry), tie_configs[k].position_);
262       tie_configs[k].dir_ = Direction (robust_scm2int (scm_cdr (entry), tie_configs[k].dir_));
263       k ++;
264     }
265
266   if (!tie_configs[0].dir_)
267     tie_configs[0].dir_ = DOWN;
268   if (!tie_configs.top().dir_)
269     tie_configs.top().dir_ = UP;
270
271   /*
272     Seconds
273    */
274   for (int i = 1; i < tie_configs.size(); i++)
275     {
276       if (fabs (tie_configs[i-1].position_ - tie_configs[i].position_) <= 1)
277         {
278           if (!tie_configs[i-1].dir_)
279             tie_configs[i-1].dir_ = DOWN;
280           if (!tie_configs[i].dir_)
281             tie_configs[i].dir_ = UP;
282         }
283     }
284
285   for (int i = 1; i < tie_configs.size() - 1; i++)
286     {
287       if (tie_configs[i].dir_)
288         continue;
289
290       Direction position_dir = (Direction) sign (tie_configs[i].position_);
291       if (!position_dir)
292         position_dir = DOWN;
293       
294       tie_configs[i].dir_ = position_dir;
295     }
296
297   Grob *common = me;
298   for (int i = 0; i < ties.size (); i++)
299     {
300       common = dynamic_cast<Spanner*> (ties[i])->get_bound (LEFT)->common_refpoint (common, X_AXIS); 
301       common = dynamic_cast<Spanner*> (ties[i])->get_bound (RIGHT)->common_refpoint (common, X_AXIS); 
302     }
303
304   Drul_array< Array<Skyline_entry> > skylines;
305   set_chord_outlines (&skylines, ties, common);
306   
307   Tie_details details;
308   details.init (ties[0]);
309
310   /*
311     Let the ties flow out, according to our single-tie formatting.
312    */
313   if (!manual_override)
314     {
315       Tie::get_configuration (ties[0], common, &tie_configs.elem_ref (0),
316                               &skylines,
317                               details
318                               );
319       Tie::get_configuration (ties.top (), common,
320                               &tie_configs.elem_ref (tie_configs.size()-1),
321                               &skylines,
322                               details
323                               );
324     }
325
326   /*
327     Calculate final width and shape of the ties.
328    */
329   Real staff_space = Staff_symbol_referencer::staff_space (ties[0]);
330   Real gap = robust_scm2double (ties[0]->get_property ("x-gap"), 0.2);
331   for (int i = 0; i < ties.size(); i++)
332     {
333       if (!manual_override
334           && (i == 0 || i == ties.size () -1))
335         continue;
336       
337       Tie_configuration conf = tie_configs[i];
338       conf = tie_configs[i];
339
340       Real line_dy = 0.0;
341       bool on_line = Staff_symbol_referencer::on_staffline (ties[0],
342                                                             int (rint (conf.position_)));
343       if (on_line)
344         line_dy = - sign (conf.height (details) - 0.6 * staff_space)
345           * 0.2 * staff_space * conf.dir_;
346
347       Real y = conf.position_ * staff_space * 0.5
348         + line_dy;
349       conf.attachment_x_
350         = get_skyline_attachment (skylines, y);
351       conf.attachment_x_.intersect (get_skyline_attachment (skylines,
352                                                             y + conf.dir_ * staff_space * .5));
353
354
355       conf.delta_y_ += line_dy;
356       conf.attachment_x_.widen (-gap);
357       if (!on_line
358           && Staff_symbol_referencer::staff_radius (ties[0]) * staff_space > y)
359         conf.center_tie_vertically (details);
360               
361       tie_configs[i] = conf;
362     }
363
364   /*
365     Try to shift small ties into available spaces.
366    */
367   if (!manual_override)
368     {
369       set<int> positions_taken; 
370       for (int i = 0; i < tie_configs.size (); i++)
371         positions_taken.insert (int (rint (tie_configs[i].position_)));
372
373       for (int i = 0; i < tie_configs.size (); i++)
374         {
375           Tie_configuration * conf = &tie_configs.elem_ref (i);
376
377           /*
378             on staff line and small enough, translate a little further 
379            */
380           Real h = conf->height (details);
381           bool next_free = positions_taken.find (int (rint (conf->position_ + conf->dir_)))
382             == positions_taken.end ();
383           bool on_line = Staff_symbol_referencer::on_staffline (ties[0],
384                                                                 int (rint (conf->position_ + conf->delta_y_)));
385           if (next_free)
386             if (on_line && h < 0.4 * staff_space)
387               {
388                 positions_taken.insert (int (rint (conf->position_ + conf->dir_)));
389                 conf->delta_y_ += 0.2 * staff_space * conf->dir_;
390               }
391             else if (!on_line && h > 0.6 * staff_space)
392               {
393                 positions_taken.insert (int (rint (conf->position_ + conf->dir_)));
394                 conf->delta_y_ += 0.5 * staff_space * conf->dir_;
395               }
396         }
397     }
398   
399   for (int i = 0; i < ties.size(); i++)
400     {
401       Tie::set_control_points (ties[i], common, tie_configs[i],
402                                details
403                                );
404       set_grob_direction (ties[i], tie_configs[i].dir_);
405     }
406 }
407
408
409 ADD_INTERFACE (Tie_column, "tie-column-interface",
410                "Object that sets directions of multiple ties in a tied chord",
411                "positioning-done "
412                "tie-configuration "
413                );