]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-spacing.cc
Issue 4550 (2/2) Avoid "using namespace std;" in included files
[lilypond.git] / lily / note-spacing.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2001--2015  Han-Wen Nienhuys <hanwen@xs4all.nl>
5
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.
10
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.
15
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/>.
18 */
19
20 #include "note-spacing.hh"
21
22 #include "accidental-placement.hh"
23 #include "bar-line.hh"
24 #include "directional-element-interface.hh"
25 #include "grob-array.hh"
26 #include "moment.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"
34 #include "staff-symbol-referencer.hh"
35 #include "stem.hh"
36 #include "warn.hh"
37
38 using std::vector;
39
40 /*
41   Adjust the ideal and minimum distance between note columns,
42   based on the notehead size, skylines, and optical illusions.
43 */
44 Spring
45 Note_spacing::get_spacing (Grob *me, Item *right_col,
46                            Spring base, Real increment)
47 {
48   vector<Item *> note_columns = Spacing_interface::left_note_columns (me);
49   Real left_head_end = 0;
50
51   for (vsize i = 0; i < note_columns.size (); i++)
52     {
53       SCM r = note_columns[i]->get_object ("rest");
54       Grob *g = unsmob<Grob> (r);
55       Grob *col = note_columns[i]->get_column ();
56
57       if (!g)
58         g = Note_column::first_head (note_columns[i]);
59
60       /*
61         Ugh. If Stem is switched off, we don't know what the
62         first note head will be.
63       */
64       if (g)
65         {
66           if (g->common_refpoint (col, X_AXIS) != col)
67             programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect");
68           else
69             left_head_end = g->extent (col, X_AXIS)[RIGHT];
70         }
71     }
72
73   /*
74     The main factor that determines the amount of space is the width of the
75     note head (or the rest). For example, a quarter rest gets almost 0.5 ss
76     less horizontal space than a note.
77   */
78   Real ideal = base.distance () - increment + left_head_end;
79   Drul_array<Skyline> skys = Spacing_interface::skylines (me, right_col);
80   Real distance = skys[LEFT].distance (skys[RIGHT], robust_scm2double (right_col->get_property ("skyline-vertical-padding"), 0.0));
81   Real min_dist = std::max (0.0, distance);
82   base.set_min_distance (min_dist);
83
84   /* If we have a NonMusical column on the right, we measure the ideal distance
85      to the bar-line (if present), not the start of the column. */
86   if (!Paper_column::is_musical (right_col)
87       && !skys[RIGHT].is_empty ()
88       && to_boolean (me->get_property ("space-to-barline")))
89     {
90       Grob *bar = Pointer_group_interface::find_grob (right_col,
91                                                       ly_symbol2scm ("elements"),
92                                                       Bar_line::non_empty_barline);
93
94       if (bar)
95         ideal -= bar->extent (right_col, X_AXIS)[LEFT];
96       else
97         {
98           /* Measure ideal distance to the right side of the NonMusical column
99              but keep at least half the gap we would have had to a note */
100           Real min_desired_space = (ideal + min_dist) / 2.0;
101           ideal -= right_col->extent (right_col, X_AXIS)[RIGHT];
102           ideal = std::max (ideal, min_desired_space);
103         }
104     }
105
106   stem_dir_correction (me, right_col, increment, &ideal);
107
108   base.set_distance (std::max (0.0, ideal));
109   return base;
110 }
111
112 static Real
113 knee_correction (Grob *note_spacing, Grob *right_stem, Real increment)
114 {
115   Real note_head_width = increment;
116   Grob *head = right_stem ? Stem::support_head (right_stem) : 0;
117   Grob *rcolumn = dynamic_cast<Item *> (head)->get_column ();
118
119   Interval head_extent;
120   if (head)
121     {
122       head_extent = head->extent (rcolumn, X_AXIS);
123
124       if (!head_extent.is_empty ())
125         note_head_width = head_extent[RIGHT];
126
127       note_head_width -= Stem::thickness (right_stem);
128     }
129
130   return -note_head_width * get_grob_direction (right_stem)
131          * robust_scm2double (note_spacing->get_property ("knee-spacing-correction"), 0);
132 }
133
134 static Real
135 different_directions_correction (Grob *note_spacing,
136                                  Drul_array<Interval> stem_posns,
137                                  Direction left_stem_dir)
138 {
139   Real ret = 0.0;
140   Interval intersect = stem_posns[LEFT];
141   intersect.intersect (stem_posns[RIGHT]);
142
143   if (!intersect.is_empty ())
144     {
145       ret = abs (intersect.length ());
146
147       /*
148         Ugh. 7 is hardcoded.
149       */
150       ret = std::min (ret / 7, 1.0)
151             * left_stem_dir
152             * robust_scm2double (note_spacing->get_property ("stem-spacing-correction"), 0);
153     }
154   return ret;
155 }
156
157 static Real
158 same_direction_correction (Grob *note_spacing, Drul_array<Interval> head_posns)
159 {
160   /*
161     Correct for the following situation:
162
163     X      X
164     |      |
165     |      |
166     |   X  |
167     |  |   |
168     ========
169
170     ^ move the center one to the left.
171
172
173     this effect seems to be much more subtle than the
174     stem-direction stuff (why?), and also does not scale with the
175     difference in stem length.
176
177   */
178
179   Interval hp = head_posns[LEFT];
180   hp.intersect (head_posns[RIGHT]);
181   if (!hp.is_empty ())
182     return 0;
183
184   Direction lowest
185     = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
186
187   Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP];
188   Real corr = robust_scm2double (note_spacing->get_property ("same-direction-correction"), 0);
189
190   return (delta > 1) ? -lowest * corr : 0;
191 }
192
193 /*
194   Correct for optical illusions. See [Wanske] p. 138. The combination
195   up-stem + down-stem should get extra space, the combination
196   down-stem + up-stem less.
197
198   TODO: have to check whether the stems are in the same staff.
199 */
200 void
201 Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
202                                    Real increment,
203                                    Real *space)
204 {
205   Drul_array<Direction> stem_dirs (CENTER, CENTER);
206   Drul_array<Interval> stem_posns;
207   Drul_array<Interval> head_posns;
208   Drul_array<SCM> props (me->get_object ("left-items"),
209                          me->get_object ("right-items"));
210
211   Drul_array<Spanner *> beams_drul (0, 0);
212   Drul_array<Grob *> stems_drul (0, 0);
213
214   stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
215   Interval intersect;
216   Interval bar_xextent;
217   Interval bar_yextent;
218
219   bool acc_right = false;
220
221   Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT,
222                                                               rcolumn->break_status_dir (),
223                                                               &bar_xextent);
224   if (bar && dynamic_cast<Item *> (bar)->get_column () == rcolumn)
225     bar_yextent = Staff_spacing::bar_y_positions (bar);
226
227   for (LEFT_and_RIGHT (d))
228     {
229       vector<Grob *> const &items (ly_scm2link_array (props [d]));
230       for (vsize i = 0; i < items.size (); i++)
231         {
232           Item *it = dynamic_cast<Item *> (items[i]);
233           if (!has_interface<Note_column> (it))
234             continue;
235           if (d == RIGHT && it->get_column () != rcolumn)
236             continue;
237
238           /*
239             Find accidentals which are sticking out of the right side.
240           */
241           if (d == RIGHT)
242             acc_right = acc_right || Note_column::accidentals (it);
243
244           Grob *stem = Note_column::get_stem (it);
245
246           if (!stem || !stem->is_live () || Stem::is_invisible (stem))
247             return;
248
249           stems_drul[d] = stem;
250           beams_drul[d] = Stem::get_beam (stem);
251
252           Direction stem_dir = get_grob_direction (stem);
253           if (stem_dirs[d] && stem_dirs[d] != stem_dir)
254             return;
255
256           stem_dirs[d] = stem_dir;
257
258           /*
259             Correction doesn't seem appropriate  when there is a large flag
260             hanging from the note.
261           */
262           if (d == LEFT
263               && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
264             return;
265
266           Interval hp = Stem::head_positions (stem);
267           if (!hp.is_empty ())
268             {
269               Real ss = Staff_symbol_referencer::staff_space (stem);
270               stem_posns[d] = stem->pure_y_extent (stem, 0, INT_MAX) * (2 / ss);
271               head_posns[d].unite (hp);
272             }
273         }
274     }
275
276   Real correction = 0.0;
277
278   if (!bar_yextent.is_empty ())
279     {
280       stem_dirs[RIGHT] = -stem_dirs[LEFT];
281       stem_posns[RIGHT] = bar_yextent;
282       stem_posns[RIGHT] *= 2;
283     }
284
285   if (stem_dirs[LEFT] * stem_dirs[RIGHT] == -1)
286     {
287       if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
288         {
289           correction = knee_correction (me, stems_drul[RIGHT], increment);
290         }
291       else
292         {
293           correction = different_directions_correction (me, stem_posns, stem_dirs[LEFT]);
294
295           if (!bar_yextent.is_empty ())
296             correction *= 0.5;
297         }
298     }
299   /*
300     Only apply same direction correction if there are no
301     accidentals sticking out of the right hand side.
302   */
303   else if (stem_dirs[LEFT] * stem_dirs[RIGHT] == 1
304            && !acc_right)
305     correction = same_direction_correction (me, head_posns);
306
307   *space += correction;
308
309   /* there used to be a correction for bar_xextent () here, but
310      it's unclear what that was good for ?
311   */
312 }
313
314 ADD_INTERFACE (Note_spacing,
315                "This object calculates spacing wishes for individual voices.",
316
317                /* properties */
318                "knee-spacing-correction "
319                "left-items "
320                "right-items "
321                "same-direction-correction "
322                "stem-spacing-correction "
323                "space-to-barline "
324               );
325