Update from Francisco.
[lilypond.git] / lily / note-spacing.cc
blob90db07a7e82ab1159a3804c4308b3f9bccc21fcb
1 /*
2 note-spacing.cc -- implement Note_spacing
4 source file of the GNU LilyPond music typesetter
6 (c) 2001--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "note-spacing.hh"
11 #include "bar-line.hh"
12 #include "directional-element-interface.hh"
13 #include "grob-array.hh"
14 #include "paper-column.hh"
15 #include "moment.hh"
16 #include "note-column.hh"
17 #include "warn.hh"
18 #include "stem.hh"
19 #include "separation-item.hh"
20 #include "spacing-interface.hh"
21 #include "staff-spacing.hh"
22 #include "accidental-placement.hh"
23 #include "output-def.hh"
24 #include "pointer-group-interface.hh"
27 TODO: detect hshifts due to collisions, and account for them in
28 spacing?
31 Spring
32 Note_spacing::get_spacing (Grob *me, Item *right_col,
33 Real base_space, Real increment)
35 vector<Item*> note_columns = Spacing_interface::left_note_columns (me);
36 Real left_head_end = 0;
38 for (vsize i = 0; i < note_columns.size (); i++)
40 SCM r = note_columns[i]->get_object ("rest");
41 Grob *g = unsmob_grob (r);
42 Grob *col = note_columns[i]->get_column ();
44 if (!g)
45 g = Note_column::first_head (note_columns[i]);
48 Ugh. If Stem is switched off, we don't know what the
49 first note head will be.
51 if (g)
53 if (g->common_refpoint (col, X_AXIS) != col)
54 programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect");
55 else
56 left_head_end = g->extent (col, X_AXIS)[RIGHT];
61 The main factor that determines the amount of space is the width of the
62 note head (or the rest). For example, a quarter rest gets almost 0.5 ss
63 less horizontal space than a note.
65 The other parts of a note column (eg. flags, accidentals, etc.) don't get
66 the full amount of space. We give them half the amount of space, but then
67 adjust things so there are no collisions.
69 Drul_array<Skyline> skys = Spacing_interface::skylines (me, right_col);
70 Real distance = skys[LEFT].distance (skys[RIGHT]);
71 Real min_dist = max (0.0, distance);
72 Real min_desired_space = left_head_end + (min_dist - left_head_end + base_space - increment) / 2;
73 Real ideal = base_space - increment + left_head_end;
75 /* If we have a NonMusical column on the right, we measure the ideal distance
76 to the bar-line (if present), not the start of the column. */
77 if (!Paper_column::is_musical (right_col)
78 && !skys[RIGHT].is_empty ()
79 && to_boolean (me->get_property ("space-to-barline")))
81 Grob *bar = Pointer_group_interface::find_grob (right_col,
82 ly_symbol2scm ("elements"),
83 Bar_line::has_interface);
85 if (bar)
87 Real shift = bar->extent (right_col, X_AXIS)[LEFT];
88 ideal -= shift;
89 min_desired_space -= max (shift, 0.0);
91 else
92 ideal -= right_col->extent (right_col, X_AXIS)[RIGHT];
95 ideal = max (ideal, min_desired_space);
96 stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space);
98 /* TODO: grace notes look bad when things are stretched. Should we increase
99 their stretch strength? */
100 Spring ret (max (0.0, ideal), min_dist);
101 ret.set_inverse_compress_strength (max (0.0, ideal - min_desired_space));
102 ret.set_inverse_stretch_strength (max (0.1, base_space - increment));
103 return ret;
106 static Real
107 knee_correction (Grob *note_spacing, Grob *right_stem, Real increment)
109 Real note_head_width = increment;
110 Grob *head = right_stem ? Stem::support_head (right_stem) : 0;
111 Grob *rcolumn = dynamic_cast<Item*> (head)->get_column ();
113 Interval head_extent;
114 if (head)
116 head_extent = head->extent (rcolumn, X_AXIS);
118 if (!head_extent.is_empty ())
119 note_head_width = head_extent[RIGHT];
121 note_head_width -= Stem::thickness (right_stem);
124 return -note_head_width * get_grob_direction (right_stem)
125 * robust_scm2double (note_spacing->get_property ("knee-spacing-correction"), 0);
128 static Real
129 different_directions_correction (Grob *note_spacing,
130 Drul_array<Interval> stem_posns,
131 Direction left_stem_dir)
133 Real ret = 0.0;
134 Interval intersect = stem_posns[LEFT];
135 intersect.intersect (stem_posns[RIGHT]);
137 if (!intersect.is_empty ())
139 ret = abs (intersect.length ());
142 Ugh. 7 is hardcoded.
144 ret = min (ret / 7, 1.0)
145 * left_stem_dir
146 * robust_scm2double (note_spacing->get_property ("stem-spacing-correction"), 0);
148 return ret;
151 static Real
152 same_direction_correction (Grob *note_spacing, Drul_array<Interval> head_posns)
155 Correct for the following situation:
160 | X |
161 | | |
162 ========
164 ^ move the center one to the left.
167 this effect seems to be much more subtle than the
168 stem-direction stuff (why?), and also does not scale with the
169 difference in stem length.
173 Interval hp = head_posns[LEFT];
174 hp.intersect (head_posns[RIGHT]);
175 if (!hp.is_empty ())
176 return 0;
178 Direction lowest
179 = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
181 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
182 Real corr = robust_scm2double (note_spacing->get_property ("same-direction-correction"), 0);
184 return (delta > 1) ? -lowest * corr : 0;
189 Correct for optical illusions. See [Wanske] p. 138. The combination
190 up-stem + down-stem should get extra space, the combination
191 down-stem + up-stem less.
193 TODO: have to check whether the stems are in the same staff.
195 void
196 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
197 Real increment,
198 Real *space, Real *fixed)
200 Drul_array<Direction> stem_dirs (CENTER, CENTER);
201 Drul_array<Interval> stem_posns;
202 Drul_array<Interval> head_posns;
203 Drul_array<SCM> props (me->get_object ("left-items"),
204 me->get_object ("right-items"));
206 Drul_array<Spanner *> beams_drul (0, 0);
207 Drul_array<Grob *> stems_drul (0, 0);
209 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
210 Interval intersect;
211 Interval bar_xextent;
212 Interval bar_yextent;
214 Direction d = LEFT;
216 Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT,
217 rcolumn->break_status_dir (),
218 &bar_xextent);
219 if (bar && dynamic_cast<Item*> (bar)->get_column () == rcolumn)
220 bar_yextent = Staff_spacing::bar_y_positions (bar);
224 vector<Grob*> const &items (ly_scm2link_array (props [d]));
225 for (vsize i = 0; i < items.size (); i++)
227 Item *it = dynamic_cast<Item *> (items[i]);
228 if (!Note_column::has_interface (it))
229 continue;
232 don't correct if accidentals are sticking out of the right side.
234 if (d == RIGHT && Note_column::accidentals (it))
235 return;
237 Grob *stem = Note_column::get_stem (it);
239 if (!stem || !stem->is_live () || Stem::is_invisible (stem))
240 return;
242 stems_drul[d] = stem;
243 beams_drul[d] = Stem::get_beam (stem);
245 Direction stem_dir = get_grob_direction (stem);
246 if (stem_dirs[d] && stem_dirs[d] != stem_dir)
247 return;
249 stem_dirs[d] = stem_dir;
252 Correction doesn't seem appropriate when there is a large flag
253 hanging from the note.
255 if (d == LEFT
256 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
257 return;
259 Interval hp = Stem::head_positions (stem);
260 if (!hp.is_empty ())
262 Real chord_start = hp[stem_dir];
265 can't look at stem-end-position, since that triggers
266 beam slope computations.
268 Real stem_end = hp[stem_dir] +
269 stem_dir * robust_scm2double (stem->get_property ("length"), 7);
271 stem_posns[d] = Interval (min (chord_start, stem_end),
272 max (chord_start, stem_end));
273 head_posns[d].unite (hp);
277 while (flip (&d) != LEFT);
279 Real correction = 0.0;
281 if (!bar_yextent.is_empty ())
283 stem_dirs[RIGHT] = -stem_dirs[LEFT];
284 stem_posns[RIGHT] = bar_yextent;
285 stem_posns[RIGHT] *= 2;
288 if (stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
290 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
292 correction = knee_correction (me, stems_drul[RIGHT], increment);
293 *fixed += correction;
295 else
297 correction = different_directions_correction (me, stem_posns, stem_dirs[LEFT]);
299 if (!bar_yextent.is_empty ())
300 correction *= 0.5;
303 else if (stem_dirs[LEFT] * stem_dirs[RIGHT] == 1)
304 correction = same_direction_correction (me, head_posns);
306 *space += correction;
308 /* there used to be a correction for bar_xextent () here, but
309 it's unclear what that was good for ?
313 ADD_INTERFACE (Note_spacing,
314 "This object calculates spacing wishes for individual voices.",
316 /* properties */
317 "knee-spacing-correction "
318 "left-items "
319 "right-items "
320 "same-direction-correction "
321 "stem-spacing-correction "
322 "space-to-barline "