]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-spacing.cc
release: 1.5.38
[lilypond.git] / lily / note-spacing.cc
1 /*   
2   note-spacing.cc -- implement Note_spacing
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2001--2002  Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9
10 #include "paper-column.hh"
11 #include "item.hh"
12 #include "moment.hh"
13 #include "note-spacing.hh"
14 #include "grob.hh"
15 #include "note-column.hh"
16 #include "warn.hh"
17 #include "stem.hh"
18 #include "separation-item.hh"
19 #include "staff-spacing.hh"
20
21 bool
22 Note_spacing::has_interface (Grob* g)
23 {
24   return g && g->has_interface (ly_symbol2scm ("note-spacing-interface"));
25 }
26
27 Real
28 Note_spacing::get_spacing (Grob *me)
29 {
30   Drul_array<SCM> props(me->get_grob_property ("left-items"),
31                         me->get_grob_property ("right-items"));
32   Direction d = LEFT;
33   Drul_array<Interval> extents;
34   do
35     {
36       for (SCM  s = props[d]; gh_pair_p (s); s = gh_cdr (s))
37         {
38           Item * it= dynamic_cast<Item*> (unsmob_grob (gh_car(s)));
39           extents[d].unite (it->extent (it->column_l (), X_AXIS));
40
41           if (d == RIGHT)
42             {
43               Grob * accs = Note_column::accidentals (it);
44               if (!accs)
45                 accs = Note_column::accidentals (it->get_parent (X_AXIS));
46               
47               if (accs)
48                 extents[d].unite (accs->extent (it->column_l (), X_AXIS));
49             }
50         }
51
52       if (extents[d].empty_b ())
53         extents[d] = Interval (0,0);
54     }
55   while (flip (&d) != LEFT);
56
57   /*
58     
59     What's sticking out at the left of the right side has less
60     influence.
61
62   */
63   Real dx= extents[LEFT][RIGHT] - 0.5 * extents[RIGHT][LEFT];
64   return dx;
65 }
66
67 Item *
68 Note_spacing::left_column (Grob *me)
69 {
70   if (me->immutable_property_alist_ == SCM_EOL)
71     return 0;
72   
73   return dynamic_cast<Item*> (me)->column_l ();
74 }
75
76 /*
77   Compute the column of the right-items.  This is a big function,
78 since RIGHT-ITEMS may span more columns (eg. if a clef if inserted,
79 this will add a new columns to RIGHT-ITEMS. Here we look at the
80 columns, and return the left-most. If there are multiple columns, we
81 prune RIGHT-ITEMS.
82    
83  */
84 Item *
85 Note_spacing::right_column (Grob*me)
86 {
87   /*
88     ugh. should have generic is_live() method?
89    */
90   if (me->immutable_property_alist_ == SCM_EOL)
91     return 0;
92   
93   SCM right = me->get_grob_property ("right-items");
94   Item *mincol = 0;
95   int min_rank = INT_MAX;
96   bool prune = false;
97   for (SCM s = right ; gh_pair_p (s) ; s = gh_cdr (s))
98     {
99       Item * ri = unsmob_item (gh_car (s));
100
101       Item * col = ri->column_l ();
102       int rank = Paper_column::rank_i (col);
103
104       if (rank < min_rank)
105         {
106           min_rank = rank;
107           if (mincol)
108             prune = true;
109
110           mincol = col;
111         }
112     }
113   
114   if (prune)
115     {
116       // I'm a lazy bum. We could do this in-place.
117       SCM newright  = SCM_EOL;
118       for (SCM s = right ; gh_pair_p (s) ; s =gh_cdr (s))
119         {
120           if (unsmob_item (gh_car (s))->column_l () == mincol)
121             newright = gh_cons (gh_car (s), newright);
122         }
123
124       me->set_grob_property ("right-items", newright);
125     }
126   
127   if (!mincol)
128     {
129       /*
130       int r = Paper_column::rank_i (dynamic_cast<Item*>(me)->column_l ());
131       programming_error (_f("Spacing wish column %d has no right item.", r));
132       */
133
134       return 0;
135     }
136
137   return mincol;
138 }
139
140 /**
141    Correct for optical illusions. See [Wanske] p. 138. The combination
142    up-stem + down-stem should get extra space, the combination
143    down-stem + up-stem less.
144
145    TODO: have to check wether the stems are in the same staff.
146
147    TODO: also correct for bar lines in RIGHT-ITEMS.  Should check if
148    the barline is the leftmost object of the break alignment.
149
150 */
151 Real
152 Note_spacing::stem_dir_correction (Grob*me) 
153 {
154   Drul_array<Direction> stem_dirs(CENTER,CENTER);
155   Drul_array<Interval> stem_posns;
156   Drul_array<Interval> head_posns;  
157   Drul_array<SCM> props(me->get_grob_property ("left-items"),
158                         me->get_grob_property ("right-items"));
159
160   Real correction = 0.0;
161   
162   stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
163   Interval intersect;
164   Interval bar_xextent;
165   Interval bar_yextent;  
166   
167   bool correct = true;
168   Direction d = LEFT;
169   bool acc_right = false;
170   
171   do
172     {
173       for (SCM  s = props[d]; gh_pair_p (s); s = gh_cdr (s))
174         {
175           Item * it= dynamic_cast<Item*> (unsmob_grob (gh_car(s)));
176
177           if (d == RIGHT)
178             acc_right = acc_right || Note_column::accidentals (it);
179           
180           Grob *stem = Note_column::stem_l (it);
181
182           if (!stem)
183             {
184               if (d == RIGHT && Separation_item::has_interface (it))
185                 {
186                   Grob *last = Staff_spacing::extremal_break_aligned_grob (it, LEFT, &bar_xextent);
187
188                   if (last)
189                     bar_yextent = Staff_spacing::bar_y_positions (last);
190
191                   break;
192                 }
193
194               goto exit_func; 
195             }
196           if(Stem::invisible_b (stem))
197             {
198               correct = false;
199               goto exit_func ;
200             }
201
202           Direction sd = Stem::get_direction (stem);
203           if (stem_dirs[d] && stem_dirs[d] != sd)
204             {
205               correct = false;
206               goto exit_func;
207             }
208           stem_dirs[d] = sd;
209
210           Interval hp  = Stem::head_positions (stem);
211           Real chord_start = hp[sd];      
212           Real stem_end = Stem::stem_end_position (stem);
213           
214           stem_posns[d] = Interval(chord_start<?stem_end, chord_start>? stem_end);
215           head_posns[d].unite (hp);
216         }
217     }
218   while (flip (&d) != LEFT);
219   
220
221   /*
222     don't correct if accidentals are sticking out of the right side.
223
224   */
225   if (acc_right)
226     return 0.0;
227
228   if (!bar_yextent.empty_b())
229     {
230       stem_dirs[RIGHT] = - stem_dirs[LEFT];
231       stem_posns[RIGHT] = bar_yextent;
232     }
233   
234   if (correct &&stem_dirs[LEFT] *stem_dirs[RIGHT] == -1)
235     {
236       
237       intersect = stem_posns[LEFT];  
238       intersect.intersect(stem_posns[RIGHT]);
239       correct = correct && !intersect.empty_b ();
240
241       if (!correct)
242         return 0.0;
243       /*
244         Ugh. 7 is hardcoded.
245       */
246       correction = abs (intersect.length ());
247       correction = (correction/7) <? 1.0;
248       correction *= stem_dirs[LEFT] ;
249       correction *= gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
250
251       if (!bar_yextent.empty_b())
252         {
253           correction *= 0.5;
254         }
255     }
256   else if (correct)
257     {
258       /*
259         Correct for the following situation:
260
261          X      X
262         |      | 
263         |      |
264         |   X  |
265         |  |   |
266         ========
267
268            ^ move the center one to the left.
269         
270
271         this effect seems to be much more subtle than the
272         stem-direction stuff (why?), and also does not scale with the
273         difference in stem length.
274         
275        */
276
277       
278       Interval hp = head_posns[LEFT];
279       hp.intersect  (head_posns[RIGHT]);
280       if (!hp.empty_b())
281         return 0.0;
282
283       Direction lowest =
284         (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
285
286       Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP] ;
287       Real corr = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
288       corr =  (delta <= 1) ? 0.0 : 0.25;
289       
290       correction=  -lowest * corr ;
291     }
292
293   if (!bar_xextent.empty_b())
294     correction += - bar_xextent[LEFT];
295   
296  exit_func:
297   return correction;
298 }
299