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