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