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