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
44 Note_spacing::get_spacing (Grob *me, Item *right_col,
45 Real base_space, Real increment)
47 vector<Item *> note_columns = Spacing_interface::left_note_columns (me);
48 Real left_head_end = 0;
50 for (vsize i = 0; i < note_columns.size (); i++)
52 SCM r = note_columns[i]->get_object ("rest");
53 Grob *g = unsmob_grob (r);
54 Grob *col = note_columns[i]->get_column ();
57 g = Note_column::first_head (note_columns[i]);
60 Ugh. If Stem is switched off, we don't know what the
61 first note head will be.
65 if (g->common_refpoint (col, X_AXIS) != col)
66 programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect");
68 left_head_end = g->extent (col, X_AXIS)[RIGHT];
73 The main factor that determines the amount of space is the width of the
74 note head (or the rest). For example, a quarter rest gets almost 0.5 ss
75 less horizontal space than a note.
77 The other parts of a note column (eg. flags, accidentals, etc.) don't get
78 the full amount of space. We give them half the amount of space, but then
79 adjust things so there are no collisions.
81 Drul_array<Skyline> skys = Spacing_interface::skylines (me, right_col);
82 Real distance = skys[LEFT].distance (skys[RIGHT], robust_scm2double (right_col->get_property ("skyline-vertical-padding"), 0.0));
83 Real min_dist = max (0.0, distance);
84 Real min_desired_space = left_head_end + (min_dist - left_head_end + base_space - increment) / 2;
85 Real ideal = base_space - increment + left_head_end;
87 /* If we have a NonMusical column on the right, we measure the ideal distance
88 to the bar-line (if present), not the start of the column. */
89 if (!Paper_column::is_musical (right_col)
90 && !skys[RIGHT].is_empty ()
91 && to_boolean (me->get_property ("space-to-barline")))
93 Grob *bar = Pointer_group_interface::find_grob (right_col,
94 ly_symbol2scm ("elements"),
95 Bar_line::non_empty_barline);
99 Real shift = bar->extent (right_col, X_AXIS)[LEFT];
101 min_desired_space -= max (shift, 0.0);
104 ideal -= right_col->extent (right_col, X_AXIS)[RIGHT];
107 ideal = max (ideal, min_desired_space);
108 stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space);
110 /* TODO: grace notes look bad when things are stretched. Should we increase
111 their stretch strength? */
112 Spring ret (max (0.0, ideal), min_dist);
113 ret.set_inverse_compress_strength (max (0.0, ideal - min_desired_space));
114 ret.set_inverse_stretch_strength (max (0.1, base_space - increment));
119 knee_correction (Grob *note_spacing, Grob *right_stem, Real increment)
121 Real note_head_width = increment;
122 Grob *head = right_stem ? Stem::support_head (right_stem) : 0;
123 Grob *rcolumn = dynamic_cast<Item *> (head)->get_column ();
125 Interval head_extent;
128 head_extent = head->extent (rcolumn, X_AXIS);
130 if (!head_extent.is_empty ())
131 note_head_width = head_extent[RIGHT];
133 note_head_width -= Stem::thickness (right_stem);
136 return -note_head_width * get_grob_direction (right_stem)
137 * robust_scm2double (note_spacing->get_property ("knee-spacing-correction"), 0);
141 different_directions_correction (Grob *note_spacing,
142 Drul_array<Interval> stem_posns,
143 Direction left_stem_dir)
146 Interval intersect = stem_posns[LEFT];
147 intersect.intersect (stem_posns[RIGHT]);
149 if (!intersect.is_empty ())
151 ret = abs (intersect.length ());
156 ret = min (ret / 7, 1.0)
158 * robust_scm2double (note_spacing->get_property ("stem-spacing-correction"), 0);
164 same_direction_correction (Grob *note_spacing, Drul_array<Interval> head_posns)
167 Correct for the following situation:
176 ^ move the center one to the left.
179 this effect seems to be much more subtle than the
180 stem-direction stuff (why?), and also does not scale with the
181 difference in stem length.
185 Interval hp = head_posns[LEFT];
186 hp.intersect (head_posns[RIGHT]);
191 = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
193 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
194 Real corr = robust_scm2double (note_spacing->get_property ("same-direction-correction"), 0);
196 return (delta > 1) ? -lowest * corr : 0;
200 Correct for optical illusions. See [Wanske] p. 138. The combination
201 up-stem + down-stem should get extra space, the combination
202 down-stem + up-stem less.
204 TODO: have to check whether the stems are in the same staff.
207 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
209 Real *space, Real *fixed)
211 Drul_array<Direction> stem_dirs (CENTER, CENTER);
212 Drul_array<Interval> stem_posns;
213 Drul_array<Interval> head_posns;
214 Drul_array<SCM> props (me->get_object ("left-items"),
215 me->get_object ("right-items"));
217 Drul_array<Spanner *> beams_drul (0, 0);
218 Drul_array<Grob *> stems_drul (0, 0);
220 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
222 Interval bar_xextent;
223 Interval bar_yextent;
225 bool acc_right = false;
227 Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT,
228 rcolumn->break_status_dir (),
230 if (bar && dynamic_cast<Item *> (bar)->get_column () == rcolumn)
231 bar_yextent = Staff_spacing::bar_y_positions (bar);
233 for (LEFT_and_RIGHT (d))
235 vector<Grob *> const &items (ly_scm2link_array (props [d]));
236 for (vsize i = 0; i < items.size (); i++)
238 Item *it = dynamic_cast<Item *> (items[i]);
239 if (!Note_column::has_interface (it))
241 if (d == RIGHT && it->get_column () != rcolumn)
245 Find accidentals which are sticking out of the right side.
248 acc_right = acc_right || Note_column::accidentals (it);
250 Grob *stem = Note_column::get_stem (it);
252 if (!stem || !stem->is_live () || Stem::is_invisible (stem))
255 stems_drul[d] = stem;
256 beams_drul[d] = Stem::get_beam (stem);
258 Direction stem_dir = get_grob_direction (stem);
259 if (stem_dirs[d] && stem_dirs[d] != stem_dir)
262 stem_dirs[d] = stem_dir;
265 Correction doesn't seem appropriate when there is a large flag
266 hanging from the note.
269 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
272 Interval hp = Stem::head_positions (stem);
275 Real ss = Staff_symbol_referencer::staff_space (stem);
276 stem_posns[d] = stem->pure_height (stem, 0, INT_MAX) * (2 / ss);
277 head_posns[d].unite (hp);
282 Real correction = 0.0;
284 if (!bar_yextent.is_empty ())
286 stem_dirs[RIGHT] = -stem_dirs[LEFT];
287 stem_posns[RIGHT] = bar_yextent;
288 stem_posns[RIGHT] *= 2;
291 if (stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
293 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
295 correction = knee_correction (me, stems_drul[RIGHT], increment);
299 correction = different_directions_correction (me, stem_posns, stem_dirs[LEFT]);
301 if (!bar_yextent.is_empty ())
306 Only apply same direction correction if there are no
307 accidentals sticking out of the right hand side.
309 else if (stem_dirs[LEFT] * stem_dirs[RIGHT] == 1
311 correction = same_direction_correction (me, head_posns);
313 *fixed += correction;
314 *space += correction;
316 /* there used to be a correction for bar_xextent () here, but
317 it's unclear what that was good for ?
321 ADD_INTERFACE (Note_spacing,
322 "This object calculates spacing wishes for individual voices.",
325 "knee-spacing-correction "
328 "same-direction-correction "
329 "stem-spacing-correction "