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;
121 vector<Grob*> const &items (ly_scm2link_array (props [d]));
122 for (vsize i = 0; i < items.size (); i++)
124 Item *it = dynamic_cast<Item *> (items[i]);
127 acc_right = acc_right || Note_column::accidentals (it);
129 Grob *stem = Note_column::get_stem (it);
131 if (!stem || !stem->is_live ())
133 if (d == RIGHT && Separation_item::has_interface (it))
135 if (it->get_column () != rcolumn)
136 it = it->find_prebroken_piece (rcolumn->break_status_dir ());
138 Grob *last = Separation_item::extremal_break_aligned_grob (it, LEFT, &bar_xextent);
141 bar_yextent = Staff_spacing::bar_y_positions (last);
149 if (Stem::is_invisible (stem))
151 correct_stem_dirs = false;
155 stems_drul[d] = stem;
156 beams_drul[d] = Stem::get_beam (stem);
158 Direction stem_dir = get_grob_direction (stem);
159 if (stem_dirs[d] && stem_dirs[d] != stem_dir)
161 correct_stem_dirs = false;
164 stem_dirs[d] = stem_dir;
167 Correction doesn't seem appropriate when there is a large flag
168 hanging from the note.
171 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
172 correct_stem_dirs = false;
174 Interval hp = Stem::head_positions (stem);
175 if (correct_stem_dirs
178 Real chord_start = hp[stem_dir];
181 can't look at stem-end-position, since that triggers
182 beam slope computations.
184 Real stem_end = hp[stem_dir] +
185 stem_dir * robust_scm2double (stem->get_property ("length"), 7);
187 stem_posns[d] = Interval (min (chord_start, stem_end),
188 max (chord_start, stem_end));
189 head_posns[d].unite (hp);
193 while (flip (&d) != LEFT);
196 don't correct if accidentals are sticking out of the right side.
201 Real correction = 0.0;
203 if (!bar_yextent.is_empty ())
205 stem_dirs[RIGHT] = -stem_dirs[LEFT];
206 stem_posns[RIGHT] = bar_yextent;
207 stem_posns[RIGHT] *= 2;
210 if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
212 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
216 this is a knee: maximal correction.
218 Real note_head_width = increment;
219 Grob *st = stems_drul[RIGHT];
220 Grob *head = st ? Stem::support_head (st) : 0;
222 Interval head_extent;
225 head_extent = head->extent (rcolumn, X_AXIS);
227 if (!head_extent.is_empty ())
228 note_head_width = head_extent[RIGHT];
230 note_head_width -= Stem::thickness (st);
233 correction = note_head_width * stem_dirs[LEFT];
234 correction *= robust_scm2double (me->get_property ("knee-spacing-correction"), 0);
235 *fixed += correction;
239 intersect = stem_posns[LEFT];
240 intersect.intersect (stem_posns[RIGHT]);
241 correct_stem_dirs = correct_stem_dirs && !intersect.is_empty ();
243 if (correct_stem_dirs)
245 correction = abs (intersect.length ());
250 correction = min (correction / 7, 1.0);
251 correction *= stem_dirs[LEFT];
253 *= robust_scm2double (me->get_property ("stem-spacing-correction"), 0);
256 if (!bar_yextent.is_empty ())
260 else if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == UP)
263 Correct for the following situation:
272 ^ move the center one to the left.
275 this effect seems to be much more subtle than the
276 stem-direction stuff (why?), and also does not scale with the
277 difference in stem length.
281 Interval hp = head_posns[LEFT];
282 hp.intersect (head_posns[RIGHT]);
287 = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
289 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
290 Real corr = robust_scm2double (me->get_property ("same-direction-correction"), 0);
293 correction = -lowest * corr;
296 *space += correction;
298 /* there used to be a correction for bar_xextent () here, but
299 it's unclear what that was good for ?
303 ADD_INTERFACE (Note_spacing,
304 "This object calculates spacing wishes for individual voices.",
307 "knee-spacing-correction "
310 "same-direction-correction "
311 "stem-spacing-correction "