Allow for nested contexts of any depth.
[lilypond.git] / lily / staff-spacing.cc
blob801fb96948e66fdc78c66750519d3f986928781f
1 /*
2 staff-spacing.cc -- implement Staff_spacing
4 source file of the GNU LilyPond music typesetter
6 (c) 2001--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "staff-spacing.hh"
11 #include <cstdio>
12 using namespace std;
14 #include "international.hh"
15 #include "paper-column.hh"
16 #include "separation-item.hh"
17 #include "warn.hh"
18 #include "bar-line.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "note-column.hh"
21 #include "stem.hh"
22 #include "spacing-interface.hh"
23 #include "accidental-placement.hh"
24 #include "pointer-group-interface.hh"
25 #include "directional-element-interface.hh"
27 /* A stem following a bar-line creates an optical illusion similar to the
28 one mentioned in note-spacing.cc. We correct for it here.
30 TODO: should we still correct if there are accidentals/arpeggios before
31 the stem?
34 Real
35 Staff_spacing::optical_correction (Grob *me, Grob *g, Interval bar_height)
37 if (!g || !Note_column::has_interface (g))
38 return 0;
40 Grob *stem = Note_column::get_stem (g);
41 Real ret = 0.0;
43 if (!bar_height.is_empty () && stem)
45 Direction d = get_grob_direction (stem);
46 if (Stem::is_normal_stem (stem) && d == DOWN)
50 can't look at stem-end-position, since that triggers
51 beam slope computations.
53 Real stem_start = Stem::head_positions (stem) [d];
54 Real stem_end = stem_start +
55 d * robust_scm2double (stem->get_property ("length"), 7);
57 Interval stem_posns (min (stem_start, stem_end),
58 max (stem_end, stem_start));
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);
66 return ret;
70 Y-positions that are covered by BAR_GROB, in the case that it is a
71 barline. */
72 Interval
73 Staff_spacing::bar_y_positions (Grob *bar_grob)
75 Interval bar_size;
76 bar_size.set_empty ();
77 if (Bar_line::has_interface (bar_grob))
79 SCM glyph = bar_grob->get_property ("glyph-name");
80 Grob *staff_sym = Staff_symbol_referencer::get_staff_symbol (bar_grob);
82 string glyph_string = scm_is_string (glyph) ? ly_scm2string (glyph) : "";
83 if (glyph_string.substr (0, 1) == "|"
84 || glyph_string.substr (0, 1) == ".")
86 Grob *common = bar_grob->common_refpoint (staff_sym, Y_AXIS);
87 bar_size = bar_grob->extent (common, Y_AXIS);
88 bar_size *= 1.0 / Staff_symbol_referencer::staff_space (bar_grob);
91 return bar_size;
94 Real
95 Staff_spacing::next_notes_correction (Grob *me,
96 Grob *last_grob)
98 Interval bar_size = bar_y_positions (last_grob);
99 Grob *orig = me->original () ? me->original () : me;
100 vector<Item*> note_columns = Spacing_interface::right_note_columns (orig);
102 Real max_optical = 0.0;
104 for (vsize i = 0; i < note_columns.size (); i++)
105 max_optical = max (max_optical, optical_correction (me, note_columns[i], bar_size));
107 return max_optical;
110 /* We calculate three things here: the ideal distance, the minimum distance
111 (which is the distance at which collisions will occur) and the "fixed"
112 distance, which is the distance at which things start to look really bad.
113 We arrange things so that the fixed distance will be attained when the
114 line is compressed with a force of 1.0 */
115 Spring
116 Staff_spacing::get_spacing (Grob *me, Grob *right_col)
118 Item *me_item = dynamic_cast<Item *> (me);
119 Grob *left_col = me_item->get_column ();
121 Interval last_ext;
122 Direction break_dir = me_item->break_status_dir ();
123 Grob *last_grob = Spacing_interface::extremal_break_aligned_grob (me, LEFT,
124 break_dir,
125 &last_ext);
126 if (!last_grob)
129 TODO:
131 Should insert a adjustable space here? For excercises, you might want to
132 use a staff without a clef in the beginning.
136 we used to have a warning here, but it generates a lot of
137 spurious error messages.
139 return Spring ();
142 SCM alist = last_grob->get_property ("space-alist");
143 if (!scm_list_p (alist))
144 return Spring ();
146 SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
147 if (me_item->break_status_dir () == CENTER)
149 SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
150 if (scm_is_pair (nndef))
151 space_def = nndef;
154 if (!scm_is_pair (space_def))
156 programming_error ("unknown prefatory spacing");
157 return Spring ();
160 space_def = scm_cdr (space_def);
161 Real distance = scm_to_double (scm_cdr (space_def));
162 SCM type = scm_car (space_def);
164 Real fixed = last_ext[RIGHT];
165 Real ideal = fixed + 1.0;
167 if (type == ly_symbol2scm ("fixed-space"))
169 fixed += distance;
170 ideal = fixed;
172 else if (type == ly_symbol2scm ("extra-space"))
173 ideal = fixed + distance;
174 else if (type == ly_symbol2scm ("semi-fixed-space"))
176 fixed += distance / 2;
177 ideal = fixed + distance / 2;
179 else if (type == ly_symbol2scm ("minimum-space"))
180 ideal = last_ext[LEFT] + max (last_ext.length (), distance);
181 else if (type == ly_symbol2scm ("minimum-fixed-space"))
183 fixed = last_ext[LEFT] + max (last_ext.length (), distance);
184 ideal = fixed;
188 Real optical_correction = next_notes_correction (me, last_grob);
189 Real min_dist = Paper_column::minimum_distance (left_col, right_col);
191 /* ensure that the "fixed" distance will leave a gap of at least 0.3 ss. */
192 Real min_dist_correction = max (0.0, 0.3 + min_dist - fixed);
193 Real correction = max (optical_correction, min_dist_correction);
195 fixed += correction;
196 ideal += correction;
198 Spring ret (ideal, min_dist);
199 ret.set_inverse_stretch_strength (max (0.0, ideal - fixed));
200 return ret;
203 ADD_INTERFACE (Staff_spacing,
204 "This object calculates spacing details from a breakable"
205 " symbol (left) to another object. For example, it takes care"
206 " of optical spacing from a bar line to a note.",
208 /* properties */
209 "stem-spacing-correction "