2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2001--2009 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 "bar-line.hh"
23 #include "directional-element-interface.hh"
24 #include "grob-array.hh"
25 #include "paper-column.hh"
27 #include "note-column.hh"
30 #include "separation-item.hh"
31 #include "spacing-interface.hh"
32 #include "staff-spacing.hh"
33 #include "accidental-placement.hh"
34 #include "output-def.hh"
35 #include "pointer-group-interface.hh"
38 non_empty_barline (Grob *me)
40 return Bar_line::has_interface (me) && !me->extent (me, X_AXIS).is_empty ();
44 TODO: detect hshifts due to collisions, and account for them in
49 Note_spacing::get_spacing (Grob *me, Item *right_col,
50 Real base_space, Real increment)
52 vector<Item*> note_columns = Spacing_interface::left_note_columns (me);
53 Real left_head_end = 0;
55 for (vsize i = 0; i < note_columns.size (); i++)
57 SCM r = note_columns[i]->get_object ("rest");
58 Grob *g = unsmob_grob (r);
59 Grob *col = note_columns[i]->get_column ();
62 g = Note_column::first_head (note_columns[i]);
65 Ugh. If Stem is switched off, we don't know what the
66 first note head will be.
70 if (g->common_refpoint (col, X_AXIS) != col)
71 programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect");
73 left_head_end = g->extent (col, X_AXIS)[RIGHT];
78 The main factor that determines the amount of space is the width of the
79 note head (or the rest). For example, a quarter rest gets almost 0.5 ss
80 less horizontal space than a note.
82 The other parts of a note column (eg. flags, accidentals, etc.) don't get
83 the full amount of space. We give them half the amount of space, but then
84 adjust things so there are no collisions.
86 Drul_array<Skyline> skys = Spacing_interface::skylines (me, right_col);
87 Real distance = skys[LEFT].distance (skys[RIGHT]);
88 Real min_dist = max (0.0, distance);
89 Real min_desired_space = left_head_end + (min_dist - left_head_end + base_space - increment) / 2;
90 Real ideal = base_space - increment + left_head_end;
92 /* If we have a NonMusical column on the right, we measure the ideal distance
93 to the bar-line (if present), not the start of the column. */
94 if (!Paper_column::is_musical (right_col)
95 && !skys[RIGHT].is_empty ()
96 && to_boolean (me->get_property ("space-to-barline")))
98 Grob *bar = Pointer_group_interface::find_grob (right_col,
99 ly_symbol2scm ("elements"),
104 Real shift = bar->extent (right_col, X_AXIS)[LEFT];
106 min_desired_space -= max (shift, 0.0);
109 ideal -= right_col->extent (right_col, X_AXIS)[RIGHT];
112 ideal = max (ideal, min_desired_space);
113 stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space);
115 /* TODO: grace notes look bad when things are stretched. Should we increase
116 their stretch strength? */
117 Spring ret (max (0.0, ideal), min_dist);
118 ret.set_inverse_compress_strength (max (0.0, ideal - min_desired_space));
119 ret.set_inverse_stretch_strength (max (0.1, base_space - increment));
124 knee_correction (Grob *note_spacing, Grob *right_stem, Real increment)
126 Real note_head_width = increment;
127 Grob *head = right_stem ? Stem::support_head (right_stem) : 0;
128 Grob *rcolumn = dynamic_cast<Item*> (head)->get_column ();
130 Interval head_extent;
133 head_extent = head->extent (rcolumn, X_AXIS);
135 if (!head_extent.is_empty ())
136 note_head_width = head_extent[RIGHT];
138 note_head_width -= Stem::thickness (right_stem);
141 return -note_head_width * get_grob_direction (right_stem)
142 * robust_scm2double (note_spacing->get_property ("knee-spacing-correction"), 0);
146 different_directions_correction (Grob *note_spacing,
147 Drul_array<Interval> stem_posns,
148 Direction left_stem_dir)
151 Interval intersect = stem_posns[LEFT];
152 intersect.intersect (stem_posns[RIGHT]);
154 if (!intersect.is_empty ())
156 ret = abs (intersect.length ());
161 ret = min (ret / 7, 1.0)
163 * robust_scm2double (note_spacing->get_property ("stem-spacing-correction"), 0);
169 same_direction_correction (Grob *note_spacing, Drul_array<Interval> head_posns)
172 Correct for the following situation:
181 ^ move the center one to the left.
184 this effect seems to be much more subtle than the
185 stem-direction stuff (why?), and also does not scale with the
186 difference in stem length.
190 Interval hp = head_posns[LEFT];
191 hp.intersect (head_posns[RIGHT]);
196 = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
198 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
199 Real corr = robust_scm2double (note_spacing->get_property ("same-direction-correction"), 0);
201 return (delta > 1) ? -lowest * corr : 0;
206 Correct for optical illusions. See [Wanske] p. 138. The combination
207 up-stem + down-stem should get extra space, the combination
208 down-stem + up-stem less.
210 TODO: have to check whether the stems are in the same staff.
213 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
215 Real *space, Real *fixed)
217 Drul_array<Direction> stem_dirs (CENTER, CENTER);
218 Drul_array<Interval> stem_posns;
219 Drul_array<Interval> head_posns;
220 Drul_array<SCM> props (me->get_object ("left-items"),
221 me->get_object ("right-items"));
223 Drul_array<Spanner *> beams_drul (0, 0);
224 Drul_array<Grob *> stems_drul (0, 0);
226 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
228 Interval bar_xextent;
229 Interval bar_yextent;
233 bool acc_right = false;
235 Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT,
236 rcolumn->break_status_dir (),
238 if (bar && dynamic_cast<Item*> (bar)->get_column () == rcolumn)
239 bar_yextent = Staff_spacing::bar_y_positions (bar);
243 vector<Grob*> const &items (ly_scm2link_array (props [d]));
244 for (vsize i = 0; i < items.size (); i++)
246 Item *it = dynamic_cast<Item *> (items[i]);
247 if (!Note_column::has_interface (it))
249 if (d == RIGHT && it->get_column () != rcolumn)
253 Find accidentals which are sticking out of the right side.
256 acc_right = acc_right || Note_column::accidentals (it);
258 Grob *stem = Note_column::get_stem (it);
260 if (!stem || !stem->is_live () || Stem::is_invisible (stem))
263 stems_drul[d] = stem;
264 beams_drul[d] = Stem::get_beam (stem);
266 Direction stem_dir = get_grob_direction (stem);
267 if (stem_dirs[d] && stem_dirs[d] != stem_dir)
270 stem_dirs[d] = stem_dir;
273 Correction doesn't seem appropriate when there is a large flag
274 hanging from the note.
277 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
280 Interval hp = Stem::head_positions (stem);
283 Real chord_start = hp[stem_dir];
286 can't look at stem-end-position, since that triggers
287 beam slope computations.
289 Real stem_end = hp[stem_dir] +
290 stem_dir * robust_scm2double (stem->get_property ("length"), 7);
292 stem_posns[d] = Interval (min (chord_start, stem_end),
293 max (chord_start, stem_end));
294 head_posns[d].unite (hp);
298 while (flip (&d) != LEFT);
300 Real correction = 0.0;
302 if (!bar_yextent.is_empty ())
304 stem_dirs[RIGHT] = -stem_dirs[LEFT];
305 stem_posns[RIGHT] = bar_yextent;
306 stem_posns[RIGHT] *= 2;
309 if (stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
311 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
313 correction = knee_correction (me, stems_drul[RIGHT], increment);
314 *fixed += correction;
318 correction = different_directions_correction (me, stem_posns, stem_dirs[LEFT]);
320 if (!bar_yextent.is_empty ())
325 Only apply same direction correction if there are no
326 accidentals sticking out of the right hand side.
328 else if (stem_dirs[LEFT] * stem_dirs[RIGHT] == 1
330 correction = same_direction_correction (me, head_posns);
332 *space += correction;
334 /* there used to be a correction for bar_xextent () here, but
335 it's unclear what that was good for ?
339 ADD_INTERFACE (Note_spacing,
340 "This object calculates spacing wishes for individual voices.",
343 "knee-spacing-correction "
346 "same-direction-correction "
347 "stem-spacing-correction "