]> git.donarmstrong.com Git - lilypond.git/blob - lily/staff-spacing.cc
Revert "Issue 4550 (2/2) Avoid "using namespace std;" in included files"
[lilypond.git] / lily / staff-spacing.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2001--2015  Han-Wen Nienhuys <hanwen@xs4all.nl>
5
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.
10
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.
15
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/>.
18 */
19
20 #include "staff-spacing.hh"
21
22 #include <cstdio>
23 using namespace std;
24
25 #include "international.hh"
26 #include "paper-column.hh"
27 #include "separation-item.hh"
28 #include "warn.hh"
29 #include "staff-symbol-referencer.hh"
30 #include "note-column.hh"
31 #include "stem.hh"
32 #include "spacing-interface.hh"
33 #include "accidental-placement.hh"
34 #include "pointer-group-interface.hh"
35 #include "directional-element-interface.hh"
36
37 using std::string;
38 using std::vector;
39
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.
42
43    TODO: should we still correct if there are accidentals/arpeggios before
44    the stem?
45 */
46
47 Real
48 Staff_spacing::optical_correction (Grob *me, Grob *g, Interval bar_height)
49 {
50   if (!g || !has_interface<Note_column> (g))
51     return 0;
52
53   Grob *stem = Note_column::get_stem (g);
54   Real ret = 0.0;
55
56   if (!bar_height.is_empty () && stem)
57     {
58       Direction d = get_grob_direction (stem);
59       if (Stem::is_normal_stem (stem) && d == DOWN)
60         {
61           Interval stem_posns = stem->pure_y_extent (stem, 0, INT_MAX);
62
63           stem_posns.intersect (bar_height);
64
65           ret = min (abs (stem_posns.length () / 7.0), 1.0);
66           ret *= robust_scm2double (me->get_property ("stem-spacing-correction"), 1);
67         }
68     }
69   return ret;
70 }
71
72 /*
73   Y-positions that are covered by BAR_GROB, in the case that it is a
74   barline.  */
75 Interval
76 Staff_spacing::bar_y_positions (Grob *bar_grob)
77 {
78   Interval bar_size;
79   bar_size.set_empty ();
80
81   if (bar_grob->internal_has_interface (ly_symbol2scm ("bar-line-interface")))
82     {
83       SCM glyph = bar_grob->get_property ("glyph-name");
84       Grob *staff_sym = Staff_symbol_referencer::get_staff_symbol (bar_grob);
85
86       string glyph_string = scm_is_string (glyph) ? ly_scm2string (glyph) : "";
87       if (glyph_string.substr (0, 1) == "|"
88           || glyph_string.substr (0, 1) == ".")
89         {
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);
93         }
94     }
95   return bar_size;
96 }
97
98 Real
99 Staff_spacing::next_notes_correction (Grob *me,
100                                       Grob *last_grob)
101 {
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);
105
106   Real max_optical = 0.0;
107
108   for (vsize i = 0; i < note_columns.size (); i++)
109     max_optical = max (max_optical, optical_correction (me, note_columns[i], bar_size));
110
111   return max_optical;
112 }
113
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 */
119 Spring
120 Staff_spacing::get_spacing (Grob *me, Grob *right_col, Real situational_space)
121 {
122   Item *me_item = dynamic_cast<Item *> (me);
123   Grob *left_col = me_item->get_column ();
124
125   Interval last_ext;
126   Direction break_dir = me_item->break_status_dir ();
127   Grob *last_grob = Spacing_interface::extremal_break_aligned_grob (me, LEFT,
128                     break_dir,
129                     &last_ext);
130   if (!last_grob)
131     {
132       /*
133         TODO:
134
135         Should insert an adjustable space here? For exercises, you might want to
136         use a staff without a clef in the beginning.
137       */
138
139       /*
140         we used to have a warning here, but it generates a lot of
141         spurious error messages.
142       */
143       return Spring ();
144     }
145
146   SCM alist = last_grob->get_property ("space-alist");
147   if (!ly_is_list (alist))
148     return Spring ();
149
150   SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
151   if (me_item->break_status_dir () == CENTER)
152     {
153       SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
154       if (scm_is_pair (nndef))
155         space_def = nndef;
156     }
157
158   if (!scm_is_pair (space_def))
159     {
160       programming_error ("unknown prefatory spacing");
161       return Spring ();
162     }
163
164   space_def = scm_cdr (space_def);
165   Real distance = scm_to_double (scm_cdr (space_def));
166   SCM type = scm_car (space_def);
167
168   Real fixed = last_ext[RIGHT];
169   Real ideal = fixed + 1.0;
170
171   if (scm_is_eq (type, ly_symbol2scm ("fixed-space")))
172     {
173       fixed += distance;
174       ideal = fixed;
175     }
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")))
179     {
180       fixed += distance / 2;
181       ideal = fixed + distance / 2;
182     }
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")))
186     {
187       fixed = last_ext[LEFT] + max (last_ext.length (), distance);
188       ideal = fixed;
189     }
190
191   Real stretchability = ideal - fixed;
192
193   /* 'situational_space' passed by the caller
194       could include full-measure-extra-space */
195   ideal += situational_space;
196
197   Real optical_correction = next_notes_correction (me, last_grob);
198   fixed += optical_correction;
199   ideal += optical_correction;
200
201   Real min_dist = Paper_column::minimum_distance (left_col, right_col);
202
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);
207
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));
211   return ret;
212 }
213
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.",
218
219                /* properties */
220                "stem-spacing-correction "
221               );