]> git.donarmstrong.com Git - lilypond.git/blob - lily/staff-spacing.cc
Run `make grand-replace'.
[lilypond.git] / lily / staff-spacing.cc
1 /*
2   staff-spacing.cc -- implement Staff_spacing
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2001--2008  Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "staff-spacing.hh"
10
11 #include <cstdio>
12 using namespace std;
13
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"
26
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.
29
30    TODO: should we still correct if there are accidentals/arpeggios before
31    the stem?
32 */
33
34 Real
35 Staff_spacing::optical_correction (Grob *me, Grob *g, Interval bar_height)
36 {
37   if (!g || !Note_column::has_interface (g))
38     return 0;
39
40   Grob *stem = Note_column::get_stem (g);
41   Real ret = 0.0;
42
43   if (!bar_height.is_empty () && stem)
44     {
45       Direction d = get_grob_direction (stem);
46       if (Stem::is_normal_stem (stem) && d == DOWN)
47         {
48
49           /*
50             can't look at stem-end-position, since that triggers
51             beam slope computations.
52           */
53           Real stem_start = Stem::head_positions (stem) [d];
54           Real stem_end = stem_start + 
55             d * robust_scm2double (stem->get_property ("length"), 7);
56           
57           Interval stem_posns (min (stem_start, stem_end),
58                                max (stem_end, stem_start));
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   if (Bar_line::has_interface (bar_grob))
78     {
79       SCM glyph = bar_grob->get_property ("glyph-name");
80       Grob *staff_sym = Staff_symbol_referencer::get_staff_symbol (bar_grob);
81
82       string glyph_string = scm_is_string (glyph) ? ly_scm2string (glyph) : "";
83       if (glyph_string.substr (0, 1) == "|"
84           || glyph_string.substr (0, 1) == ".")
85         {
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);
89         }
90     }
91   return bar_size;
92 }
93
94 Real
95 Staff_spacing::next_notes_correction (Grob *me,
96                                       Grob *last_grob)
97 {
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);
101
102   Real max_optical = 0.0;
103
104   for (vsize i = 0; i < note_columns.size (); i++)
105     max_optical = max (max_optical, optical_correction (me, note_columns[i], bar_size));
106
107   return max_optical;
108 }
109
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)
117 {
118   Item *me_item = dynamic_cast<Item *> (me);
119   Grob *left_col = me_item->get_column ();
120
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)
127     {
128       /*
129         TODO:
130
131         Should  insert a adjustable space here? For excercises, you might want to
132         use a staff without a clef in the beginning.
133       */
134
135       /*
136         we used to have a warning here, but it generates a lot of
137         spurious error messages.
138       */
139       return Spring ();
140     }
141
142   SCM alist = last_grob->get_property ("space-alist");
143   if (!scm_list_p (alist))
144     return Spring ();
145
146   SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
147   if (me_item->break_status_dir () == CENTER)
148     {
149       SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
150       if (scm_is_pair (nndef))
151         space_def = nndef;
152     }
153
154   if (!scm_is_pair (space_def))
155     {
156       programming_error ("unknown prefatory spacing");
157       return Spring ();
158     }
159
160   space_def = scm_cdr (space_def);
161   Real distance = scm_to_double (scm_cdr (space_def));
162   SCM type = scm_car (space_def);
163
164   Real fixed = last_ext[RIGHT];
165   Real ideal = fixed + 1.0;
166
167   if (type == ly_symbol2scm ("fixed-space"))
168     {
169       fixed += distance;
170       ideal = fixed;
171     }
172   else if (type == ly_symbol2scm ("extra-space"))
173     ideal = fixed + distance;
174   else if (type == ly_symbol2scm ("semi-fixed-space"))
175     {
176       fixed += distance / 2;
177       ideal = fixed + distance / 2;
178     }
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"))
182     {
183       fixed = last_ext[LEFT] + max (last_ext.length (), distance);
184       ideal = fixed;
185     }
186
187
188   Real optical_correction = next_notes_correction (me, last_grob);
189   Real min_dist = Paper_column::minimum_distance (left_col, right_col);
190
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);
194
195   fixed += correction;
196   ideal += correction;
197
198   Spring ret (ideal, min_dist);
199   ret.set_inverse_stretch_strength (max (0.0, ideal - fixed));
200   return ret;
201 }
202
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.",
207
208                /* properties */
209                "stem-spacing-correction "
210                );