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));
89 knee_correction (Grob *note_spacing, Grob *right_stem, Real increment)
91 Real note_head_width = increment;
92 Grob *head = right_stem ? Stem::support_head (right_stem) : 0;
93 Grob *rcolumn = dynamic_cast<Item*> (head)->get_column ();
98 head_extent = head->extent (rcolumn, X_AXIS);
100 if (!head_extent.is_empty ())
101 note_head_width = head_extent[RIGHT];
103 note_head_width -= Stem::thickness (right_stem);
106 return -note_head_width * get_grob_direction (right_stem)
107 * robust_scm2double (note_spacing->get_property ("knee-spacing-correction"), 0);
111 different_directions_correction (Grob *note_spacing,
112 Drul_array<Interval> stem_posns,
113 Direction left_stem_dir)
116 Interval intersect = stem_posns[LEFT];
117 intersect.intersect (stem_posns[RIGHT]);
119 if (!intersect.is_empty ())
121 ret = abs (intersect.length ());
126 ret = min (ret / 7, 1.0)
128 * robust_scm2double (note_spacing->get_property ("stem-spacing-correction"), 0);
134 same_direction_correction (Grob *note_spacing, Drul_array<Interval> head_posns)
137 Correct for the following situation:
146 ^ move the center one to the left.
149 this effect seems to be much more subtle than the
150 stem-direction stuff (why?), and also does not scale with the
151 difference in stem length.
155 Interval hp = head_posns[LEFT];
156 hp.intersect (head_posns[RIGHT]);
161 = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
163 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
164 Real corr = robust_scm2double (note_spacing->get_property ("same-direction-correction"), 0);
166 return (delta > 1) ? -lowest * corr : 0;
171 Correct for optical illusions. See [Wanske] p. 138. The combination
172 up-stem + down-stem should get extra space, the combination
173 down-stem + up-stem less.
175 TODO: have to check whether the stems are in the same staff.
178 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
180 Real *space, Real *fixed)
182 Drul_array<Direction> stem_dirs (CENTER, CENTER);
183 Drul_array<Interval> stem_posns;
184 Drul_array<Interval> head_posns;
185 Drul_array<SCM> props (me->get_object ("left-items"),
186 me->get_object ("right-items"));
188 Drul_array<Spanner *> beams_drul (0, 0);
189 Drul_array<Grob *> stems_drul (0, 0);
191 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
193 Interval bar_xextent;
194 Interval bar_yextent;
198 Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT,
199 rcolumn->break_status_dir (),
202 bar_yextent = Staff_spacing::bar_y_positions (bar);
206 vector<Grob*> const &items (ly_scm2link_array (props [d]));
207 for (vsize i = 0; i < items.size (); i++)
209 Item *it = dynamic_cast<Item *> (items[i]);
212 don't correct if accidentals are sticking out of the right side.
214 if (d == RIGHT && Note_column::accidentals (it))
217 Grob *stem = Note_column::get_stem (it);
219 if (!stem || !stem->is_live () || Stem::is_invisible (stem))
222 stems_drul[d] = stem;
223 beams_drul[d] = Stem::get_beam (stem);
225 Direction stem_dir = get_grob_direction (stem);
226 if (stem_dirs[d] && stem_dirs[d] != stem_dir)
229 stem_dirs[d] = stem_dir;
232 Correction doesn't seem appropriate when there is a large flag
233 hanging from the note.
236 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
239 Interval hp = Stem::head_positions (stem);
242 Real chord_start = hp[stem_dir];
245 can't look at stem-end-position, since that triggers
246 beam slope computations.
248 Real stem_end = hp[stem_dir] +
249 stem_dir * robust_scm2double (stem->get_property ("length"), 7);
251 stem_posns[d] = Interval (min (chord_start, stem_end),
252 max (chord_start, stem_end));
253 head_posns[d].unite (hp);
257 while (flip (&d) != LEFT);
259 Real correction = 0.0;
261 if (!bar_yextent.is_empty ())
263 stem_dirs[RIGHT] = -stem_dirs[LEFT];
264 stem_posns[RIGHT] = bar_yextent;
265 stem_posns[RIGHT] *= 2;
268 if (stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
270 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
272 correction = knee_correction (me, stems_drul[RIGHT], increment);
273 *fixed += correction;
277 correction = different_directions_correction (me, stem_posns, stem_dirs[LEFT]);
279 if (!bar_yextent.is_empty ())
283 else if (stem_dirs[LEFT] * stem_dirs[RIGHT] == 1)
284 correction = same_direction_correction (me, head_posns);
286 *space += correction;
288 /* there used to be a correction for bar_xextent () here, but
289 it's unclear what that was good for ?
293 ADD_INTERFACE (Note_spacing,
294 "This object calculates spacing wishes for individual voices.",
297 "knee-spacing-correction "
300 "same-direction-correction "
301 "stem-spacing-correction "