2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2001--2014 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 "staff-spacing.hh"
25 #include "international.hh"
26 #include "paper-column.hh"
27 #include "separation-item.hh"
29 #include "staff-symbol-referencer.hh"
30 #include "note-column.hh"
32 #include "spacing-interface.hh"
33 #include "accidental-placement.hh"
34 #include "pointer-group-interface.hh"
35 #include "directional-element-interface.hh"
37 /* A stem following a bar-line creates an optical illusion similar to the
38 one mentioned in note-spacing.cc. We correct for it here.
40 TODO: should we still correct if there are accidentals/arpeggios before
45 Staff_spacing::optical_correction (Grob *me, Grob *g, Interval bar_height)
47 if (!g || !Note_column::has_interface (g))
50 Grob *stem = Note_column::get_stem (g);
53 if (!bar_height.is_empty () && stem)
55 Direction d = get_grob_direction (stem);
56 if (Stem::is_normal_stem (stem) && d == DOWN)
58 Interval stem_posns = stem->pure_height (stem, 0, INT_MAX);
60 stem_posns.intersect (bar_height);
62 ret = min (abs (stem_posns.length () / 7.0), 1.0);
63 ret *= robust_scm2double (me->get_property ("stem-spacing-correction"), 1);
70 Y-positions that are covered by BAR_GROB, in the case that it is a
73 Staff_spacing::bar_y_positions (Grob *bar_grob)
76 bar_size.set_empty ();
78 if (bar_grob->internal_has_interface (ly_symbol2scm ("bar-line-interface")))
80 SCM glyph = bar_grob->get_property ("glyph-name");
81 Grob *staff_sym = Staff_symbol_referencer::get_staff_symbol (bar_grob);
83 string glyph_string = scm_is_string (glyph) ? ly_scm2string (glyph) : "";
84 if (glyph_string.substr (0, 1) == "|"
85 || glyph_string.substr (0, 1) == ".")
87 Grob *common = bar_grob->common_refpoint (staff_sym, Y_AXIS);
88 bar_size = bar_grob->extent (common, Y_AXIS);
89 bar_size *= 1.0 / Staff_symbol_referencer::staff_space (bar_grob);
96 Staff_spacing::next_notes_correction (Grob *me,
99 Interval bar_size = bar_y_positions (last_grob);
100 Grob *orig = me->original () ? me->original () : me;
101 vector<Item *> note_columns = Spacing_interface::right_note_columns (orig);
103 Real max_optical = 0.0;
105 for (vsize i = 0; i < note_columns.size (); i++)
106 max_optical = max (max_optical, optical_correction (me, note_columns[i], bar_size));
111 /* We calculate three things here: the ideal distance, the minimum distance
112 (which is the distance at which collisions will occur) and the "fixed"
113 distance, which is the distance at which things start to look really bad.
114 We arrange things so that the fixed distance will be attained when the
115 line is compressed with a force of 1.0 */
117 Staff_spacing::get_spacing (Grob *me, Grob *right_col, Real situational_space)
119 Item *me_item = dynamic_cast<Item *> (me);
120 Grob *left_col = me_item->get_column ();
123 Direction break_dir = me_item->break_status_dir ();
124 Grob *last_grob = Spacing_interface::extremal_break_aligned_grob (me, LEFT,
132 Should insert an adjustable space here? For exercises, you might want to
133 use a staff without a clef in the beginning.
137 we used to have a warning here, but it generates a lot of
138 spurious error messages.
143 SCM alist = last_grob->get_property ("space-alist");
144 if (!ly_is_list (alist))
147 SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
148 if (me_item->break_status_dir () == CENTER)
150 SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
151 if (scm_is_pair (nndef))
155 if (!scm_is_pair (space_def))
157 programming_error ("unknown prefatory spacing");
161 space_def = scm_cdr (space_def);
162 Real distance = scm_to_double (scm_cdr (space_def));
163 SCM type = scm_car (space_def);
165 Real fixed = last_ext[RIGHT];
166 Real ideal = fixed + 1.0;
168 if (type == ly_symbol2scm ("fixed-space"))
173 else if (type == ly_symbol2scm ("extra-space"))
174 ideal = fixed + distance;
175 else if (type == ly_symbol2scm ("semi-fixed-space"))
177 fixed += distance / 2;
178 ideal = fixed + distance / 2;
180 else if (type == ly_symbol2scm ("minimum-space"))
181 ideal = last_ext[LEFT] + max (last_ext.length (), distance);
182 else if (type == ly_symbol2scm ("minimum-fixed-space"))
184 fixed = last_ext[LEFT] + max (last_ext.length (), distance);
188 Real stretchability = ideal - fixed;
190 /* 'situational_space' passed by the caller
191 could include full-measure-extra-space */
192 ideal += situational_space;
194 Real optical_correction = next_notes_correction (me, last_grob);
195 fixed += optical_correction;
196 ideal += optical_correction;
198 Real min_dist = Paper_column::minimum_distance (left_col, right_col);
200 /* ensure that the "fixed" distance will leave a gap of at least 0.3 ss. */
201 Real min_dist_correction = max (0.0, 0.3 + min_dist - fixed);
202 fixed += min_dist_correction;
203 ideal = max (ideal, fixed);
205 Spring ret (ideal, min_dist);
206 ret.set_inverse_stretch_strength (max (0.0, stretchability));
207 ret.set_inverse_compress_strength (max (0.0, ideal - fixed));
211 ADD_INTERFACE (Staff_spacing,
212 "This object calculates spacing details from a breakable"
213 " symbol (left) to another object. For example, it takes care"
214 " of optical spacing from a bar line to a note.",
217 "stem-spacing-correction "