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