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