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