]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-spacing.cc
(parse_symbol_list): Bugfix.
[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--2005  Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "note-spacing.hh"
10
11 #include "grob-array.hh"
12 #include "paper-column.hh"
13 #include "moment.hh"
14 #include "note-column.hh"
15 #include "warn.hh"
16 #include "stem.hh"
17 #include "separation-item.hh"
18 #include "staff-spacing.hh"
19 #include "accidental-placement.hh"
20 #include "output-def.hh"
21 #include "pointer-group-interface.hh"
22
23 /*
24   TODO: detect hshifts due to collisions, and account for them in
25   spacing?
26 */
27
28 void
29 Note_spacing::get_spacing (Grob *me, Item *right_col,
30                            Real base_space, Real increment, Real *space, Real *fixed)
31 {
32   Drul_array<SCM> props (me->get_object ("left-items"),
33                          me->get_object ("right-items"));
34   Direction d = LEFT;
35   Direction col_dir = right_col->break_status_dir ();
36   Drul_array<Interval> extents;
37
38   Interval left_head_wid;
39   do
40     {
41       Link_array<Grob> const &items (ly_scm2link_array (props [d]));
42       for (int i = items.size (); i--;)
43         {
44           Item *it = dynamic_cast<Item *> (items[i]);
45
46           if (d == RIGHT && it->break_status_dir () != col_dir)
47             it = it->find_prebroken_piece (col_dir);
48
49           /*
50             some kind of mismatch, eg. a note column, that is behind a
51             linebreak.
52           */
53           if (!it)
54             continue;
55
56           Item *it_col = it->get_column ();
57           if (d == RIGHT && right_col != it_col)
58             continue;
59
60           if (Separation_item::has_interface (it))
61             {
62               extents[d].unite (Separation_item::width (it));
63               continue;
64             }
65
66           if (d == LEFT)
67             {
68               SCM r = it->get_object ("rest");
69               Grob *g = unsmob_grob (r);
70               if (!g)
71                 g = Note_column::first_head (it);
72
73               /*
74                 Ugh. If Stem is switched off, we don't know what the
75                 first note head will be.
76               */
77               if (g)
78                 left_head_wid = g->extent (it_col, X_AXIS);
79             }
80
81           extents[d].unite (it->extent (it_col, X_AXIS));
82           if (d == RIGHT)
83             {
84               Grob *accs = Note_column::accidentals (it);
85               if (!accs)
86                 accs = Note_column::accidentals (it->get_parent (X_AXIS));
87
88               if (accs)
89                 {
90                   Interval v
91                     = Accidental_placement::get_relevant_accidental_extent (accs, it_col, me);
92
93                   extents[d].unite (v);
94                 }
95
96               if (Grob *arpeggio = Note_column::arpeggio (it))
97                 extents[d].unite (arpeggio->extent (it_col, X_AXIS));
98             }
99         }
100
101       if (extents[d].is_empty ())
102         extents[d] = Interval (0, 0);
103     }
104   while (flip (&d) != LEFT);
105
106   /*
107     We look at the width of the note head, since smaller heads get less space
108     eg. a quarter rest gets almost 0.5 ss less horizontal space than a note.
109
110     What is sticking out of the note head (eg. a flag), doesn't get
111     the full amount of space.
112
113     FIXED also includes the left part of the right object.
114   */
115   *fixed
116     = (left_head_wid.is_empty () ? increment
117        :        /*
118                   Size of the head:
119                 */
120        (left_head_wid[RIGHT]+
121
122         /*
123           What's sticking out of the head, eg. a flag:
124         */
125         (extents[LEFT][RIGHT] - left_head_wid[RIGHT]) / 2))
126
127     /*
128       What is sticking out on the left side of the right note:
129     */
130     + (extents[RIGHT].is_empty ()
131        ? 0.0
132        : ((- extents[RIGHT][LEFT] / 2)
133
134           /*
135             Add that which sticks out a lot.
136           */
137           + max (0.0, -extents[RIGHT][LEFT] - (base_space - increment))));
138
139
140   /*
141     We don't do complicated stuff: (base_space - increment) is the
142     normal amount of white, which also determines the amount of
143     stretch. Upon (extreme) stretching, notes with accidentals should
144     stretch as much as notes without accidentals.
145   */
146   *space = (base_space - increment) + *fixed;
147
148   if (!extents[RIGHT].is_empty ()
149       && (Item::is_breakable (right_col)
150           || right_col->original_))
151     {
152       /*
153         This is for the situation
154
155         rest | 3/4 (eol)
156
157         Since we only take half of the right-object space above, the
158         barline will bump into the notes preceding it, if the right
159         thing is big. We add the rest of the extents here:
160       */
161
162       *space += -extents[RIGHT][LEFT] / 2;
163       *fixed += -extents[RIGHT][LEFT] / 2;
164     }
165
166   stem_dir_correction (me, right_col, increment, space, fixed);
167 }
168
169 Item *
170 Note_spacing::left_column (Grob *me)
171 {
172   if (!me->is_live ())
173     return 0;
174
175   return dynamic_cast<Item *> (me)->get_column ();
176 }
177
178 /*
179   Compute the column of the right-items.  This is a big function,
180   since RIGHT-ITEMS may span more columns (eg. if a clef if inserted,
181   this will add a new columns to RIGHT-ITEMS. Here we look at the
182   columns, and return the left-most. If there are multiple columns, we
183   prune RIGHT-ITEMS.
184 */
185 Item *
186 Note_spacing::right_column (Grob *me)
187 {
188   if (!me->is_live ())
189     return 0;
190
191   Grob_array *a = unsmob_grob_array (me->get_object ("right-items"));
192   Item *mincol = 0;
193   int min_rank = INT_MAX;
194   bool prune = false;
195   for (int i = 0; a && i < a->size (); i++)
196     {
197       Item *ri = a->item (i);
198       Item *col = ri->get_column ();
199
200       int rank = Paper_column::get_rank (col);
201
202       if (rank < min_rank)
203         {
204           min_rank = rank;
205           if (mincol)
206             prune = true;
207
208           mincol = col;
209         }
210     }
211
212   if (prune && a)
213     {
214       Link_array<Grob> &right = a->array_reference ();
215       for (int i = right.size (); i--;)
216         {
217           if (dynamic_cast<Item *> (right[i])->get_column () != mincol)
218             right.del (i);
219         }
220     }
221
222   if (!mincol)
223     {
224       return 0;
225     }
226
227   return mincol;
228 }
229
230 /**
231    Correct for optical illusions. See [Wanske] p. 138. The combination
232    up-stem + down-stem should get extra space, the combination
233    down-stem + up-stem less.
234
235    TODO: have to check wether the stems are in the same staff.
236 */
237 void
238 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
239                                    Real increment,
240                                    Real *space, Real *fixed)
241 {
242   Drul_array<Direction> stem_dirs (CENTER, CENTER);
243   Drul_array<Interval> stem_posns;
244   Drul_array<Interval> head_posns;
245   Drul_array<SCM> props (me->get_object ("left-items"),
246                          me->get_object ("right-items"));
247
248   Drul_array<Spanner *> beams_drul (0, 0);
249   Drul_array<Grob *> stems_drul (0, 0);
250
251   stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
252   Interval intersect;
253   Interval bar_xextent;
254   Interval bar_yextent;
255
256   bool correct_stem_dirs = true;
257   Direction d = LEFT;
258   bool acc_right = false;
259
260   do
261     {
262       Link_array<Grob> const &items (ly_scm2link_array (props [d]));
263       for (int i = 0; i < items.size (); i++)
264         {
265           Item *it = dynamic_cast<Item *> (items[i]);
266
267           if (d == RIGHT)
268             acc_right = acc_right || Note_column::accidentals (it);
269
270           Grob *stem = Note_column::get_stem (it);
271
272           if (!stem || !stem->is_live ())
273             {
274               if (d == RIGHT && Separation_item::has_interface (it))
275                 {
276                   if (it->get_column () != rcolumn)
277                     it = it->find_prebroken_piece (rcolumn->break_status_dir ());
278
279                   Grob *last = Separation_item::extremal_break_aligned_grob (it, LEFT, &bar_xextent);
280
281                   if (last)
282                     bar_yextent = Staff_spacing::bar_y_positions (last);
283
284                   break;
285                 }
286
287               return;
288             }
289
290           if (Stem::is_invisible (stem))
291             {
292               correct_stem_dirs = false;
293               continue;
294             }
295
296           stems_drul[d] = stem;
297           beams_drul[d] = Stem::get_beam (stem);
298
299           Direction sd = Stem::get_direction (stem);
300           if (stem_dirs[d] && stem_dirs[d] != sd)
301             {
302               correct_stem_dirs = false;
303               continue;
304             }
305           stem_dirs[d] = sd;
306
307           /*
308             Correction doesn't seem appropriate  when there is a large flag
309             hanging from the note.
310           */
311           if (d == LEFT
312               && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
313             correct_stem_dirs = false;
314
315           Interval hp = Stem::head_positions (stem);
316           if (!hp.is_empty ())
317             {
318               Real chord_start = hp[sd];
319               Real stem_end = Stem::stem_end_position (stem);
320
321               stem_posns[d] = Interval (min (chord_start, stem_end), max (chord_start, stem_end));
322               head_posns[d].unite (hp);
323             }
324         }
325     }
326   while (flip (&d) != LEFT);
327
328   /*
329     don't correct if accidentals are sticking out of the right side.
330   */
331   if (acc_right)
332     return;
333
334   Real correction = 0.0;
335
336   if (!bar_yextent.is_empty ())
337     {
338       stem_dirs[RIGHT] = -stem_dirs[LEFT];
339       stem_posns[RIGHT] = bar_yextent;
340     }
341
342   if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
343     {
344       if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
345         {
346
347           /*
348             this is a knee: maximal correction.
349           */
350           Real note_head_width = increment;
351           Grob *st = stems_drul[RIGHT];
352           Grob *head = st ? Stem::support_head (st) : 0;
353
354           Interval head_extent;
355           if (head)
356             {
357               head_extent = head->extent (rcolumn, X_AXIS);
358
359               if (!head_extent.is_empty ())
360                 note_head_width = head_extent[RIGHT];
361
362               if (st)
363                 {
364                   Real thick = Stem::thickness (st);
365
366                   note_head_width -= thick;
367                 }
368             }
369
370           correction = note_head_width * stem_dirs[LEFT];
371           correction *= robust_scm2double (me->get_property ("knee-spacing-correction"), 0);
372           *fixed += correction;
373         }
374       else
375         {
376           intersect = stem_posns[LEFT];
377           intersect.intersect (stem_posns[RIGHT]);
378           correct_stem_dirs = correct_stem_dirs && !intersect.is_empty ();
379
380           if (correct_stem_dirs)
381             {
382               correction = abs (intersect.length ());
383
384               /*
385                 Ugh. 7 is hardcoded.
386               */
387               correction = min (correction / 7, 1.0);
388               correction *= stem_dirs[LEFT];
389               correction
390                 *= robust_scm2double (me->get_property ("stem-spacing-correction"), 0);
391             }
392
393           if (!bar_yextent.is_empty ())
394             correction *= 0.5;
395         }
396     }
397   else if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == UP)
398     {
399       /*
400         Correct for the following situation:
401
402         X      X
403         |      |
404         |      |
405         |   X  |
406         |  |   |
407         ========
408
409         ^ move the center one to the left.
410
411
412         this effect seems to be much more subtle than the
413         stem-direction stuff (why?), and also does not scale with the
414         difference in stem length.
415
416       */
417
418       Interval hp = head_posns[LEFT];
419       hp.intersect (head_posns[RIGHT]);
420       if (!hp.is_empty ())
421         return;
422
423       Direction lowest
424         = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
425
426       Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
427       Real corr = robust_scm2double (me->get_property ("same-direction-correction"), 0);
428
429       if (delta > 1)
430         correction = -lowest * corr;
431     }
432
433   *space += correction;
434
435   /* there used to be a correction for bar_xextent () here, but
436      it's unclear what that was good for ?
437   */
438 }
439
440 ADD_INTERFACE (Note_spacing, "note-spacing-interface",
441                "This object calculates spacing wishes for individual voices.",
442                "left-items right-items stem-spacing-correction same-direction-correction knee-spacing-correction");
443