]> git.donarmstrong.com Git - lilypond.git/blob - lily/staff-spacing.cc
update outdated comments
[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--2007  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 occure) 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   Grob *separation_item = 0;
119   Item *me_item = dynamic_cast<Item *> (me);
120
121   extract_grob_set (me, "left-items", items);
122   for (vsize i = items.size (); i--;)
123     {
124       Grob *cand = items[i];
125       if (cand && Separation_item::has_interface (cand))
126         separation_item = cand;
127     }
128
129   //  printf ("doing col %d\n" , Paper_column::get_rank (left_col));
130   if (!separation_item)
131     {
132       programming_error ("no sep item");
133       return Spring ();
134     }
135
136   Interval last_ext;
137   Grob *last_grob = Separation_item::extremal_break_aligned_grob (separation_item, RIGHT,
138                                                                   &last_ext);
139   if (!last_grob)
140     {
141       /*
142         TODO:
143
144         Should  insert a adjustable space here? For excercises, you might want to
145         use a staff without a clef in the beginning.
146       */
147
148       /*
149         we used to have a warning here, but it generates a lot of
150         spurious error messages.
151       */
152       return Spring ();
153     }
154
155   SCM alist = last_grob->get_property ("space-alist");
156   if (!scm_list_p (alist))
157     return Spring ();
158
159   SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
160   if (me_item->break_status_dir () == CENTER)
161     {
162       SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
163       if (scm_is_pair (nndef))
164         space_def = nndef;
165     }
166
167   if (!scm_is_pair (space_def))
168     {
169       programming_error ("unknown prefatory spacing");
170       return Spring ();
171     }
172
173   space_def = scm_cdr (space_def);
174   Real distance = scm_to_double (scm_cdr (space_def));
175   SCM type = scm_car (space_def);
176
177   Real fixed = last_ext[RIGHT];
178   Real ideal = fixed + 1.0;
179
180   if (type == ly_symbol2scm ("fixed-space"))
181     {
182       fixed += distance;
183       ideal = fixed;
184     }
185   else if (type == ly_symbol2scm ("extra-space"))
186     ideal = fixed + distance;
187   else if (type == ly_symbol2scm ("semi-fixed-space"))
188     {
189       fixed += distance / 2;
190       ideal = fixed + distance / 2;
191     }
192   else if (type == ly_symbol2scm ("minimum-space"))
193     ideal = last_ext[LEFT] + max (last_ext.length (), distance);
194   else if (type == ly_symbol2scm ("minimum-fixed-space"))
195     {
196       fixed = last_ext[LEFT] + max (last_ext.length (), distance);
197       ideal = fixed;
198     }
199
200   Real optical_correction = next_notes_correction (me, last_grob);
201   Real min_dist = Spacing_interface::minimum_distance (me, 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   Real correction = max (optical_correction, min_dist_correction);
206
207   fixed += correction;
208   ideal += correction;
209
210   Spring ret (ideal, min_dist);
211   ret.set_inverse_stretch_strength (ideal - fixed);
212   return ret;
213 }
214
215 ADD_INTERFACE (Staff_spacing,
216                "This object calculates spacing details from a "
217                " breakable symbol (left) to another object. For example, it takes care "
218                " of  optical spacing from  a bar lines to a note.",
219
220                /* properties */
221                "stem-spacing-correction "
222                );