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