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