]> git.donarmstrong.com Git - lilypond.git/blob - lily/dot-column.cc
* lily/include/paper-score.hh (class Paper_score): remove unused prototypes.
[lilypond.git] / lily / dot-column.cc
1 /*
2   dot-column.cc -- implement Dot_column
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "dot-column.hh"
10
11 #include <cstdio>
12 #include <math.h>
13 #include <map>
14
15 #include "dots.hh"
16 #include "rhythmic-head.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "directional-element-interface.hh"
19 #include "side-position-interface.hh"
20 #include "axis-group-interface.hh"
21 #include "stem.hh"
22
23 /*
24   TODO: let Dot_column communicate with stem via Note_column.
25 */
26
27 MAKE_SCHEME_CALLBACK (Dot_column, force_shift_callback, 2);
28 SCM
29 Dot_column::force_shift_callback (SCM element_smob, SCM axis)
30 {
31   Grob *me = unsmob_grob (element_smob);
32   (void) axis;
33   assert (scm_to_int (axis) == Y_AXIS);
34   me = me->get_parent (X_AXIS);
35
36   if (!to_boolean (me->get_property ("positioning-done")))
37     {
38       me->set_property ("positioning-done", SCM_BOOL_T);
39
40       do_shifts (me);
41     }
42   return scm_make_real (0.0);
43 }
44
45 MAKE_SCHEME_CALLBACK (Dot_column, side_position, 2);
46 SCM
47 Dot_column::side_position (SCM element_smob, SCM axis)
48 {
49   Grob *me = unsmob_grob (element_smob);
50   (void) axis;
51   assert (scm_to_int (axis) == X_AXIS);
52
53   Grob *stem = unsmob_grob (me->get_property ("stem"));
54   if (stem
55       && !Stem::get_beam (stem)
56       && Stem::duration_log (stem) > 2
57       && !Stem::is_invisible (stem))
58     {
59       /*
60         trigger stem end & direction calculation.
61
62         This will add the stem to the support if a flag collision happens.
63       */
64       Stem::stem_end_position (stem);
65     }
66   return Side_position_interface::aligned_side (element_smob, axis);
67 }
68
69 struct Dot_position
70 {
71   int pos_;
72   Direction dir_;
73   Grob *dot_;
74   bool extremal_head_;
75
76   Dot_position ()
77   {
78     dot_ = 0;
79     pos_ = 0;
80     dir_ = CENTER;
81   }
82 };
83
84 typedef std::map<int, Dot_position> Dot_configuration;
85
86 /*
87   Value CFG according.
88 */
89 int
90 dot_config_badness (Dot_configuration const &cfg)
91 {
92   int t = 0;
93   for (Dot_configuration::const_iterator i (cfg.begin ());
94        i != cfg.end (); i++)
95     {
96       int p = i->first;
97       int demerit = sqr (p - i->second.pos_) * 2;
98
99       int dot_move_dir = sign (p - i->second.pos_);
100       if (i->second.extremal_head_)
101         {
102           if (i->second.dir_
103               && dot_move_dir != i->second.dir_)
104             demerit += 3;
105           else if (dot_move_dir != UP)
106             demerit += 2;
107         }
108       else if (dot_move_dir != UP)
109         demerit += 1;
110
111       t += demerit;
112     }
113
114   return t;
115 }
116
117 void
118 print_dot_configuration (Dot_configuration const &cfg)
119 {
120   printf ("dotconf { ");
121   for (Dot_configuration::const_iterator i (cfg.begin ());
122        i != cfg.end (); i++)
123     printf ("%d, ", i->first);
124   printf ("} \n");
125 }
126
127 /*
128   Shift K and following (preceding) entries up (down) as necessary to
129   prevent staffline collisions if D is up (down).
130
131   If K is in CFG, then do nothing.
132 */
133
134 Dot_configuration
135 shift_one (Dot_configuration const &cfg,
136            int k, Direction d)
137 {
138   Dot_configuration new_cfg;
139   int offset = 0;
140
141   if (d > 0)
142     {
143       for (Dot_configuration::const_iterator i (cfg.begin ());
144            i != cfg.end (); i++)
145         {
146           int p = i->first;
147           if (p == k)
148             {
149               if (Staff_symbol_referencer::on_staffline (i->second.dot_, p))
150                 p += d;
151               else
152                 p += 2* d;
153
154               offset = 2*d;
155
156               new_cfg[p] = i->second;
157             }
158           else
159             {
160               if (new_cfg.find (p) == new_cfg.end ())
161                 {
162                   offset = 0;
163                 }
164               new_cfg[p + offset] = i->second;
165             }
166         }
167     }
168   else
169     {
170       Dot_configuration::const_iterator i (cfg.end ());
171       do
172         {
173           i--;
174
175           int p = i->first;
176           if (p == k)
177             {
178               if (Staff_symbol_referencer::on_staffline (i->second.dot_, p))
179                 p += d;
180               else
181                 p += 2* d;
182
183               offset = 2*d;
184
185               new_cfg[p] = i->second;
186             }
187           else
188             {
189               if (new_cfg.find (p) == new_cfg.end ())
190                 {
191                   offset = 0;
192                 }
193
194               new_cfg[p + offset] = i->second;
195             }
196         }
197       while (i != cfg.begin ());
198     }
199
200   return new_cfg;
201 }
202
203 /*
204   Remove the collision in CFG either by shifting up or down, whichever
205   is best.
206 */
207 void
208 remove_collision (Dot_configuration &cfg, int p)
209 {
210   bool collide = cfg.find (p) != cfg.end ();
211
212   if (collide)
213     {
214       Dot_configuration cfg_up = shift_one (cfg, p, UP);
215       Dot_configuration cfg_down = shift_one (cfg, p, DOWN);
216
217       int b_up = dot_config_badness (cfg_up);
218       int b_down = dot_config_badness (cfg_down);
219
220       cfg = (b_up < b_down) ? cfg_up : cfg_down;
221     }
222 }
223
224 SCM
225 Dot_column::do_shifts (Grob *me)
226 {
227   Link_array<Grob> dots
228     = extract_grob_array (me, ly_symbol2scm ("dots"));
229
230   { /*
231       Trigger note collision resolution first, since that may kill off
232       dots when merging.
233     */
234     Grob *c = 0;
235     for (int i = dots.size (); i--;)
236       {
237         Grob *n = dots[i]->get_parent (Y_AXIS);
238         if (c)
239           c = n->common_refpoint (c, X_AXIS);
240         else
241           c = n;
242       }
243     for (int i = dots.size (); i--;)
244       {
245         Grob *n = dots[i]->get_parent (Y_AXIS);
246         n->relative_coordinate (c, X_AXIS);
247       }
248   }
249
250   dots.sort (compare_position);
251   for (int i = dots.size (); i--;)
252     if (!dots[i]->is_live ())
253       dots.del (i);
254
255   Dot_configuration cfg;
256   for (int i = 0;i < dots.size (); i++)
257     {
258       Dot_position dp;
259       dp.dot_ = dots[i];
260
261       Grob *note = dots[i]->get_parent (Y_AXIS);
262       if (note)
263         {
264           Grob *stem = unsmob_grob (note->get_property ("stem"));
265           if (stem)
266             dp.extremal_head_ = Stem::first_head (stem) == note;
267         }
268
269       int p = Staff_symbol_referencer::get_rounded_position (dp.dot_);
270       dp.pos_ = p;
271
272       if (dp.extremal_head_)
273         dp.dir_ = to_dir (dp.dot_->get_property ("direction"));
274
275       remove_collision (cfg, p);
276       cfg[p] = dp;
277       if (Staff_symbol_referencer::on_staffline (dp.dot_, p))
278         remove_collision (cfg, p);
279     }
280
281   for (Dot_configuration::const_iterator i (cfg.begin ());
282        i != cfg.end (); i++)
283     {
284       Staff_symbol_referencer::set_position (i->second.dot_, i->first);
285     }
286
287   return SCM_UNSPECIFIED;
288 }
289
290 void
291 Dot_column::add_head (Grob *me, Grob *rh)
292 {
293   Grob *d = unsmob_grob (rh->get_property ("dot"));
294   if (d)
295     {
296       Side_position_interface::add_support (me, rh);
297
298       Pointer_group_interface::add_grob (me, ly_symbol2scm ("dots"), d);
299       d->add_offset_callback (Dot_column::force_shift_callback_proc, Y_AXIS);
300       Axis_group_interface::add_element (me, d);
301     }
302 }
303
304 ADD_INTERFACE (Dot_column, "dot-column-interface",
305                "Groups dot objects so they form a column, and position dots so they do not "
306                "clash with staff lines ",
307                "positioning-done direction stem");
308