]> git.donarmstrong.com Git - lilypond.git/blob - lily/staff-spacing.cc
Web-ja: update introduction
[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 /* A stem following a bar-line creates an optical illusion similar to the
38    one mentioned in note-spacing.cc. We correct for it here.
39
40    TODO: should we still correct if there are accidentals/arpeggios before
41    the stem?
42 */
43
44 Real
45 Staff_spacing::optical_correction (Grob *me, Grob *g, Interval bar_height)
46 {
47   if (!g || !has_interface<Note_column> (g))
48     return 0;
49
50   Grob *stem = Note_column::get_stem (g);
51   Real ret = 0.0;
52
53   if (!bar_height.is_empty () && stem)
54     {
55       Direction d = get_grob_direction (stem);
56       if (Stem::is_normal_stem (stem) && d == DOWN)
57         {
58           Interval stem_posns = stem->pure_y_extent (stem, 0, INT_MAX);
59
60           stem_posns.intersect (bar_height);
61
62           ret = min (abs (stem_posns.length () / 7.0), 1.0);
63           ret *= robust_scm2double (me->get_property ("stem-spacing-correction"), 1);
64         }
65     }
66   return ret;
67 }
68
69 /*
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)
74 {
75   Interval bar_size;
76   bar_size.set_empty ();
77
78   if (bar_grob->internal_has_interface (ly_symbol2scm ("bar-line-interface")))
79     {
80       SCM glyph = bar_grob->get_property ("glyph-name");
81       Grob *staff_sym = Staff_symbol_referencer::get_staff_symbol (bar_grob);
82
83       string glyph_string = scm_is_string (glyph) ? ly_scm2string (glyph) : "";
84       if (glyph_string.substr (0, 1) == "|"
85           || glyph_string.substr (0, 1) == ".")
86         {
87           Grob *common = bar_grob->common_refpoint (staff_sym, Y_AXIS);
88           bar_size = bar_grob->extent (common, Y_AXIS);
89           bar_size *= 1.0 / Staff_symbol_referencer::staff_space (bar_grob);
90         }
91     }
92   return bar_size;
93 }
94
95 Real
96 Staff_spacing::next_notes_correction (Grob *me,
97                                       Grob *last_grob)
98 {
99   Interval bar_size = bar_y_positions (last_grob);
100   Grob *orig = me->original () ? me->original () : me;
101   vector<Item *> note_columns = Spacing_interface::right_note_columns (orig);
102
103   Real max_optical = 0.0;
104
105   for (vsize i = 0; i < note_columns.size (); i++)
106     max_optical = max (max_optical, optical_correction (me, note_columns[i], bar_size));
107
108   return max_optical;
109 }
110
111 /* We calculate three things here: the ideal distance, the minimum distance
112    (which is the distance at which collisions will occur) and the "fixed"
113    distance, which is the distance at which things start to look really bad.
114    We arrange things so that the fixed distance will be attained when the
115    line is compressed with a force of 1.0 */
116 Spring
117 Staff_spacing::get_spacing (Grob *me, Grob *right_col, Real situational_space)
118 {
119   Item *me_item = dynamic_cast<Item *> (me);
120   Grob *left_col = me_item->get_column ();
121
122   Interval last_ext;
123   Direction break_dir = me_item->break_status_dir ();
124   Grob *last_grob = Spacing_interface::extremal_break_aligned_grob (me, LEFT,
125                     break_dir,
126                     &last_ext);
127   if (!last_grob)
128     {
129       /*
130         TODO:
131
132         Should insert an adjustable space here? For exercises, you might want to
133         use a staff without a clef in the beginning.
134       */
135
136       /*
137         we used to have a warning here, but it generates a lot of
138         spurious error messages.
139       */
140       return Spring ();
141     }
142
143   SCM alist = last_grob->get_property ("space-alist");
144   if (!ly_is_list (alist))
145     return Spring ();
146
147   SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
148   if (me_item->break_status_dir () == CENTER)
149     {
150       SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
151       if (scm_is_pair (nndef))
152         space_def = nndef;
153     }
154
155   if (!scm_is_pair (space_def))
156     {
157       programming_error ("unknown prefatory spacing");
158       return Spring ();
159     }
160
161   space_def = scm_cdr (space_def);
162   Real distance = scm_to_double (scm_cdr (space_def));
163   SCM type = scm_car (space_def);
164
165   Real fixed = last_ext[RIGHT];
166   Real ideal = fixed + 1.0;
167
168   if (scm_is_eq (type, ly_symbol2scm ("fixed-space")))
169     {
170       fixed += distance;
171       ideal = fixed;
172     }
173   else if (scm_is_eq (type, ly_symbol2scm ("extra-space")))
174     ideal = fixed + distance;
175   else if (scm_is_eq (type, ly_symbol2scm ("semi-fixed-space")))
176     {
177       fixed += distance / 2;
178       ideal = fixed + distance / 2;
179     }
180   else if (scm_is_eq (type, ly_symbol2scm ("minimum-space")))
181     ideal = last_ext[LEFT] + max (last_ext.length (), distance);
182   else if (scm_is_eq (type, ly_symbol2scm ("minimum-fixed-space")))
183     {
184       fixed = last_ext[LEFT] + max (last_ext.length (), distance);
185       ideal = fixed;
186     }
187
188   Real stretchability = ideal - fixed;
189
190   /* 'situational_space' passed by the caller
191       could include full-measure-extra-space */
192   ideal += situational_space;
193
194   Real optical_correction = next_notes_correction (me, last_grob);
195   fixed += optical_correction;
196   ideal += optical_correction;
197
198   Real min_dist = Paper_column::minimum_distance (left_col, right_col);
199
200   /* ensure that the "fixed" distance will leave a gap of at least 0.3 ss. */
201   Real min_dist_correction = max (0.0, 0.3 + min_dist - fixed);
202   fixed += min_dist_correction;
203   ideal = max (ideal, fixed);
204
205   Spring ret (ideal, min_dist);
206   ret.set_inverse_stretch_strength (max (0.0, stretchability));
207   ret.set_inverse_compress_strength (max (0.0, ideal - fixed));
208   return ret;
209 }
210
211 ADD_INTERFACE (Staff_spacing,
212                "This object calculates spacing details from a breakable"
213                " symbol (left) to another object.  For example, it takes care"
214                " of optical spacing from a bar line to a note.",
215
216                /* properties */
217                "stem-spacing-correction "
218               );