]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-spacing.cc
move extremal_break_aligned_grob to Spacing_interface
[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--2007  Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "note-spacing.hh"
10
11 #include "directional-element-interface.hh"
12 #include "grob-array.hh"
13 #include "paper-column.hh"
14 #include "moment.hh"
15 #include "note-column.hh"
16 #include "warn.hh"
17 #include "stem.hh"
18 #include "separation-item.hh"
19 #include "spacing-interface.hh"
20 #include "staff-spacing.hh"
21 #include "accidental-placement.hh"
22 #include "output-def.hh"
23 #include "pointer-group-interface.hh"
24
25 /*
26   TODO: detect hshifts due to collisions, and account for them in
27   spacing?
28 */
29
30 Spring
31 Note_spacing::get_spacing (Grob *me, Item *right_col,
32                            Real base_space, Real increment)
33 {
34   vector<Item*> note_columns = Spacing_interface::left_note_columns (me);
35   Real left_head_end = 0;
36
37   for (vsize i = 0; i < note_columns.size (); i++)
38     {
39        SCM r = note_columns[i]->get_object ("rest");
40        Grob *g = unsmob_grob (r);
41        Grob *col = note_columns[i]->get_column ();
42
43        if (!g)
44          g = Note_column::first_head (note_columns[i]);
45
46        /*
47          Ugh. If Stem is switched off, we don't know what the
48          first note head will be.
49        */
50        if (g)
51          {
52            if (g->common_refpoint (col, X_AXIS) != col)
53              programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect");
54            else
55              left_head_end = g->extent (col, X_AXIS)[RIGHT];
56          }
57     }
58
59   /*
60     The main factor that determines the amount of space is the width of the
61     note head (or the rest). For example, a quarter rest gets almost 0.5 ss
62     less horizontal space than a note.
63
64     The other parts of a note column (eg. flags, accidentals, etc.) don't get
65     the full amount of space. We give them half the amount of space, but then
66     adjust things so there are no collisions.
67   */
68   Drul_array<Skyline> skys = Spacing_interface::skylines (me, right_col);
69   Real min_dist = max (0.0, skys[LEFT].distance (skys[RIGHT]));
70   Real min_desired_space = left_head_end + (min_dist - left_head_end) / 2;
71
72   /* if the right object sticks out a lot, include a bit of extra space.
73      But only for non-musical-columns; this shouldn't apply to accidentals */
74   if (!Paper_column::is_musical (right_col))
75     min_desired_space = max (min_desired_space,
76                              left_head_end + LEFT * skys[RIGHT].max_height ());
77
78   Real ideal = base_space - increment + min_desired_space;
79
80   stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space);
81
82   Spring ret (ideal, min_dist);
83   ret.set_inverse_compress_strength (max (0.0, ideal - max (min_dist, min_desired_space)));
84   ret.set_inverse_stretch_strength (max (0.1, base_space - increment));
85   return ret;
86 }
87
88
89 /**
90    Correct for optical illusions. See [Wanske] p. 138. The combination
91    up-stem + down-stem should get extra space, the combination
92    down-stem + up-stem less.
93
94    TODO: have to check whether the stems are in the same staff.
95 */
96 void
97 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
98                                    Real increment,
99                                    Real *space, Real *fixed)
100 {
101   Drul_array<Direction> stem_dirs (CENTER, CENTER);
102   Drul_array<Interval> stem_posns;
103   Drul_array<Interval> head_posns;
104   Drul_array<SCM> props (me->get_object ("left-items"),
105                          me->get_object ("right-items"));
106
107   Drul_array<Spanner *> beams_drul (0, 0);
108   Drul_array<Grob *> stems_drul (0, 0);
109
110   stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
111   Interval intersect;
112   Interval bar_xextent;
113   Interval bar_yextent;
114
115   bool correct_stem_dirs = true;
116   Direction d = LEFT;
117   bool acc_right = false;
118
119   Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT,
120                                                               rcolumn->break_status_dir (),
121                                                               &bar_xextent);
122   if (bar)
123     bar_yextent = Staff_spacing::bar_y_positions (bar);
124
125   do
126     {
127       vector<Grob*> const &items (ly_scm2link_array (props [d]));
128       for (vsize i = 0; i < items.size (); i++)
129         {
130           Item *it = dynamic_cast<Item *> (items[i]);
131
132           if (d == RIGHT)
133             acc_right = acc_right || Note_column::accidentals (it);
134
135           Grob *stem = Note_column::get_stem (it);
136
137           if (!stem || !stem->is_live ())
138             return;
139
140           if (Stem::is_invisible (stem))
141             {
142               correct_stem_dirs = false;
143               continue;
144             }
145
146           stems_drul[d] = stem;
147           beams_drul[d] = Stem::get_beam (stem);
148
149           Direction stem_dir = get_grob_direction (stem);
150           if (stem_dirs[d] && stem_dirs[d] != stem_dir)
151             {
152               correct_stem_dirs = false;
153               continue;
154             }
155           stem_dirs[d] = stem_dir;
156
157           /*
158             Correction doesn't seem appropriate  when there is a large flag
159             hanging from the note.
160           */
161           if (d == LEFT
162               && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
163             correct_stem_dirs = false;
164
165           Interval hp = Stem::head_positions (stem);
166           if (correct_stem_dirs
167               && !hp.is_empty ())
168             {
169               Real chord_start = hp[stem_dir];
170
171               /*
172                 can't look at stem-end-position, since that triggers
173                 beam slope computations.
174               */
175               Real stem_end = hp[stem_dir] +
176                 stem_dir * robust_scm2double (stem->get_property ("length"), 7);
177
178               stem_posns[d] = Interval (min (chord_start, stem_end),
179                                         max (chord_start, stem_end));
180               head_posns[d].unite (hp);
181             }
182         }
183     }
184   while (flip (&d) != LEFT);
185
186   /*
187     don't correct if accidentals are sticking out of the right side.
188   */
189   if (acc_right)
190     return;
191
192   Real correction = 0.0;
193
194   if (!bar_yextent.is_empty ())
195     {
196       stem_dirs[RIGHT] = -stem_dirs[LEFT];
197       stem_posns[RIGHT] = bar_yextent;
198       stem_posns[RIGHT] *= 2;
199     }
200
201   if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
202     {
203       if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
204         {
205
206           /*
207             this is a knee: maximal correction.
208           */
209           Real note_head_width = increment;
210           Grob *st = stems_drul[RIGHT];
211           Grob *head = st ? Stem::support_head (st) : 0;
212
213           Interval head_extent;
214           if (head)
215             {
216               head_extent = head->extent (rcolumn, X_AXIS);
217
218               if (!head_extent.is_empty ())
219                 note_head_width = head_extent[RIGHT];
220
221               note_head_width -= Stem::thickness (st);
222             }
223
224           correction = note_head_width * stem_dirs[LEFT];
225           correction *= robust_scm2double (me->get_property ("knee-spacing-correction"), 0);
226           *fixed += correction;
227         }
228       else
229         {
230           intersect = stem_posns[LEFT];
231           intersect.intersect (stem_posns[RIGHT]);
232           correct_stem_dirs = correct_stem_dirs && !intersect.is_empty ();
233
234           if (correct_stem_dirs)
235             {
236               correction = abs (intersect.length ());
237
238               /*
239                 Ugh. 7 is hardcoded.
240               */
241               correction = min (correction / 7, 1.0);
242               correction *= stem_dirs[LEFT];
243               correction
244                 *= robust_scm2double (me->get_property ("stem-spacing-correction"), 0);
245             }
246
247           if (!bar_yextent.is_empty ())
248             correction *= 0.5;
249         }
250     }
251   else if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == UP)
252     {
253       /*
254         Correct for the following situation:
255
256         X      X
257         |      |
258         |      |
259         |   X  |
260         |  |   |
261         ========
262
263         ^ move the center one to the left.
264
265
266         this effect seems to be much more subtle than the
267         stem-direction stuff (why?), and also does not scale with the
268         difference in stem length.
269
270       */
271
272       Interval hp = head_posns[LEFT];
273       hp.intersect (head_posns[RIGHT]);
274       if (!hp.is_empty ())
275         return;
276
277       Direction lowest
278         = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
279
280       Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
281       Real corr = robust_scm2double (me->get_property ("same-direction-correction"), 0);
282
283       if (delta > 1)
284         correction = -lowest * corr;
285     }
286
287   *space += correction;
288
289   /* there used to be a correction for bar_xextent () here, but
290      it's unclear what that was good for ?
291   */
292 }
293
294 ADD_INTERFACE (Note_spacing,
295                "This object calculates spacing wishes for individual voices.",
296
297                
298                "knee-spacing-correction "
299                "left-items "
300                "right-items "
301                "same-direction-correction "
302                "stem-spacing-correction "
303
304                );
305