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 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;
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 ());
78 Real ideal = base_space - increment + min_desired_space;
80 stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space);
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));
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.
94 TODO: have to check whether the stems are in the same staff.
97 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
99 Real *space, Real *fixed)
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"));
107 Drul_array<Spanner *> beams_drul (0, 0);
108 Drul_array<Grob *> stems_drul (0, 0);
110 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
112 Interval bar_xextent;
113 Interval bar_yextent;
115 bool correct_stem_dirs = true;
117 bool acc_right = false;
119 Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT,
120 rcolumn->break_status_dir (),
123 bar_yextent = Staff_spacing::bar_y_positions (bar);
127 vector<Grob*> const &items (ly_scm2link_array (props [d]));
128 for (vsize i = 0; i < items.size (); i++)
130 Item *it = dynamic_cast<Item *> (items[i]);
133 acc_right = acc_right || Note_column::accidentals (it);
135 Grob *stem = Note_column::get_stem (it);
137 if (!stem || !stem->is_live ())
140 if (Stem::is_invisible (stem))
142 correct_stem_dirs = false;
146 stems_drul[d] = stem;
147 beams_drul[d] = Stem::get_beam (stem);
149 Direction stem_dir = get_grob_direction (stem);
150 if (stem_dirs[d] && stem_dirs[d] != stem_dir)
152 correct_stem_dirs = false;
155 stem_dirs[d] = stem_dir;
158 Correction doesn't seem appropriate when there is a large flag
159 hanging from the note.
162 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
163 correct_stem_dirs = false;
165 Interval hp = Stem::head_positions (stem);
166 if (correct_stem_dirs
169 Real chord_start = hp[stem_dir];
172 can't look at stem-end-position, since that triggers
173 beam slope computations.
175 Real stem_end = hp[stem_dir] +
176 stem_dir * robust_scm2double (stem->get_property ("length"), 7);
178 stem_posns[d] = Interval (min (chord_start, stem_end),
179 max (chord_start, stem_end));
180 head_posns[d].unite (hp);
184 while (flip (&d) != LEFT);
187 don't correct if accidentals are sticking out of the right side.
192 Real correction = 0.0;
194 if (!bar_yextent.is_empty ())
196 stem_dirs[RIGHT] = -stem_dirs[LEFT];
197 stem_posns[RIGHT] = bar_yextent;
198 stem_posns[RIGHT] *= 2;
201 if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
203 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
207 this is a knee: maximal correction.
209 Real note_head_width = increment;
210 Grob *st = stems_drul[RIGHT];
211 Grob *head = st ? Stem::support_head (st) : 0;
213 Interval head_extent;
216 head_extent = head->extent (rcolumn, X_AXIS);
218 if (!head_extent.is_empty ())
219 note_head_width = head_extent[RIGHT];
221 note_head_width -= Stem::thickness (st);
224 correction = note_head_width * stem_dirs[LEFT];
225 correction *= robust_scm2double (me->get_property ("knee-spacing-correction"), 0);
226 *fixed += correction;
230 intersect = stem_posns[LEFT];
231 intersect.intersect (stem_posns[RIGHT]);
232 correct_stem_dirs = correct_stem_dirs && !intersect.is_empty ();
234 if (correct_stem_dirs)
236 correction = abs (intersect.length ());
241 correction = min (correction / 7, 1.0);
242 correction *= stem_dirs[LEFT];
244 *= robust_scm2double (me->get_property ("stem-spacing-correction"), 0);
247 if (!bar_yextent.is_empty ())
251 else if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == UP)
254 Correct for the following situation:
263 ^ move the center one to the left.
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.
272 Interval hp = head_posns[LEFT];
273 hp.intersect (head_posns[RIGHT]);
278 = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
280 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
281 Real corr = robust_scm2double (me->get_property ("same-direction-correction"), 0);
284 correction = -lowest * corr;
287 *space += correction;
289 /* there used to be a correction for bar_xextent () here, but
290 it's unclear what that was good for ?
294 ADD_INTERFACE (Note_spacing,
295 "This object calculates spacing wishes for individual voices.",
298 "knee-spacing-correction "
301 "same-direction-correction "
302 "stem-spacing-correction "