]> git.donarmstrong.com Git - lilypond.git/blob - lily/dot-column.cc
* lily/include/grob-info.hh: origin_contexts() now does not
[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 <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   { /*
236       Trigger note collision resolution first, since that may kill off
237       dots when merging.
238      */
239     Grob * c = 0;
240     for (int i = dots.size (); i-- ; )
241       {
242         Grob * n = dots[i]->get_parent (Y_AXIS);
243         if (c)
244           c = n->common_refpoint (c, X_AXIS);
245         else
246           c = n;
247       }
248     for (int i = dots.size (); i-- ; )
249       {
250         Grob * n = dots[i]->get_parent (Y_AXIS);
251         n->relative_coordinate (c ,  X_AXIS);
252       }
253   }
254   
255   dots.sort (compare_position);
256   for (int i = dots.size (); i--;)
257     if (!dots[i]->live ())
258       dots.del (i);
259   
260   Dot_configuration cfg;
261   for (int i =0;i < dots.size (); i++)
262     {
263       Dot_position dp;
264       dp.dot_ = dots[i];
265
266       Grob * note = dots[i]->get_parent (Y_AXIS);
267       if (note)
268         {
269           Grob *stem = unsmob_grob (note->get_grob_property ("stem"));
270           if (stem)
271             dp.extremal_head_ = Stem::first_head (stem) == note;
272         }
273       
274       int p = int (Staff_symbol_referencer::get_position (dp.dot_));
275       dp.pos_= p;
276
277       if (dp.extremal_head_)
278         dp.dir_ = to_dir (dp.dot_->get_grob_property  ("direction"));
279
280       remove_collision (cfg, p);
281       cfg[p] = dp;
282       if (Staff_symbol_referencer::on_staffline (dp.dot_, p))
283         remove_collision (cfg, p);
284     }
285
286   for (Dot_configuration::const_iterator i (cfg.begin ());
287        i != cfg.end (); i++)
288     {
289       Staff_symbol_referencer::set_position (i->second.dot_, i->first);  
290     }
291
292   return SCM_UNSPECIFIED;
293 }
294
295 void
296 Dot_column::add_head (Grob * me, Grob *rh)
297 {
298   Grob * d = unsmob_grob (rh->get_grob_property ("dot"));
299   if (d)
300     {
301       Side_position_interface::add_support (me,rh);
302
303       Pointer_group_interface::add_grob (me, ly_symbol2scm ("dots"), d);
304       d->add_offset_callback (Dot_column::force_shift_callback_proc , Y_AXIS);
305       Axis_group_interface::add_element (me, d);
306     }
307 }
308
309
310
311
312 ADD_INTERFACE (Dot_column, "dot-column-interface",
313                "Groups dot objects so they form a column, and position dots so they do not "
314                "clash with staff lines "
315                ,
316                "positioning-done direction stem");
317