2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2001--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
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.
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.
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/>.
20 #include "note-spacing.hh"
22 #include "accidental-placement.hh"
23 #include "bar-line.hh"
24 #include "directional-element-interface.hh"
25 #include "grob-array.hh"
27 #include "note-column.hh"
28 #include "output-def.hh"
29 #include "paper-column.hh"
30 #include "pointer-group-interface.hh"
31 #include "separation-item.hh"
32 #include "spacing-interface.hh"
33 #include "staff-spacing.hh"
38 TODO: detect hshifts due to collisions, and account for them in
43 Note_spacing::get_spacing (Grob
*me
, Item
*right_col
,
44 Real base_space
, Real increment
)
46 vector
<Item
*> note_columns
= Spacing_interface::left_note_columns (me
);
47 Real left_head_end
= 0;
49 for (vsize i
= 0; i
< note_columns
.size (); i
++)
51 SCM r
= note_columns
[i
]->get_object ("rest");
52 Grob
*g
= unsmob_grob (r
);
53 Grob
*col
= note_columns
[i
]->get_column ();
56 g
= Note_column::first_head (note_columns
[i
]);
59 Ugh. If Stem is switched off, we don't know what the
60 first note head will be.
64 if (g
->common_refpoint (col
, X_AXIS
) != col
)
65 programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect");
67 left_head_end
= g
->extent (col
, X_AXIS
)[RIGHT
];
72 The main factor that determines the amount of space is the width of the
73 note head (or the rest). For example, a quarter rest gets almost 0.5 ss
74 less horizontal space than a note.
76 The other parts of a note column (eg. flags, accidentals, etc.) don't get
77 the full amount of space. We give them half the amount of space, but then
78 adjust things so there are no collisions.
80 Drul_array
<Skyline
> skys
= Spacing_interface::skylines (me
, right_col
);
81 Real distance
= skys
[LEFT
].distance (skys
[RIGHT
]);
82 Real min_dist
= max (0.0, distance
);
83 Real min_desired_space
= left_head_end
+ (min_dist
- left_head_end
+ base_space
- increment
) / 2;
84 Real ideal
= base_space
- increment
+ left_head_end
;
86 /* If we have a NonMusical column on the right, we measure the ideal distance
87 to the bar-line (if present), not the start of the column. */
88 if (!Paper_column::is_musical (right_col
)
89 && !skys
[RIGHT
].is_empty ()
90 && to_boolean (me
->get_property ("space-to-barline")))
92 Grob
*bar
= Pointer_group_interface::find_grob (right_col
,
93 ly_symbol2scm ("elements"),
94 Bar_line::non_empty_barline
);
98 Real shift
= bar
->extent (right_col
, X_AXIS
)[LEFT
];
100 min_desired_space
-= max (shift
, 0.0);
103 ideal
-= right_col
->extent (right_col
, X_AXIS
)[RIGHT
];
106 ideal
= max (ideal
, min_desired_space
);
107 stem_dir_correction (me
, right_col
, increment
, &ideal
, &min_desired_space
);
109 /* TODO: grace notes look bad when things are stretched. Should we increase
110 their stretch strength? */
111 Spring
ret (max (0.0, ideal
), min_dist
);
112 ret
.set_inverse_compress_strength (max (0.0, ideal
- min_desired_space
));
113 ret
.set_inverse_stretch_strength (max (0.1, base_space
- increment
));
118 knee_correction (Grob
*note_spacing
, Grob
*right_stem
, Real increment
)
120 Real note_head_width
= increment
;
121 Grob
*head
= right_stem
? Stem::support_head (right_stem
) : 0;
122 Grob
*rcolumn
= dynamic_cast<Item
*> (head
)->get_column ();
124 Interval head_extent
;
127 head_extent
= head
->extent (rcolumn
, X_AXIS
);
129 if (!head_extent
.is_empty ())
130 note_head_width
= head_extent
[RIGHT
];
132 note_head_width
-= Stem::thickness (right_stem
);
135 return -note_head_width
* get_grob_direction (right_stem
)
136 * robust_scm2double (note_spacing
->get_property ("knee-spacing-correction"), 0);
140 different_directions_correction (Grob
*note_spacing
,
141 Drul_array
<Interval
> stem_posns
,
142 Direction left_stem_dir
)
145 Interval intersect
= stem_posns
[LEFT
];
146 intersect
.intersect (stem_posns
[RIGHT
]);
148 if (!intersect
.is_empty ())
150 ret
= abs (intersect
.length ());
155 ret
= min (ret
/ 7, 1.0)
157 * robust_scm2double (note_spacing
->get_property ("stem-spacing-correction"), 0);
163 same_direction_correction (Grob
*note_spacing
, Drul_array
<Interval
> head_posns
)
166 Correct for the following situation:
175 ^ move the center one to the left.
178 this effect seems to be much more subtle than the
179 stem-direction stuff (why?), and also does not scale with the
180 difference in stem length.
184 Interval hp
= head_posns
[LEFT
];
185 hp
.intersect (head_posns
[RIGHT
]);
190 = (head_posns
[LEFT
][DOWN
] > head_posns
[RIGHT
][UP
]) ? RIGHT
: LEFT
;
192 Real delta
= head_posns
[-lowest
][DOWN
] - head_posns
[lowest
][UP
];
193 Real corr
= robust_scm2double (note_spacing
->get_property ("same-direction-correction"), 0);
195 return (delta
> 1) ? -lowest
* corr
: 0;
199 Correct for optical illusions. See [Wanske] p. 138. The combination
200 up-stem + down-stem should get extra space, the combination
201 down-stem + up-stem less.
203 TODO: have to check whether the stems are in the same staff.
206 Note_spacing::stem_dir_correction (Grob
*me
, Item
*rcolumn
,
208 Real
*space
, Real
*fixed
)
210 Drul_array
<Direction
> stem_dirs (CENTER
, CENTER
);
211 Drul_array
<Interval
> stem_posns
;
212 Drul_array
<Interval
> head_posns
;
213 Drul_array
<SCM
> props (me
->get_object ("left-items"),
214 me
->get_object ("right-items"));
216 Drul_array
<Spanner
*> beams_drul (0, 0);
217 Drul_array
<Grob
*> stems_drul (0, 0);
219 stem_dirs
[LEFT
] = stem_dirs
[RIGHT
] = CENTER
;
221 Interval bar_xextent
;
222 Interval bar_yextent
;
226 bool acc_right
= false;
228 Grob
*bar
= Spacing_interface::extremal_break_aligned_grob (me
, RIGHT
,
229 rcolumn
->break_status_dir (),
231 if (bar
&& dynamic_cast<Item
*> (bar
)->get_column () == rcolumn
)
232 bar_yextent
= Staff_spacing::bar_y_positions (bar
);
236 vector
<Grob
*> const &items (ly_scm2link_array (props
[d
]));
237 for (vsize i
= 0; i
< items
.size (); i
++)
239 Item
*it
= dynamic_cast<Item
*> (items
[i
]);
240 if (!Note_column::has_interface (it
))
242 if (d
== RIGHT
&& it
->get_column () != rcolumn
)
246 Find accidentals which are sticking out of the right side.
249 acc_right
= acc_right
|| Note_column::accidentals (it
);
251 Grob
*stem
= Note_column::get_stem (it
);
253 if (!stem
|| !stem
->is_live () || Stem::is_invisible (stem
))
256 stems_drul
[d
] = stem
;
257 beams_drul
[d
] = Stem::get_beam (stem
);
259 Direction stem_dir
= get_grob_direction (stem
);
260 if (stem_dirs
[d
] && stem_dirs
[d
] != stem_dir
)
263 stem_dirs
[d
] = stem_dir
;
266 Correction doesn't seem appropriate when there is a large flag
267 hanging from the note.
270 && Stem::duration_log (stem
) > 2 && !Stem::get_beam (stem
))
273 Interval hp
= Stem::head_positions (stem
);
276 Real chord_start
= hp
[stem_dir
];
279 can't look at stem-end-position, since that triggers
280 beam slope computations.
282 Real stem_end
= hp
[stem_dir
]
283 + stem_dir
* robust_scm2double (stem
->get_property ("length"), 7);
285 stem_posns
[d
] = Interval (min (chord_start
, stem_end
),
286 max (chord_start
, stem_end
));
287 head_posns
[d
].unite (hp
);
291 while (flip (&d
) != LEFT
);
293 Real correction
= 0.0;
295 if (!bar_yextent
.is_empty ())
297 stem_dirs
[RIGHT
] = -stem_dirs
[LEFT
];
298 stem_posns
[RIGHT
] = bar_yextent
;
299 stem_posns
[RIGHT
] *= 2;
302 if (stem_dirs
[LEFT
] * stem_dirs
[RIGHT
] == -1)
304 if (beams_drul
[LEFT
] && beams_drul
[LEFT
] == beams_drul
[RIGHT
])
306 correction
= knee_correction (me
, stems_drul
[RIGHT
], increment
);
307 *fixed
+= correction
;
311 correction
= different_directions_correction (me
, stem_posns
, stem_dirs
[LEFT
]);
313 if (!bar_yextent
.is_empty ())
318 Only apply same direction correction if there are no
319 accidentals sticking out of the right hand side.
321 else if (stem_dirs
[LEFT
] * stem_dirs
[RIGHT
] == 1
323 correction
= same_direction_correction (me
, head_posns
);
325 *space
+= correction
;
327 /* there used to be a correction for bar_xextent () here, but
328 it's unclear what that was good for ?
332 ADD_INTERFACE (Note_spacing
,
333 "This object calculates spacing wishes for individual voices.",
336 "knee-spacing-correction "
339 "same-direction-correction "
340 "stem-spacing-correction "