]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-spacing.cc
Nitpick run.
[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     We don't do complicated stuff: (base_space - increment) is the
141     normal amount of white, which also determines the amount of
142     stretch. Upon (extreme) stretching, notes with accidentals should
143     stretch as much as notes without accidentals.
144   */
145   *space = (base_space - increment) + *fixed;
146
147   if (!extents[RIGHT].is_empty ()
148       && (Item::is_breakable (right_col)
149           || right_col->original_))
150     {
151       /*
152         This is for the situation
153
154         rest | 3/4 (eol)
155
156         Since we only take half of the right-object space above, the
157         barline will bump into the notes preceding it, if the right
158         thing is big. We add the rest of the extents here:
159       */
160
161       *space += -extents[RIGHT][LEFT] / 2;
162       *fixed += -extents[RIGHT][LEFT] / 2;
163     }
164
165   stem_dir_correction (me, right_col, increment, space, fixed);
166 }
167
168 Item *
169 Note_spacing::left_column (Grob *me)
170 {
171   if (!me->is_live ())
172     return 0;
173
174   return dynamic_cast<Item *> (me)->get_column ();
175 }
176
177 /*
178   Compute the column of the right-items.  This is a big function,
179   since RIGHT-ITEMS may span more columns (eg. if a clef if inserted,
180   this will add a new columns to RIGHT-ITEMS. Here we look at the
181   columns, and return the left-most. If there are multiple columns, we
182   prune RIGHT-ITEMS.
183 */
184 Item *
185 Note_spacing::right_column (Grob *me)
186 {
187   if (!me->is_live ())
188     return 0;
189
190   Grob_array *a = unsmob_grob_array (me->get_object ("right-items"));
191   Item *mincol = 0;
192   int min_rank = INT_MAX;
193   bool prune = false;
194   for (int i = 0; a && i < a->size (); i++)
195     {
196       Item *ri = a->item (i);
197       Item *col = ri->get_column ();
198
199       int rank = Paper_column::get_rank (col);
200
201       if (rank < min_rank)
202         {
203           min_rank = rank;
204           if (mincol)
205             prune = true;
206
207           mincol = col;
208         }
209     }
210
211   if (prune && a)
212     {
213       Link_array<Grob> &right = a->array_reference ();
214       for (int i = right.size (); i--;)
215         {
216           if (dynamic_cast<Item *> (right[i])->get_column () != mincol)
217             right.del (i);
218         }
219     }
220
221   if (!mincol)
222     return 0;
223
224   return mincol;
225 }
226
227 /**
228    Correct for optical illusions. See [Wanske] p. 138. The combination
229    up-stem + down-stem should get extra space, the combination
230    down-stem + up-stem less.
231
232    TODO: have to check wether the stems are in the same staff.
233 */
234 void
235 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
236                                    Real increment,
237                                    Real *space, Real *fixed)
238 {
239   Drul_array<Direction> stem_dirs (CENTER, CENTER);
240   Drul_array<Interval> stem_posns;
241   Drul_array<Interval> head_posns;
242   Drul_array<SCM> props (me->get_object ("left-items"),
243                          me->get_object ("right-items"));
244
245   Drul_array<Spanner *> beams_drul (0, 0);
246   Drul_array<Grob *> stems_drul (0, 0);
247
248   stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
249   Interval intersect;
250   Interval bar_xextent;
251   Interval bar_yextent;
252
253   bool correct_stem_dirs = true;
254   Direction d = LEFT;
255   bool acc_right = false;
256
257   do
258     {
259       Link_array<Grob> const &items (ly_scm2link_array (props [d]));
260       for (int i = 0; i < items.size (); i++)
261         {
262           Item *it = dynamic_cast<Item *> (items[i]);
263
264           if (d == RIGHT)
265             acc_right = acc_right || Note_column::accidentals (it);
266
267           Grob *stem = Note_column::get_stem (it);
268
269           if (!stem || !stem->is_live ())
270             {
271               if (d == RIGHT && Separation_item::has_interface (it))
272                 {
273                   if (it->get_column () != rcolumn)
274                     it = it->find_prebroken_piece (rcolumn->break_status_dir ());
275
276                   Grob *last = Separation_item::extremal_break_aligned_grob (it, LEFT, &bar_xextent);
277
278                   if (last)
279                     bar_yextent = Staff_spacing::bar_y_positions (last);
280
281                   break;
282                 }
283
284               return;
285             }
286
287           if (Stem::is_invisible (stem))
288             {
289               correct_stem_dirs = false;
290               continue;
291             }
292
293           stems_drul[d] = stem;
294           beams_drul[d] = Stem::get_beam (stem);
295
296           Direction sd = Stem::get_direction (stem);
297           if (stem_dirs[d] && stem_dirs[d] != sd)
298             {
299               correct_stem_dirs = false;
300               continue;
301             }
302           stem_dirs[d] = sd;
303
304           /*
305             Correction doesn't seem appropriate  when there is a large flag
306             hanging from the note.
307           */
308           if (d == LEFT
309               && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
310             correct_stem_dirs = false;
311
312           Interval hp = Stem::head_positions (stem);
313           if (!hp.is_empty ())
314             {
315               Real chord_start = hp[sd];
316               Real stem_end = Stem::stem_end_position (stem);
317
318               stem_posns[d] = Interval (min (chord_start, stem_end), max (chord_start, stem_end));
319               head_posns[d].unite (hp);
320             }
321         }
322     }
323   while (flip (&d) != LEFT);
324
325   /*
326     don't correct if accidentals are sticking out of the right side.
327   */
328   if (acc_right)
329     return;
330
331   Real correction = 0.0;
332
333   if (!bar_yextent.is_empty ())
334     {
335       stem_dirs[RIGHT] = -stem_dirs[LEFT];
336       stem_posns[RIGHT] = bar_yextent;
337     }
338
339   if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
340     {
341       if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
342         {
343
344           /*
345             this is a knee: maximal correction.
346           */
347           Real note_head_width = increment;
348           Grob *st = stems_drul[RIGHT];
349           Grob *head = st ? Stem::support_head (st) : 0;
350
351           Interval head_extent;
352           if (head)
353             {
354               head_extent = head->extent (rcolumn, X_AXIS);
355
356               if (!head_extent.is_empty ())
357                 note_head_width = head_extent[RIGHT];
358
359               if (st)
360                 {
361                   Real thick = Stem::thickness (st);
362
363                   note_head_width -= thick;
364                 }
365             }
366
367           correction = note_head_width * stem_dirs[LEFT];
368           correction *= robust_scm2double (me->get_property ("knee-spacing-correction"), 0);
369           *fixed += correction;
370         }
371       else
372         {
373           intersect = stem_posns[LEFT];
374           intersect.intersect (stem_posns[RIGHT]);
375           correct_stem_dirs = correct_stem_dirs && !intersect.is_empty ();
376
377           if (correct_stem_dirs)
378             {
379               correction = abs (intersect.length ());
380
381               /*
382                 Ugh. 7 is hardcoded.
383               */
384               correction = min (correction / 7, 1.0);
385               correction *= stem_dirs[LEFT];
386               correction
387                 *= robust_scm2double (me->get_property ("stem-spacing-correction"), 0);
388             }
389
390           if (!bar_yextent.is_empty ())
391             correction *= 0.5;
392         }
393     }
394   else if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == UP)
395     {
396       /*
397         Correct for the following situation:
398
399         X      X
400         |      |
401         |      |
402         |   X  |
403         |  |   |
404         ========
405
406         ^ move the center one to the left.
407
408
409         this effect seems to be much more subtle than the
410         stem-direction stuff (why?), and also does not scale with the
411         difference in stem length.
412
413       */
414
415       Interval hp = head_posns[LEFT];
416       hp.intersect (head_posns[RIGHT]);
417       if (!hp.is_empty ())
418         return;
419
420       Direction lowest
421         = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
422
423       Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
424       Real corr = robust_scm2double (me->get_property ("same-direction-correction"), 0);
425
426       if (delta > 1)
427         correction = -lowest * corr;
428     }
429
430   *space += correction;
431
432   /* there used to be a correction for bar_xextent () here, but
433      it's unclear what that was good for ?
434   */
435 }
436
437 ADD_INTERFACE (Note_spacing, "note-spacing-interface",
438                "This object calculates spacing wishes for individual voices.",
439                "left-items right-items stem-spacing-correction same-direction-correction knee-spacing-correction");
440