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"
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"
40 /* A stem following a bar-line creates an optical illusion similar to the
41 one mentioned in note-spacing.cc. We correct for it here.
43 TODO: should we still correct if there are accidentals/arpeggios before
48 Staff_spacing::optical_correction (Grob *me, Grob *g, Interval bar_height)
50 if (!g || !has_interface<Note_column> (g))
53 Grob *stem = Note_column::get_stem (g);
56 if (!bar_height.is_empty () && stem)
58 Direction d = get_grob_direction (stem);
59 if (Stem::is_normal_stem (stem) && d == DOWN)
61 Interval stem_posns = stem->pure_y_extent (stem, 0, INT_MAX);
63 stem_posns.intersect (bar_height);
65 ret = min (abs (stem_posns.length () / 7.0), 1.0);
66 ret *= robust_scm2double (me->get_property ("stem-spacing-correction"), 1);
73 Y-positions that are covered by BAR_GROB, in the case that it is a
76 Staff_spacing::bar_y_positions (Grob *bar_grob)
79 bar_size.set_empty ();
81 if (bar_grob->internal_has_interface (ly_symbol2scm ("bar-line-interface")))
83 SCM glyph = bar_grob->get_property ("glyph-name");
84 Grob *staff_sym = Staff_symbol_referencer::get_staff_symbol (bar_grob);
86 string glyph_string = scm_is_string (glyph) ? ly_scm2string (glyph) : "";
87 if (glyph_string.substr (0, 1) == "|"
88 || glyph_string.substr (0, 1) == ".")
90 Grob *common = bar_grob->common_refpoint (staff_sym, Y_AXIS);
91 bar_size = bar_grob->extent (common, Y_AXIS);
92 bar_size *= 1.0 / Staff_symbol_referencer::staff_space (bar_grob);
99 Staff_spacing::next_notes_correction (Grob *me,
102 Interval bar_size = bar_y_positions (last_grob);
103 Grob *orig = me->original () ? me->original () : me;
104 vector<Item *> note_columns = Spacing_interface::right_note_columns (orig);
106 Real max_optical = 0.0;
108 for (vsize i = 0; i < note_columns.size (); i++)
109 max_optical = max (max_optical, optical_correction (me, note_columns[i], bar_size));
114 /* We calculate three things here: the ideal distance, the minimum distance
115 (which is the distance at which collisions will occur) and the "fixed"
116 distance, which is the distance at which things start to look really bad.
117 We arrange things so that the fixed distance will be attained when the
118 line is compressed with a force of 1.0 */
120 Staff_spacing::get_spacing (Grob *me, Grob *right_col, Real situational_space)
122 Item *me_item = dynamic_cast<Item *> (me);
123 Grob *left_col = me_item->get_column ();
126 Direction break_dir = me_item->break_status_dir ();
127 Grob *last_grob = Spacing_interface::extremal_break_aligned_grob (me, LEFT,
135 Should insert an adjustable space here? For exercises, you might want to
136 use a staff without a clef in the beginning.
140 we used to have a warning here, but it generates a lot of
141 spurious error messages.
146 SCM alist = last_grob->get_property ("space-alist");
147 if (!ly_is_list (alist))
150 SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
151 if (me_item->break_status_dir () == CENTER)
153 SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
154 if (scm_is_pair (nndef))
158 if (!scm_is_pair (space_def))
160 programming_error ("unknown prefatory spacing");
164 space_def = scm_cdr (space_def);
165 Real distance = scm_to_double (scm_cdr (space_def));
166 SCM type = scm_car (space_def);
168 Real fixed = last_ext[RIGHT];
169 Real ideal = fixed + 1.0;
171 if (scm_is_eq (type, ly_symbol2scm ("fixed-space")))
176 else if (scm_is_eq (type, ly_symbol2scm ("extra-space")))
177 ideal = fixed + distance;
178 else if (scm_is_eq (type, ly_symbol2scm ("semi-fixed-space")))
180 fixed += distance / 2;
181 ideal = fixed + distance / 2;
183 else if (scm_is_eq (type, ly_symbol2scm ("minimum-space")))
184 ideal = last_ext[LEFT] + max (last_ext.length (), distance);
185 else if (scm_is_eq (type, ly_symbol2scm ("minimum-fixed-space")))
187 fixed = last_ext[LEFT] + max (last_ext.length (), distance);
191 Real stretchability = ideal - fixed;
193 /* 'situational_space' passed by the caller
194 could include full-measure-extra-space */
195 ideal += situational_space;
197 Real optical_correction = next_notes_correction (me, last_grob);
198 fixed += optical_correction;
199 ideal += optical_correction;
201 Real min_dist = Paper_column::minimum_distance (left_col, right_col);
203 /* ensure that the "fixed" distance will leave a gap of at least 0.3 ss. */
204 Real min_dist_correction = max (0.0, 0.3 + min_dist - fixed);
205 fixed += min_dist_correction;
206 ideal = max (ideal, fixed);
208 Spring ret (ideal, min_dist);
209 ret.set_inverse_stretch_strength (max (0.0, stretchability));
210 ret.set_inverse_compress_strength (max (0.0, ideal - fixed));
214 ADD_INTERFACE (Staff_spacing,
215 "This object calculates spacing details from a breakable"
216 " symbol (left) to another object. For example, it takes care"
217 " of optical spacing from a bar line to a note.",
220 "stem-spacing-correction "