2 note-spacing.cc -- implement Note_spacing
4 source file of the GNU LilyPond music typesetter
6 (c) 2001--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "note-spacing.hh"
11 #include "directional-element-interface.hh"
12 #include "grob-array.hh"
13 #include "paper-column.hh"
15 #include "note-column.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"
26 TODO: detect hshifts due to collisions, and account for them in
31 Note_spacing::get_spacing (Grob *me, Item *right_col,
32 Real base_space, Real increment)
34 vector<Item*> note_columns = Spacing_interface::left_note_columns (me);
35 Real left_head_end = 0;
37 for (vsize i = 0; i < note_columns.size (); i++)
39 SCM r = note_columns[i]->get_object ("rest");
40 Grob *g = unsmob_grob (r);
41 Grob *col = note_columns[i]->get_column ();
44 g = Note_column::first_head (note_columns[i]);
47 Ugh. If Stem is switched off, we don't know what the
48 first note head will be.
52 if (g->common_refpoint (col, X_AXIS) != col)
53 programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect");
55 left_head_end = g->extent (col, X_AXIS)[RIGHT];
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.
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.
68 Real min_dist = Spacing_interface::minimum_distance (me, right_col);
69 Real min_desired_space = left_head_end + (min_dist - left_head_end) / 2;
70 Real ideal = base_space - increment + min_desired_space;
72 stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space);
74 Spring ret (ideal, min_dist);
75 ret.set_inverse_compress_strength (max (0.0, ideal - max (min_dist, min_desired_space)));
76 ret.set_inverse_stretch_strength (max (0.1, base_space - increment));
82 Correct for optical illusions. See [Wanske] p. 138. The combination
83 up-stem + down-stem should get extra space, the combination
84 down-stem + up-stem less.
86 TODO: have to check whether the stems are in the same staff.
89 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
91 Real *space, Real *fixed)
93 Drul_array<Direction> stem_dirs (CENTER, CENTER);
94 Drul_array<Interval> stem_posns;
95 Drul_array<Interval> head_posns;
96 Drul_array<SCM> props (me->get_object ("left-items"),
97 me->get_object ("right-items"));
99 Drul_array<Spanner *> beams_drul (0, 0);
100 Drul_array<Grob *> stems_drul (0, 0);
102 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
104 Interval bar_xextent;
105 Interval bar_yextent;
107 bool correct_stem_dirs = true;
109 bool acc_right = false;
113 vector<Grob*> const &items (ly_scm2link_array (props [d]));
114 for (vsize i = 0; i < items.size (); i++)
116 Item *it = dynamic_cast<Item *> (items[i]);
119 acc_right = acc_right || Note_column::accidentals (it);
121 Grob *stem = Note_column::get_stem (it);
123 if (!stem || !stem->is_live ())
125 if (d == RIGHT && Separation_item::has_interface (it))
127 if (it->get_column () != rcolumn)
128 it = it->find_prebroken_piece (rcolumn->break_status_dir ());
130 Grob *last = Separation_item::extremal_break_aligned_grob (it, LEFT, &bar_xextent);
133 bar_yextent = Staff_spacing::bar_y_positions (last);
141 if (Stem::is_invisible (stem))
143 correct_stem_dirs = false;
147 stems_drul[d] = stem;
148 beams_drul[d] = Stem::get_beam (stem);
150 Direction stem_dir = get_grob_direction (stem);
151 if (stem_dirs[d] && stem_dirs[d] != stem_dir)
153 correct_stem_dirs = false;
156 stem_dirs[d] = stem_dir;
159 Correction doesn't seem appropriate when there is a large flag
160 hanging from the note.
163 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
164 correct_stem_dirs = false;
166 Interval hp = Stem::head_positions (stem);
167 if (correct_stem_dirs
170 Real chord_start = hp[stem_dir];
173 can't look at stem-end-position, since that triggers
174 beam slope computations.
176 Real stem_end = hp[stem_dir] +
177 stem_dir * robust_scm2double (stem->get_property ("length"), 7);
179 stem_posns[d] = Interval (min (chord_start, stem_end),
180 max (chord_start, stem_end));
181 head_posns[d].unite (hp);
185 while (flip (&d) != LEFT);
188 don't correct if accidentals are sticking out of the right side.
193 Real correction = 0.0;
195 if (!bar_yextent.is_empty ())
197 stem_dirs[RIGHT] = -stem_dirs[LEFT];
198 stem_posns[RIGHT] = bar_yextent;
199 stem_posns[RIGHT] *= 2;
202 if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
204 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
208 this is a knee: maximal correction.
210 Real note_head_width = increment;
211 Grob *st = stems_drul[RIGHT];
212 Grob *head = st ? Stem::support_head (st) : 0;
214 Interval head_extent;
217 head_extent = head->extent (rcolumn, X_AXIS);
219 if (!head_extent.is_empty ())
220 note_head_width = head_extent[RIGHT];
222 note_head_width -= Stem::thickness (st);
225 correction = note_head_width * stem_dirs[LEFT];
226 correction *= robust_scm2double (me->get_property ("knee-spacing-correction"), 0);
227 *fixed += correction;
231 intersect = stem_posns[LEFT];
232 intersect.intersect (stem_posns[RIGHT]);
233 correct_stem_dirs = correct_stem_dirs && !intersect.is_empty ();
235 if (correct_stem_dirs)
237 correction = abs (intersect.length ());
242 correction = min (correction / 7, 1.0);
243 correction *= stem_dirs[LEFT];
245 *= robust_scm2double (me->get_property ("stem-spacing-correction"), 0);
248 if (!bar_yextent.is_empty ())
252 else if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == UP)
255 Correct for the following situation:
264 ^ move the center one to the left.
267 this effect seems to be much more subtle than the
268 stem-direction stuff (why?), and also does not scale with the
269 difference in stem length.
273 Interval hp = head_posns[LEFT];
274 hp.intersect (head_posns[RIGHT]);
279 = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
281 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
282 Real corr = robust_scm2double (me->get_property ("same-direction-correction"), 0);
285 correction = -lowest * corr;
288 *space += correction;
290 /* there used to be a correction for bar_xextent () here, but
291 it's unclear what that was good for ?
295 ADD_INTERFACE (Note_spacing,
296 "This object calculates spacing wishes for individual voices.",
299 "knee-spacing-correction "
302 "same-direction-correction "
303 "stem-spacing-correction "