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