]> git.donarmstrong.com Git - lilypond.git/blob - lily/dot-column.cc
*** empty log message ***
[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--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include <stdio.h>
10 #include <math.h>
11 #include <map>
12
13 #include "dots.hh"
14 #include "dot-column.hh"
15 #include "rhythmic-head.hh"
16 #include "group-interface.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   Axis a = (Axis) scm_to_int (axis);
33   assert (a == 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   Axis a = (Axis) scm_to_int (axis);
51   assert (a == 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       /*
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
71
72 struct Dot_position
73 {
74   int pos_;
75   Direction dir_;
76   Grob *dot_;
77   bool extremal_head_;
78
79   
80   Dot_position ()
81   {
82     dot_ = 0;
83     pos_ =0;
84     dir_ = CENTER;
85   }
86 };
87
88
89 typedef std::map<int, Dot_position> Dot_configuration;
90
91 /*
92   Value CFG according.
93
94 */
95 int
96 dot_config_badness (Dot_configuration const  &cfg)
97 {
98   int t = 0;
99   for (Dot_configuration::const_iterator i (cfg.begin ());
100        i != cfg.end (); i++)
101     {
102       int p = i->first;
103       int demerit = sqr (p - i->second.pos_) * 2;
104
105       int dot_move_dir = sign (p - i->second.pos_);
106       if (i->second.extremal_head_)
107         {
108           if (i->second.dir_
109               &&  dot_move_dir != i->second.dir_)
110             demerit += 3;
111           else if (dot_move_dir != UP)
112             demerit += 2;
113         }
114       else if (dot_move_dir != UP)
115         demerit += 1;
116       
117       t += demerit;
118     }
119
120   return t;  
121 }
122
123 void
124 print_dot_configuration (Dot_configuration const &cfg)
125 {
126   printf ("dotconf { ");
127   for (Dot_configuration::const_iterator i (cfg.begin ());
128        i != cfg.end (); i++)
129     printf ("%d, " , i->first);
130   printf ("} \n");
131 }
132
133 /*
134   Shift K and following (preceding) entries up (down) as necessary to
135   prevent staffline collisions if D is up (down).
136
137   If K is in CFG, then do nothing.  
138 */
139
140 Dot_configuration
141 shift_one (Dot_configuration const &cfg,
142            int k, Direction d)
143 {
144   Dot_configuration new_cfg;
145   int offset = 0;
146   
147   if (d > 0)
148     {
149       for (Dot_configuration::const_iterator i (cfg.begin ());
150            i != cfg.end (); i++)
151         {
152           int p = i->first;
153           if (p == k)
154             {
155               if (Staff_symbol_referencer::on_staffline (i->second.dot_, p))
156                 p += d ;
157               else
158                 p += 2* d;
159
160               offset = 2*d;
161
162               new_cfg[p] = i->second; 
163             }
164           else
165             {
166               if (new_cfg.find (p) == new_cfg.end ())
167                 {
168                   offset =0;
169                 }
170               new_cfg[p + offset] = i->second;
171             }
172         }
173     }
174   else
175     {
176       Dot_configuration::const_iterator i (cfg.end ());
177       do
178         {
179           i --;
180
181           int p = i->first;
182           if (p == k)
183             {
184               if (Staff_symbol_referencer::on_staffline (i->second.dot_, p))
185                 p += d ;
186               else
187                 p += 2* d;
188
189               offset = 2*d;
190
191               new_cfg[p] = i->second; 
192             }
193           else
194             {
195               if (new_cfg.find (p) == new_cfg.end ())
196                 {
197                   offset =0;
198                 }
199
200               new_cfg[p + offset] = i->second;
201             }
202         }
203       while (i != cfg.begin ());
204     }
205   
206   return new_cfg;
207 }
208
209 /*
210   Remove the collision in CFG either by shifting up or down, whichever
211   is best.
212  */
213 void
214 remove_collision (Dot_configuration &cfg, int p)
215 {
216   bool collide = cfg.find (p) != cfg.end ();
217
218   if (collide)
219     {
220       Dot_configuration cfg_up = shift_one (cfg, p, UP);
221       Dot_configuration cfg_down = shift_one (cfg, p, DOWN);
222       
223       int b_up =  dot_config_badness (cfg_up);
224       int b_down = dot_config_badness (cfg_down);
225
226       cfg =  (b_up < b_down) ? cfg_up : cfg_down;
227     }
228 }
229
230 SCM
231 Dot_column::do_shifts (Grob*me)
232 {
233   Link_array<Grob> dots =
234     Pointer_group_interface__extract_grobs (me, (Grob*)0, "dots");
235
236   { /*
237       Trigger note collision resolution first, since that may kill off
238       dots when merging.
239      */
240     Grob * c = 0;
241     for (int i = dots.size (); i-- ; )
242       {
243         Grob * n = dots[i]->get_parent (Y_AXIS);
244         if (c)
245           c = n->common_refpoint (c, X_AXIS);
246         else
247           c = n;
248       }
249     for (int i = dots.size (); i-- ; )
250       {
251         Grob * n = dots[i]->get_parent (Y_AXIS);
252         n->relative_coordinate (c ,  X_AXIS);
253       }
254   }
255   
256   dots.sort (compare_position);
257   for (int i = dots.size (); i--;)
258     if (!dots[i]->is_live ())
259       dots.del (i);
260   
261   Dot_configuration cfg;
262   for (int i =0;i < dots.size (); i++)
263     {
264       Dot_position dp;
265       dp.dot_ = dots[i];
266
267       Grob * note = dots[i]->get_parent (Y_AXIS);
268       if (note)
269         {
270           Grob *stem = unsmob_grob (note->get_property ("stem"));
271           if (stem)
272             dp.extremal_head_ = Stem::first_head (stem) == note;
273         }
274       
275       int p = Staff_symbol_referencer::get_rounded_position (dp.dot_);
276       dp.pos_= p;
277
278       if (dp.extremal_head_)
279         dp.dir_ = to_dir (dp.dot_->get_property  ("direction"));
280
281       remove_collision (cfg, p);
282       cfg[p] = dp;
283       if (Staff_symbol_referencer::on_staffline (dp.dot_, p))
284         remove_collision (cfg, p);
285     }
286
287   for (Dot_configuration::const_iterator i (cfg.begin ());
288        i != cfg.end (); i++)
289     {
290       Staff_symbol_referencer::set_position (i->second.dot_, i->first);  
291     }
292
293   return SCM_UNSPECIFIED;
294 }
295
296 void
297 Dot_column::add_head (Grob * me, Grob *rh)
298 {
299   Grob * d = unsmob_grob (rh->get_property ("dot"));
300   if (d)
301     {
302       Side_position_interface::add_support (me,rh);
303
304       Pointer_group_interface::add_grob (me, ly_symbol2scm ("dots"), d);
305       d->add_offset_callback (Dot_column::force_shift_callback_proc , Y_AXIS);
306       Axis_group_interface::add_element (me, d);
307     }
308 }
309
310
311
312
313 ADD_INTERFACE (Dot_column, "dot-column-interface",
314                "Groups dot objects so they form a column, and position dots so they do not "
315                "clash with staff lines "
316                ,
317                "positioning-done direction stem");
318