(staff_eligible): new function.
[lilypond.git] / lily / note-spacing.cc
blob80a789e418c499f90fa0afb03b5424946cd01e3a
1 /*
2 note-spacing.cc -- implement Note_spacing
4 source file of the GNU LilyPond music typesetter
6 (c) 2001--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
10 #include "paper-column.hh"
11 #include "item.hh"
12 #include "moment.hh"
13 #include "note-spacing.hh"
14 #include "grob.hh"
15 #include "note-column.hh"
16 #include "warn.hh"
17 #include "stem.hh"
18 #include "separation-item.hh"
19 #include "staff-spacing.hh"
20 #include "accidental-placement.hh"
21 #include "paper-def.hh"
23 void
24 Note_spacing::get_spacing (Grob *me, Item* right_col,
25 Real base_space, Real increment, Real *space, Real *fixed)
28 Drul_array<SCM> props(me->get_grob_property ("left-items"),
29 me->get_grob_property ("right-items"));
30 Direction d = LEFT;
31 Direction col_dir = right_col->break_status_dir ();
32 Drul_array<Interval> extents;
34 Interval left_head_wid;
37 for (SCM s = props[d]; gh_pair_p (s); s = gh_cdr (s))
39 Item * it= dynamic_cast<Item*> (unsmob_grob (gh_car(s)));
41 if (d == RIGHT && it->break_status_dir () != col_dir)
43 it = it -> find_prebroken_piece (col_dir);
47 some kind of mismatch, eg. a note column, that is behind a
48 linebreak.
50 if (!it)
51 continue;
53 Item *it_col = it->get_column ();
54 if (d == RIGHT && right_col != it_col)
55 continue;
57 if (Separation_item::has_interface (it))
59 extents[d].unite (Separation_item::width (it));
60 continue;
63 if (d == LEFT)
65 SCM r = it->get_grob_property ("rest");
66 Grob * g = unsmob_grob (r);
67 if (!g)
68 g = Note_column::first_head (it);
71 Ugh. If Stem is switched off, we don't know what the
72 first note head will be.
74 if (g)
75 left_head_wid = g->extent(it_col, X_AXIS);
78 extents[d].unite (it->extent (it_col, X_AXIS));
79 if (d == RIGHT)
81 Grob * accs = Note_column::accidentals (it);
82 if (!accs)
83 accs = Note_column::accidentals (it->get_parent (X_AXIS));
85 if (accs)
87 Interval v =
88 Accidental_placement::get_relevant_accidental_extent (accs, it_col, me);
90 extents[d].unite (v);
95 if (extents[d].empty_b ())
96 extents[d] = Interval (0,0);
98 while (flip (&d) != LEFT);
102 We look at the width of the note head, since smaller heads get less space
103 eg. a quarter rest gets almost 0.5 ss less horizontal space than a note.
105 What is sticking out of the note head (eg. a flag), doesn't get
106 the full amount of space.
108 FIXED also includes the left part of the right object.
110 *fixed =
111 (left_head_wid.empty_b () ? increment :
113 Size of the head:
115 (left_head_wid[RIGHT]+
118 What's sticking out of the head, eg. a flag:
120 (extents[LEFT][RIGHT] - left_head_wid[RIGHT])/2))
123 What is sticking out of the right note:
125 + (extents[RIGHT].empty_b() ? 0.0 : - extents[RIGHT][LEFT] / 2);
128 We don't do complicated stuff: (base_space - increment) is the
129 normal amount of white, which also determines the amount of
130 stretch. Upon (extreme) stretching, notes with accidentals should
131 stretch as much as notes without accidentals.
133 *space = (base_space - increment) + *fixed ;
135 if (Item::breakable_b (right_col)
136 || right_col->original_)
139 This is for the situation
141 rest | 3/4 (eol)
144 *space += -extents[RIGHT][LEFT];
145 *fixed += -extents[RIGHT][LEFT];
148 stem_dir_correction (me, right_col, increment, space, fixed);
151 Item *
152 Note_spacing::left_column (Grob *me)
154 if (!me->live())
155 return 0;
157 return dynamic_cast<Item*> (me)->get_column ();
161 Compute the column of the right-items. This is a big function,
162 since RIGHT-ITEMS may span more columns (eg. if a clef if inserted,
163 this will add a new columns to RIGHT-ITEMS. Here we look at the
164 columns, and return the left-most. If there are multiple columns, we
165 prune RIGHT-ITEMS.
168 Item *
169 Note_spacing::right_column (Grob*me)
171 if (!me->live())
172 return 0;
174 SCM right = me->get_grob_property ("right-items");
175 Item *mincol = 0;
176 int min_rank = INT_MAX;
177 bool prune = false;
178 for (SCM s = right ; gh_pair_p (s) ; s = gh_cdr (s))
180 Item * ri = unsmob_item (gh_car (s));
182 Item * col = ri->get_column ();
183 int rank = Paper_column::get_rank (col);
185 if (rank < min_rank)
187 min_rank = rank;
188 if (mincol)
189 prune = true;
191 mincol = col;
195 if (prune)
197 // I'm a lazy bum. We could do this in-place.
198 SCM newright = SCM_EOL;
199 for (SCM s = right ; gh_pair_p (s) ; s =gh_cdr (s))
201 if (unsmob_item (gh_car (s))->get_column () == mincol)
202 newright = gh_cons (gh_car (s), newright);
205 me->set_grob_property ("right-items", newright);
208 if (!mincol)
211 int r = Paper_column::get_rank (dynamic_cast<Item*>(me)->get_column ());
212 programming_error (_f("Spacing wish column %d has no right item.", r));
215 return 0;
218 return mincol;
222 Correct for optical illusions. See [Wanske] p. 138. The combination
223 up-stem + down-stem should get extra space, the combination
224 down-stem + up-stem less.
226 TODO: have to check wether the stems are in the same staff.
229 void
230 Note_spacing::stem_dir_correction (Grob*me, Item * rcolumn,
231 Real increment,
232 Real * space, Real *fixed)
234 Drul_array<Direction> stem_dirs(CENTER,CENTER);
235 Drul_array<Interval> stem_posns;
236 Drul_array<Interval> head_posns;
237 Drul_array<SCM> props(me->get_grob_property ("left-items"),
238 me->get_grob_property ("right-items"));
240 Drul_array<Grob*> beams_drul(0,0);
241 Drul_array<Grob*> stems_drul(0,0);
243 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
244 Interval intersect;
245 Interval bar_xextent;
246 Interval bar_yextent;
248 bool correct_stem_dirs = true;
249 Direction d = LEFT;
250 bool acc_right = false;
254 for (SCM s = props[d]; gh_pair_p (s); s = gh_cdr (s))
256 Item * it= dynamic_cast<Item*> (unsmob_grob (gh_car(s)));
258 if (d == RIGHT)
259 acc_right = acc_right || Note_column::accidentals (it);
261 Grob *stem = Note_column::get_stem (it);
263 if (!stem || !stem->live ())
265 if (d == RIGHT && Separation_item::has_interface (it))
267 if (it->get_column () != rcolumn)
269 it = it->find_prebroken_piece (rcolumn->break_status_dir ());
272 Grob *last = Staff_spacing::extremal_break_aligned_grob (it, LEFT, &bar_xextent);
274 if (last)
275 bar_yextent = Staff_spacing::bar_y_positions (last);
277 break;
280 return ;
283 if(Stem::invisible_b (stem))
285 correct_stem_dirs = false;
286 continue;
289 stems_drul[d] = stem;
290 beams_drul[d] = Stem::get_beam (stem);
293 Direction sd = Stem::get_direction (stem);
294 if (stem_dirs[d] && stem_dirs[d] != sd)
296 correct_stem_dirs = false;
297 continue;
299 stem_dirs[d] = sd;
302 Correction doesn't seem appropriate when there is a large flag
303 hanging from the note.
305 if (d == LEFT
306 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
308 correct_stem_dirs = false;
311 Interval hp = Stem::head_positions (stem);
312 Real chord_start = hp[sd];
313 Real stem_end = Stem::stem_end_position (stem);
315 stem_posns[d] = Interval(chord_start<?stem_end, chord_start>? stem_end);
316 head_posns[d].unite (hp);
319 while (flip (&d) != LEFT);
323 don't correct if accidentals are sticking out of the right side.
325 if (acc_right)
326 return ;
328 Real correction = 0.0;
330 if (!bar_yextent.empty_b())
332 stem_dirs[RIGHT] = - stem_dirs[LEFT];
333 stem_posns[RIGHT] = bar_yextent;
336 if (correct_stem_dirs && stem_dirs[LEFT] *stem_dirs[RIGHT] == -1)
338 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
342 this is a knee: maximal correction.
344 Real note_head_width = increment;
345 Grob * st = stems_drul[RIGHT];
346 Grob * head = st ? Stem::support_head (st) : 0;
348 Interval head_extent;
349 if (head)
351 head_extent = head->extent (rcolumn, X_AXIS);
353 if (!head_extent.empty_b())
354 note_head_width = head_extent[RIGHT];
356 if (st)
358 Real thick = gh_scm2double (st->get_grob_property ("thickness"))
359 * st->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
361 note_head_width -= thick;
365 correction = note_head_width* stem_dirs[LEFT];
366 correction *= gh_scm2double (me->get_grob_property ("knee-spacing-correction"));
367 *fixed += correction;
369 else
371 intersect = stem_posns[LEFT];
372 intersect.intersect(stem_posns[RIGHT]);
373 correct_stem_dirs = correct_stem_dirs && !intersect.empty_b ();
375 if (correct_stem_dirs)
377 correction =abs (intersect.length ());
381 Ugh. 7 is hardcoded.
383 correction = (correction/7) <? 1.0;
384 correction *= stem_dirs[LEFT] ;
385 correction *=
386 gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
389 if (!bar_yextent.empty_b())
391 correction *= 0.5;
395 else if (correct_stem_dirs && stem_dirs[LEFT] *stem_dirs[RIGHT] == UP)
398 Correct for the following situation:
401 | |
403 | X |
404 | | |
405 ========
407 ^ move the center one to the left.
410 this effect seems to be much more subtle than the
411 stem-direction stuff (why?), and also does not scale with the
412 difference in stem length.
417 Interval hp = head_posns[LEFT];
418 hp.intersect (head_posns[RIGHT]);
419 if (!hp.empty_b())
420 return ;
422 Direction lowest =
423 (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
425 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP] ;
426 Real corr = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
427 corr = (delta <= 1) ? 0.0 : 0.25;
429 correction= -lowest * corr ;
432 *space += correction;
434 #if 0
435 /* there used to be a correction for bar_xextent() here, but
436 it's unclear what that was good for ?
438 #endif
445 ADD_INTERFACE (Note_spacing,"note-spacing-interface",
447 "left-items right-items stem-spacing-correction knee-spacing-correction");