2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2001--2015 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"
24 #include "international.hh"
25 #include "paper-column.hh"
26 #include "separation-item.hh"
28 #include "staff-symbol-referencer.hh"
29 #include "note-column.hh"
31 #include "spacing-interface.hh"
32 #include "accidental-placement.hh"
33 #include "pointer-group-interface.hh"
34 #include "directional-element-interface.hh"
39 /* A stem following a bar-line creates an optical illusion similar to the
40 one mentioned in note-spacing.cc. We correct for it here.
42 TODO: should we still correct if there are accidentals/arpeggios before
47 Staff_spacing::optical_correction (Grob *me, Grob *g, Interval bar_height)
49 if (!g || !has_interface<Note_column> (g))
52 Grob *stem = Note_column::get_stem (g);
55 if (!bar_height.is_empty () && stem)
57 Direction d = get_grob_direction (stem);
58 if (Stem::is_normal_stem (stem) && d == DOWN)
60 Interval stem_posns = stem->pure_y_extent (stem, 0, INT_MAX);
62 stem_posns.intersect (bar_height);
64 ret = std::min (abs (stem_posns.length () / 7.0), 1.0);
65 ret *= robust_scm2double (me->get_property ("stem-spacing-correction"), 1);
72 Y-positions that are covered by BAR_GROB, in the case that it is a
75 Staff_spacing::bar_y_positions (Grob *bar_grob)
78 bar_size.set_empty ();
80 if (bar_grob->internal_has_interface (ly_symbol2scm ("bar-line-interface")))
82 SCM glyph = bar_grob->get_property ("glyph-name");
83 Grob *staff_sym = Staff_symbol_referencer::get_staff_symbol (bar_grob);
85 string glyph_string = scm_is_string (glyph) ? ly_scm2string (glyph) : "";
86 if (glyph_string.substr (0, 1) == "|"
87 || glyph_string.substr (0, 1) == ".")
89 Grob *common = bar_grob->common_refpoint (staff_sym, Y_AXIS);
90 bar_size = bar_grob->extent (common, Y_AXIS);
91 bar_size *= 1.0 / Staff_symbol_referencer::staff_space (bar_grob);
98 Staff_spacing::next_notes_correction (Grob *me,
101 Interval bar_size = bar_y_positions (last_grob);
102 Grob *orig = me->original () ? me->original () : me;
103 vector<Item *> note_columns = Spacing_interface::right_note_columns (orig);
105 Real max_optical = 0.0;
107 for (vsize i = 0; i < note_columns.size (); i++)
108 max_optical = std::max (max_optical, optical_correction (me, note_columns[i], bar_size));
113 /* We calculate three things here: the ideal distance, the minimum distance
114 (which is the distance at which collisions will occur) and the "fixed"
115 distance, which is the distance at which things start to look really bad.
116 We arrange things so that the fixed distance will be attained when the
117 line is compressed with a force of 1.0 */
119 Staff_spacing::get_spacing (Grob *me, Grob *right_col, Real situational_space)
121 Item *me_item = dynamic_cast<Item *> (me);
122 Grob *left_col = me_item->get_column ();
125 Direction break_dir = me_item->break_status_dir ();
126 Grob *last_grob = Spacing_interface::extremal_break_aligned_grob (me, LEFT,
134 Should insert an adjustable space here? For exercises, you might want to
135 use a staff without a clef in the beginning.
139 we used to have a warning here, but it generates a lot of
140 spurious error messages.
145 SCM alist = last_grob->get_property ("space-alist");
146 if (!ly_is_list (alist))
149 SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
150 if (me_item->break_status_dir () == CENTER)
152 SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
153 if (scm_is_pair (nndef))
157 if (!scm_is_pair (space_def))
159 programming_error ("unknown prefatory spacing");
163 space_def = scm_cdr (space_def);
164 Real distance = scm_to_double (scm_cdr (space_def));
165 SCM type = scm_car (space_def);
167 Real fixed = last_ext[RIGHT];
168 Real ideal = fixed + 1.0;
170 if (scm_is_eq (type, ly_symbol2scm ("fixed-space")))
175 else if (scm_is_eq (type, ly_symbol2scm ("extra-space")))
176 ideal = fixed + distance;
177 else if (scm_is_eq (type, ly_symbol2scm ("semi-fixed-space")))
179 fixed += distance / 2;
180 ideal = fixed + distance / 2;
182 else if (scm_is_eq (type, ly_symbol2scm ("minimum-space")))
183 ideal = last_ext[LEFT] + std::max (last_ext.length (), distance);
184 else if (scm_is_eq (type, ly_symbol2scm ("minimum-fixed-space")))
186 fixed = last_ext[LEFT] + std::max (last_ext.length (), distance);
190 Real stretchability = ideal - fixed;
192 /* 'situational_space' passed by the caller
193 could include full-measure-extra-space */
194 ideal += situational_space;
196 Real optical_correction = next_notes_correction (me, last_grob);
197 fixed += optical_correction;
198 ideal += optical_correction;
200 Real min_dist = Paper_column::minimum_distance (left_col, right_col);
202 /* ensure that the "fixed" distance will leave a gap of at least 0.3 ss. */
203 Real min_dist_correction = std::max (0.0, 0.3 + min_dist - fixed);
204 fixed += min_dist_correction;
205 ideal = std::max (ideal, fixed);
207 Spring ret (ideal, min_dist);
208 ret.set_inverse_stretch_strength (std::max (0.0, stretchability));
209 ret.set_inverse_compress_strength (std::max (0.0, ideal - fixed));
213 ADD_INTERFACE (Staff_spacing,
214 "This object calculates spacing details from a breakable"
215 " symbol (left) to another object. For example, it takes care"
216 " of optical spacing from a bar line to a note.",
219 "stem-spacing-correction "