* lily/property-iterator.cc (do_quit): add finalization functions
[lilypond.git] / lily / note-spacing.cc
blob7964a2966e0885e25739209655da14047de8df80
1 /*
2 note-spacing.cc -- implement Note_spacing
4 source file of the GNU LilyPond music typesetter
6 (c) 2001--2002 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"
22 void
23 Note_spacing::get_spacing (Grob *me, Item* right_col,
24 Real base_space, Real increment, Real *space, Real *fixed)
27 Drul_array<SCM> props(me->get_grob_property ("left-items"),
28 me->get_grob_property ("right-items"));
29 Direction d = LEFT;
30 Direction col_dir = right_col->break_status_dir ();
31 Drul_array<Interval> extents;
33 Interval left_head_wid;
36 for (SCM s = props[d]; gh_pair_p (s); s = gh_cdr (s))
38 Item * it= dynamic_cast<Item*> (unsmob_grob (gh_car(s)));
40 if (d == RIGHT && it->break_status_dir () != col_dir)
42 it = it -> find_prebroken_piece (col_dir);
46 some kind of mismatch, eg. a note column, that is behind a
47 linebreak.
49 if (!it)
50 continue;
52 Item *it_col = it->get_column ();
53 if (d == RIGHT && right_col != it_col)
54 continue;
56 if (Separation_item::has_interface (it))
58 extents[d].unite (Separation_item::width (it));
59 continue;
62 if (d == LEFT)
64 SCM r = it->get_grob_property ("rest");
65 Grob * g = unsmob_grob (r);
66 if (!g)
67 g = Note_column::first_head (it);
70 Ugh. If Stem is switched off, we don't know what the
71 first note head will be.
73 if (g)
74 left_head_wid = g->extent(it_col, X_AXIS);
77 extents[d].unite (it->extent (it_col, X_AXIS));
78 if (d == RIGHT)
80 Grob * accs = Note_column::accidentals (it);
81 if (!accs)
82 accs = Note_column::accidentals (it->get_parent (X_AXIS));
84 if (accs)
86 Interval v =
87 Accidental_placement::get_relevant_accidental_extent (accs, it_col, me);
89 extents[d].unite (v);
94 if (extents[d].empty_b ())
95 extents[d] = Interval (0,0);
97 while (flip (&d) != LEFT);
101 We look at the width of the note head, since smaller heads get less space
102 eg. a quarter rest gets almost 0.5 ss less horizontal space than a note.
104 What is sticking out of the note head (eg. a flag), doesn't get
105 the full amount of space.
107 *fixed = left_head_wid.empty_b () ? increment : left_head_wid[RIGHT];
108 *space = (base_space - increment) + *fixed +
109 (extents[LEFT][RIGHT]
110 - (left_head_wid.empty_b () ? 0.0 : left_head_wid[RIGHT]))/ 2;
113 if (*space - *fixed < 2 * ((- extents[RIGHT][LEFT]) >? 0))
117 What's sticking out at the left of the right side has less
118 influence. We only take it into account if there is not enough
119 space.
121 this sucks: this criterion is discontinuous; FIXME.
123 *space += 0.5 * (( -extents[RIGHT][LEFT]) >? 0);
126 stem_dir_correction (me, right_col, increment, space, fixed);
129 Item *
130 Note_spacing::left_column (Grob *me)
132 if (!me->live())
133 return 0;
135 return dynamic_cast<Item*> (me)->get_column ();
139 Compute the column of the right-items. This is a big function,
140 since RIGHT-ITEMS may span more columns (eg. if a clef if inserted,
141 this will add a new columns to RIGHT-ITEMS. Here we look at the
142 columns, and return the left-most. If there are multiple columns, we
143 prune RIGHT-ITEMS.
146 Item *
147 Note_spacing::right_column (Grob*me)
149 if (!me->live())
150 return 0;
152 SCM right = me->get_grob_property ("right-items");
153 Item *mincol = 0;
154 int min_rank = INT_MAX;
155 bool prune = false;
156 for (SCM s = right ; gh_pair_p (s) ; s = gh_cdr (s))
158 Item * ri = unsmob_item (gh_car (s));
160 Item * col = ri->get_column ();
161 int rank = Paper_column::get_rank (col);
163 if (rank < min_rank)
165 min_rank = rank;
166 if (mincol)
167 prune = true;
169 mincol = col;
173 if (prune)
175 // I'm a lazy bum. We could do this in-place.
176 SCM newright = SCM_EOL;
177 for (SCM s = right ; gh_pair_p (s) ; s =gh_cdr (s))
179 if (unsmob_item (gh_car (s))->get_column () == mincol)
180 newright = gh_cons (gh_car (s), newright);
183 me->set_grob_property ("right-items", newright);
186 if (!mincol)
189 int r = Paper_column::get_rank (dynamic_cast<Item*>(me)->get_column ());
190 programming_error (_f("Spacing wish column %d has no right item.", r));
193 return 0;
196 return mincol;
200 Correct for optical illusions. See [Wanske] p. 138. The combination
201 up-stem + down-stem should get extra space, the combination
202 down-stem + up-stem less.
204 TODO: have to check wether the stems are in the same staff.
207 void
208 Note_spacing::stem_dir_correction (Grob*me, Item * rcolumn,
209 Real increment,
210 Real * space, Real *fixed)
212 Drul_array<Direction> stem_dirs(CENTER,CENTER);
213 Drul_array<Interval> stem_posns;
214 Drul_array<Interval> head_posns;
215 Drul_array<SCM> props(me->get_grob_property ("left-items"),
216 me->get_grob_property ("right-items"));
218 Drul_array<Grob*> beams_drul(0,0);
219 Real correction = 0.0;
221 stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
222 Interval intersect;
223 Interval bar_xextent;
224 Interval bar_yextent;
226 bool correct = true;
227 Direction d = LEFT;
228 bool acc_right = false;
232 for (SCM s = props[d]; gh_pair_p (s); s = gh_cdr (s))
234 Item * it= dynamic_cast<Item*> (unsmob_grob (gh_car(s)));
236 if (d == RIGHT)
237 acc_right = acc_right || Note_column::accidentals (it);
239 Grob *stem = Note_column::get_stem (it);
241 if (!stem || !stem->live ())
243 if (d == RIGHT && Separation_item::has_interface (it))
245 if (it->get_column () != rcolumn)
247 it = it->find_prebroken_piece (rcolumn->break_status_dir ());
250 Grob *last = Staff_spacing::extremal_break_aligned_grob (it, LEFT, &bar_xextent);
252 if (last)
253 bar_yextent = Staff_spacing::bar_y_positions (last);
255 break;
258 return ;
261 if(Stem::invisible_b (stem))
263 return ;
266 beams_drul[d] = Stem::get_beam (stem);
269 Direction sd = Stem::get_direction (stem);
270 if (stem_dirs[d] && stem_dirs[d] != sd)
272 return ;
274 stem_dirs[d] = sd;
277 Correction doesn't seem appropriate when there is a large flag
278 hanging from the note.
280 if (d == LEFT
281 && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem))
284 return;
289 Interval hp = Stem::head_positions (stem);
290 Real chord_start = hp[sd];
291 Real stem_end = Stem::stem_end_position (stem);
293 stem_posns[d] = Interval(chord_start<?stem_end, chord_start>? stem_end);
294 head_posns[d].unite (hp);
297 while (flip (&d) != LEFT);
301 don't correct if accidentals are sticking out of the right side.
304 if (acc_right)
305 return ;
307 if (!bar_yextent.empty_b())
309 stem_dirs[RIGHT] = - stem_dirs[LEFT];
310 stem_posns[RIGHT] = bar_yextent;
313 if (correct &&stem_dirs[LEFT] *stem_dirs[RIGHT] == -1)
315 if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT])
318 this is a knee: maximal correction.
321 correction = increment* stem_dirs[LEFT];
322 correction *= gh_scm2double (me->get_grob_property ("knee-spacing-correction"));
323 *fixed += correction;
325 else
327 intersect = stem_posns[LEFT];
328 intersect.intersect(stem_posns[RIGHT]);
329 correct = correct && !intersect.empty_b ();
331 if (!correct)
332 return;
334 correction = abs (intersect.length ());
338 Ugh. 7 is hardcoded.
340 correction = (correction/7) <? 1.0;
341 correction *= stem_dirs[LEFT] ;
342 correction *= gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
344 if (!bar_yextent.empty_b())
346 correction *= 0.5;
350 else if (correct && stem_dirs[LEFT] *stem_dirs[RIGHT] == UP)
353 Correct for the following situation:
356 | |
358 | X |
359 | | |
360 ========
362 ^ move the center one to the left.
365 this effect seems to be much more subtle than the
366 stem-direction stuff (why?), and also does not scale with the
367 difference in stem length.
372 Interval hp = head_posns[LEFT];
373 hp.intersect (head_posns[RIGHT]);
374 if (!hp.empty_b())
375 return ;
377 Direction lowest =
378 (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT;
380 Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP] ;
381 Real corr = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
382 corr = (delta <= 1) ? 0.0 : 0.25;
384 correction= -lowest * corr ;
387 if (!bar_xextent.empty_b())
388 correction += - bar_xextent[LEFT];
390 *space += correction;
396 ADD_INTERFACE (Note_spacing,"note-spacing-interface",
398 "left-items right-items stem-spacing-correction knee-spacing-correction");