2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2001--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "note-spacing.hh"
22 #include "accidental-placement.hh"
23 #include "bar-line.hh"
24 #include "directional-element-interface.hh"
25 #include "grob-array.hh"
27 #include "note-column.hh"
28 #include "output-def.hh"
29 #include "paper-column.hh"
30 #include "pointer-group-interface.hh"
31 #include "separation-item.hh"
32 #include "spacing-interface.hh"
33 #include "staff-spacing.hh"
34 #include "staff-symbol-referencer.hh"
39 TODO: detect hshifts due to collisions, and account for them in
43 Adjust the ideal and minimum distance between note columns,
44 based on the notehead size, skylines, and optical illusions.
47 Note_spacing::get_spacing (Grob *me, Item *right_col,
48 Spring base, Real increment)
50 vector<Item *> note_columns = Spacing_interface::left_note_columns (me);
51 Real left_head_end = 0;
53 for (vsize i = 0; i < note_columns.size (); i++)
55 SCM r = note_columns[i]->get_object ("rest");
56 Grob *g = unsmob_grob (r);
57 Grob *col = note_columns[i]->get_column ();
60 g = Note_column::first_head (note_columns[i]);
63 Ugh. If Stem is switched off, we don't know what the
64 first note head will be.
68 if (g->common_refpoint (col, X_AXIS) != col)
69 programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect");
71 left_head_end = g->extent (col, X_AXIS)[RIGHT];
76 The main factor that determines the amount of space is the width of the
77 note head (or the rest). For example, a quarter rest gets almost 0.5 ss
78 less horizontal space than a note.
80 The other parts of a note column (eg. flags, accidentals, etc.) don't get
81 the full amount of space. We give them half the amount of space, but then
82 adjust things so there are no collisions.
84 Real ideal = base.distance () - increment + left_head_end;
85 Drul_array<Skyline> skys = Spacing_interface::skylines (me, right_col);
86 Real distance = skys[LEFT].distance (skys[RIGHT], robust_scm2double (right_col->get_property ("skyline-vertical-padding"), 0.0));
87 Real min_dist = max (0.0, distance);
88 Real min_desired_space = (ideal + min_dist) / 2;
89 base.set_min_distance (min_dist);
91 /* If we have a NonMusical column on the right, we measure the ideal distance
92 to the bar-line (if present), not the start of the column. */
93 if (!Paper_column::is_musical (right_col)
94 && !skys[RIGHT].is_empty ()
95 && to_boolean (me->get_property ("space-to-barline")))
97 Grob *bar = Pointer_group_interface::find_grob (right_col,
98 ly_symbol2scm ("elements"),
99 Bar_line::non_empty_barline);
103 Real shift = bar->extent (right_col, X_AXIS)[LEFT];
105 min_desired_space -= max (shift, 0.0);
108 ideal -= right_col->extent (right_col, X_AXIS)[RIGHT];
111 ideal = max (ideal, min_desired_space);
112 stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space);
114 base.set_distance (max (0.0, ideal));
115 base.set_inverse_compress_strength (max (0.0, ideal - min_desired_space));
120 knee_correction (Grob *note_spacing, Grob *right_stem, Real increment)
122 Real note_head_width = increment;
123 Grob *head = right_stem ? Stem::support_head (right_stem) : 0;
124 Grob *rcolumn = dynamic_cast<Item *> (head)->get_column ();
126 Interval head_extent;
129 head_extent = head->extent (rcolumn, X_AXIS);
131 if (!head_extent.is_empty ())
132 note_head_width = head_extent[RIGHT];
134 note_head_width -= Stem::thickness (right_stem);
137 return -note_head_width * get_grob_direction (right_stem)
138 * robust_scm2double (note_spacing->get_property ("knee-spacing-correction"), 0);
142 different_directions_correction (Grob *note_spacing,
143 Drul_array<Interval> stem_posns,
144 Direction left_stem_dir)
147 Interval intersect = stem_posns[LEFT];
148 intersect.intersect (stem_posns[RIGHT]);
150 if (!intersect.is_empty ())
152 ret = abs (intersect.length ());
157 ret = min (ret / 7, 1.0)
159 * robust_scm2double (note_spacing->get_property ("stem-spacing-correction"), 0);
165 same_direction_correction (Grob *note_spacing, Drul_array<Interval> head_posns)
168 Correct for the following situation:
177 ^ move the center one to the left.
180 this effect seems to be much more subtle than the
181 stem-direction stuff (why?), and also does not scale with the
182 difference in stem length.
186 Interval hp = head_posns[LEFT];
187 hp.intersect (head_posns[RIGHT]);
192 = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
194 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
195 Real corr = robust_scm2double (note_spacing->get_property ("same-direction-correction"), 0);
197 return (delta > 1) ? -lowest * corr : 0;
201 Correct for optical illusions. See [Wanske] p. 138. The combination
202 up-stem + down-stem should get extra space, the combination
203 down-stem + up-stem less.
205 TODO: have to check whether the stems are in the same staff.
208 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
210 Real *space, Real *fixed)
212 Drul_array<Direction> stem_dirs (CENTER, CENTER);
213 Drul_array<Interval> stem_posns;
214 Drul_array<Interval> head_posns;
215 Drul_array<SCM> props (me->get_object ("left-items"),
216 me->get_object ("right-items"));
218 Drul_array<Spanner *> beams_drul (0, 0);
219 Drul_array<Grob *> stems_drul (0, 0);
221 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
223 Interval bar_xextent;
224 Interval bar_yextent;
226 bool acc_right = false;
228 Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT,
229 rcolumn->break_status_dir (),
231 if (bar && dynamic_cast<Item *> (bar)->get_column () == rcolumn)
232 bar_yextent = Staff_spacing::bar_y_positions (bar);
234 for (LEFT_and_RIGHT (d))
236 vector<Grob *> const &items (ly_scm2link_array (props [d]));
237 for (vsize i = 0; i < items.size (); i++)
239 Item *it = dynamic_cast<Item *> (items[i]);
240 if (!Note_column::has_interface (it))
242 if (d == RIGHT && it->get_column () != rcolumn)
246 Find accidentals which are sticking out of the right side.
249 acc_right = acc_right || Note_column::accidentals (it);
251 Grob *stem = Note_column::get_stem (it);
253 if (!stem || !stem->is_live () || Stem::is_invisible (stem))
256 stems_drul[d] = stem;
257 beams_drul[d] = Stem::get_beam (stem);
259 Direction stem_dir = get_grob_direction (stem);
260 if (stem_dirs[d] && stem_dirs[d] != stem_dir)
263 stem_dirs[d] = stem_dir;
266 Correction doesn't seem appropriate when there is a large flag
267 hanging from the note.
270 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
273 Interval hp = Stem::head_positions (stem);
276 Real ss = Staff_symbol_referencer::staff_space (stem);
277 stem_posns[d] = stem->pure_height (stem, 0, INT_MAX) * (2 / ss);
278 head_posns[d].unite (hp);
283 Real correction = 0.0;
285 if (!bar_yextent.is_empty ())
287 stem_dirs[RIGHT] = -stem_dirs[LEFT];
288 stem_posns[RIGHT] = bar_yextent;
289 stem_posns[RIGHT] *= 2;
292 if (stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
294 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
296 correction = knee_correction (me, stems_drul[RIGHT], increment);
300 correction = different_directions_correction (me, stem_posns, stem_dirs[LEFT]);
302 if (!bar_yextent.is_empty ())
307 Only apply same direction correction if there are no
308 accidentals sticking out of the right hand side.
310 else if (stem_dirs[LEFT] * stem_dirs[RIGHT] == 1
312 correction = same_direction_correction (me, head_posns);
314 *fixed += correction;
315 *space += correction;
317 /* there used to be a correction for bar_xextent () here, but
318 it's unclear what that was good for ?
322 ADD_INTERFACE (Note_spacing,
323 "This object calculates spacing wishes for individual voices.",
326 "knee-spacing-correction "
329 "same-direction-correction "
330 "stem-spacing-correction "