]> git.donarmstrong.com Git - lilypond.git/blob - lily/staff-spacing.cc
Merge branch 'master' into jneeman
[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   Item *me_item = dynamic_cast<Item *> (me);
119   Grob *left_col = me_item->get_column ();
120
121   Interval last_ext;
122   Grob *last_grob = Separation_item::extremal_break_aligned_grob (left_col, RIGHT,
123                                                                   &last_ext);
124   if (!last_grob)
125     {
126       /*
127         TODO:
128
129         Should  insert a adjustable space here? For excercises, you might want to
130         use a staff without a clef in the beginning.
131       */
132
133       /*
134         we used to have a warning here, but it generates a lot of
135         spurious error messages.
136       */
137       return Spring ();
138     }
139
140   SCM alist = last_grob->get_property ("space-alist");
141   if (!scm_list_p (alist))
142     return Spring ();
143
144   SCM space_def = scm_sloppy_assq (ly_symbol2scm ("first-note"), alist);
145   if (me_item->break_status_dir () == CENTER)
146     {
147       SCM nndef = scm_sloppy_assq (ly_symbol2scm ("next-note"), alist);
148       if (scm_is_pair (nndef))
149         space_def = nndef;
150     }
151
152   if (!scm_is_pair (space_def))
153     {
154       programming_error ("unknown prefatory spacing");
155       return Spring ();
156     }
157
158   space_def = scm_cdr (space_def);
159   Real distance = scm_to_double (scm_cdr (space_def));
160   SCM type = scm_car (space_def);
161
162   Real fixed = last_ext[RIGHT];
163   Real ideal = fixed + 1.0;
164
165   if (type == ly_symbol2scm ("fixed-space"))
166     {
167       fixed += distance;
168       ideal = fixed;
169     }
170   else if (type == ly_symbol2scm ("extra-space"))
171     ideal = fixed + distance;
172   else if (type == ly_symbol2scm ("semi-fixed-space"))
173     {
174       fixed += distance / 2;
175       ideal = fixed + distance / 2;
176     }
177   else if (type == ly_symbol2scm ("minimum-space"))
178     ideal = last_ext[LEFT] + max (last_ext.length (), distance);
179   else if (type == ly_symbol2scm ("minimum-fixed-space"))
180     {
181       fixed = last_ext[LEFT] + max (last_ext.length (), distance);
182       ideal = fixed;
183     }
184
185
186   Real optical_correction = next_notes_correction (me, last_grob);
187   Real min_dist = Paper_column::minimum_distance (left_col, right_col);
188
189   /* ensure that the "fixed" distance will leave a gap of at least 0.3 ss. */
190   Real min_dist_correction = max (0.0, 0.3 + min_dist - fixed);
191   Real correction = max (optical_correction, min_dist_correction);
192
193   fixed += correction;
194   ideal += correction;
195
196   Spring ret (ideal, min_dist);
197   ret.set_inverse_stretch_strength (ideal - fixed);
198   return ret;
199 }
200
201 ADD_INTERFACE (Staff_spacing,
202                "This object calculates spacing details from a "
203                " breakable symbol (left) to another object. For example, it takes care "
204                " of  optical spacing from  a bar lines to a note.",
205
206                /* properties */
207                "stem-spacing-correction "
208                );