X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fnote-spacing.cc;h=c4a79e403f7b18bb0313ce9a823ab481cf2cd8d3;hb=e3bd073b603167e110d3ecac4bfa3e1ca75e0106;hp=de08af4efa24fd4d97f761159e1321bd4a3d4a8f;hpb=78d515e9c342e1760b4e8a966dd77cb1cc0218d7;p=lilypond.git diff --git a/lily/note-spacing.cc b/lily/note-spacing.cc index de08af4efa..c4a79e403f 100644 --- a/lily/note-spacing.cc +++ b/lily/note-spacing.cc @@ -1,105 +1,118 @@ -/* +/* note-spacing.cc -- implement Note_spacing source file of the GNU LilyPond music typesetter - (c) 2001--2003 Han-Wen Nienhuys + (c) 2001--2007 Han-Wen Nienhuys */ +#include "note-spacing.hh" +#include "directional-element-interface.hh" +#include "grob-array.hh" #include "paper-column.hh" -#include "item.hh" #include "moment.hh" -#include "note-spacing.hh" -#include "grob.hh" #include "note-column.hh" #include "warn.hh" #include "stem.hh" #include "separation-item.hh" #include "staff-spacing.hh" #include "accidental-placement.hh" -#include "paper-def.hh" +#include "output-def.hh" +#include "pointer-group-interface.hh" + +/* + TODO: detect hshifts due to collisions, and account for them in + spacing? +*/ void -Note_spacing::get_spacing (Grob *me, Item* right_col, +Note_spacing::get_spacing (Grob *me, Item *right_col, Real base_space, Real increment, Real *space, Real *fixed) { - - Drul_array props(me->get_grob_property ("left-items"), - me->get_grob_property ("right-items")); + Drul_array props (me->get_object ("left-items"), + me->get_object ("right-items")); Direction d = LEFT; - Direction col_dir = right_col->break_status_dir (); + Direction col_dir = right_col->break_status_dir (); Drul_array extents; - Interval left_head_wid; + Interval left_head_wid; do { - for (SCM s = props[d]; gh_pair_p (s); s = gh_cdr (s)) + vector const &items (ly_scm2link_array (props [d])); + for (vsize i = items.size (); i--;) { - Item * it= dynamic_cast (unsmob_grob (gh_car(s))); - + Item *it = dynamic_cast (items[i]); + if (d == RIGHT && it->break_status_dir () != col_dir) - { - it = it -> find_prebroken_piece (col_dir); + it = it->find_prebroken_piece (col_dir); - } /* some kind of mismatch, eg. a note column, that is behind a linebreak. - */ + */ if (!it) - continue; + continue; Item *it_col = it->get_column (); if (d == RIGHT && right_col != it_col) continue; - + if (Separation_item::has_interface (it)) { extents[d].unite (Separation_item::width (it)); continue; } - if (d == LEFT) + if (d == LEFT + && Note_column::has_interface (it)) { - SCM r = it->get_grob_property ("rest"); - Grob * g = unsmob_grob (r); + SCM r = it->get_object ("rest"); + Grob *g = unsmob_grob (r); if (!g) - g = Note_column::first_head (it); + g = Note_column::first_head (it); /* Ugh. If Stem is switched off, we don't know what the first note head will be. - */ + */ if (g) - left_head_wid = g->extent(it_col, X_AXIS); + { + if (g->common_refpoint (it_col, X_AXIS) != it_col) + programming_error ("Note_spacing::get_spacing(): Common refpoint incorrect"); + else + left_head_wid = g->extent (it_col, X_AXIS); + } } - + extents[d].unite (it->extent (it_col, X_AXIS)); if (d == RIGHT) { - Grob * accs = Note_column::accidentals (it); + Grob *accs = Note_column::accidentals (it); if (!accs) accs = Note_column::accidentals (it->get_parent (X_AXIS)); - + if (accs) { - Interval v = - Accidental_placement::get_relevant_accidental_extent (accs, it_col, me); - + Interval v + = Accidental_placement::get_relevant_accidental_extent (accs, it_col, me); + extents[d].unite (v); } + + if (Grob *arpeggio = Note_column::arpeggio (it)) + extents[d].unite (arpeggio->extent (it_col, X_AXIS)); } } - if (extents[d].empty_b ()) - extents[d] = Interval (0,0); + if (extents[d].is_empty ()) + extents[d] = Interval (0, 0); } while (flip (&d) != LEFT); - /* We look at the width of the note head, since smaller heads get less space + eg. a quarter rest gets almost 0.5 ss less horizontal space than a note. What is sticking out of the note head (eg. a flag), doesn't get @@ -107,43 +120,61 @@ Note_spacing::get_spacing (Grob *me, Item* right_col, FIXED also includes the left part of the right object. */ - *fixed = - (left_head_wid.empty_b () ? increment : - /* - Size of the head: - */ - (left_head_wid[RIGHT]+ - - /* - What's sticking out of the head, eg. a flag: - */ - (extents[LEFT][RIGHT] - left_head_wid[RIGHT])/2)) + *fixed + = (left_head_wid.is_empty () ? increment + : /* + Size of the head: + */ + (left_head_wid[RIGHT]+ + + /* + What's sticking out of the head, eg. a flag: + */ + (extents[LEFT][RIGHT] - left_head_wid[RIGHT]) / 2)) /* - What is sticking out of the right note: - */ - + (extents[RIGHT].empty_b() ? 0.0 : - extents[RIGHT][LEFT] / 2); + What is sticking out on the left side of the right note: + */ + + (extents[RIGHT].is_empty () + ? 0.0 + : ((- extents[RIGHT][LEFT] / 2) + + /* + Add that which sticks out a lot. + */ + + max (0.0, -extents[RIGHT][LEFT] - (base_space - 0.5 * increment)))); /* We don't do complicated stuff: (base_space - increment) is the normal amount of white, which also determines the amount of stretch. Upon (extreme) stretching, notes with accidentals should stretch as much as notes without accidentals. - */ - *space = (base_space - increment) + *fixed ; + */ + *space = (base_space - increment) + *fixed; - if (Item::breakable_b (right_col) - || right_col->original_) +#if 0 + /* + The below situation is now handled by the "sticks out a lot" case + above. However we keep around the code for a few releases before + we drop it. + */ + if (!extents[RIGHT].is_empty () + && (Paper_column::is_breakable (right_col))) { /* This is for the situation rest | 3/4 (eol) - - */ - *space += -extents[RIGHT][LEFT]; - *fixed += -extents[RIGHT][LEFT]; + + Since we only take half of the right-object space above, the + barline will bump into the notes preceding it, if the right + thing is big. We add the rest of the extents here: + */ + + *space += -extents[RIGHT][LEFT] / 2; + *fixed += -extents[RIGHT][LEFT] / 2; } +#endif stem_dir_correction (me, right_col, increment, space, fixed); } @@ -151,35 +182,34 @@ Note_spacing::get_spacing (Grob *me, Item* right_col, Item * Note_spacing::left_column (Grob *me) { - if (!me->live()) + if (!me->is_live ()) return 0; - - return dynamic_cast (me)->get_column (); + + return dynamic_cast (me)->get_column (); } /* Compute the column of the right-items. This is a big function, -since RIGHT-ITEMS may span more columns (eg. if a clef if inserted, -this will add a new columns to RIGHT-ITEMS. Here we look at the -columns, and return the left-most. If there are multiple columns, we -prune RIGHT-ITEMS. - - */ + since RIGHT-ITEMS may span more columns (eg. if a clef if inserted, + this will add a new columns to RIGHT-ITEMS. Here we look at the + columns, and return the left-most. If there are multiple columns, we + prune RIGHT-ITEMS. +*/ Item * -Note_spacing::right_column (Grob*me) +Note_spacing::right_column (Grob *me) { - if (!me->live()) + if (!me->is_live ()) return 0; - - SCM right = me->get_grob_property ("right-items"); + + Grob_array *a = unsmob_grob_array (me->get_object ("right-items")); Item *mincol = 0; int min_rank = INT_MAX; bool prune = false; - for (SCM s = right ; gh_pair_p (s) ; s = gh_cdr (s)) + for (vsize i = 0; a && i < a->size (); i++) { - Item * ri = unsmob_item (gh_car (s)); + Item *ri = a->item (i); + Item *col = ri->get_column (); - Item * col = ri->get_column (); int rank = Paper_column::get_rank (col); if (rank < min_rank) @@ -191,29 +221,19 @@ Note_spacing::right_column (Grob*me) mincol = col; } } - - if (prune) + + if (prune && a) { - // I'm a lazy bum. We could do this in-place. - SCM newright = SCM_EOL; - for (SCM s = right ; gh_pair_p (s) ; s =gh_cdr (s)) + vector &right = a->array_reference (); + for (vsize i = right.size (); i--;) { - if (unsmob_item (gh_car (s))->get_column () == mincol) - newright = gh_cons (gh_car (s), newright); + if (dynamic_cast (right[i])->get_column () != mincol) + right.erase (right.begin () + i); } - - me->set_grob_property ("right-items", newright); } - - if (!mincol) - { - /* - int r = Paper_column::get_rank (dynamic_cast(me)->get_column ()); - programming_error (_f("Spacing wish column %d has no right item.", r)); - */ - return 0; - } + if (!mincol) + return 0; return mincol; } @@ -224,52 +244,50 @@ Note_spacing::right_column (Grob*me) down-stem + up-stem less. TODO: have to check wether the stems are in the same staff. - */ void -Note_spacing::stem_dir_correction (Grob*me, Item * rcolumn, +Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn, Real increment, - Real * space, Real *fixed) + Real *space, Real *fixed) { - Drul_array stem_dirs(CENTER,CENTER); + Drul_array stem_dirs (CENTER, CENTER); Drul_array stem_posns; - Drul_array head_posns; - Drul_array props(me->get_grob_property ("left-items"), - me->get_grob_property ("right-items")); + Drul_array head_posns; + Drul_array props (me->get_object ("left-items"), + me->get_object ("right-items")); + + Drul_array beams_drul (0, 0); + Drul_array stems_drul (0, 0); - Drul_array beams_drul(0,0); - Drul_array stems_drul(0,0); - stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER; Interval intersect; Interval bar_xextent; - Interval bar_yextent; - + Interval bar_yextent; + bool correct_stem_dirs = true; Direction d = LEFT; bool acc_right = false; - + do { - for (SCM s = props[d]; gh_pair_p (s); s = gh_cdr (s)) + vector const &items (ly_scm2link_array (props [d])); + for (vsize i = 0; i < items.size (); i++) { - Item * it= dynamic_cast (unsmob_grob (gh_car(s))); + Item *it = dynamic_cast (items[i]); if (d == RIGHT) acc_right = acc_right || Note_column::accidentals (it); - + Grob *stem = Note_column::get_stem (it); - if (!stem || !stem->live ()) + if (!stem || !stem->is_live ()) { if (d == RIGHT && Separation_item::has_interface (it)) { if (it->get_column () != rcolumn) - { - it = it->find_prebroken_piece (rcolumn->break_status_dir ()); - } - - Grob *last = Staff_spacing::extremal_break_aligned_grob (it, LEFT, &bar_xextent); + it = it->find_prebroken_piece (rcolumn->break_status_dir ()); + + Grob *last = Separation_item::extremal_break_aligned_grob (it, LEFT, &bar_xextent); if (last) bar_yextent = Staff_spacing::bar_y_positions (last); @@ -277,10 +295,10 @@ Note_spacing::stem_dir_correction (Grob*me, Item * rcolumn, break; } - return ; + return; } - - if(Stem::invisible_b (stem)) + + if (Stem::is_invisible (stem)) { correct_stem_dirs = false; continue; @@ -288,159 +306,166 @@ Note_spacing::stem_dir_correction (Grob*me, Item * rcolumn, stems_drul[d] = stem; beams_drul[d] = Stem::get_beam (stem); - - - Direction sd = Stem::get_direction (stem); - if (stem_dirs[d] && stem_dirs[d] != sd) + + Direction stem_dir = get_grob_direction (stem); + if (stem_dirs[d] && stem_dirs[d] != stem_dir) { correct_stem_dirs = false; continue; } - stem_dirs[d] = sd; + stem_dirs[d] = stem_dir; /* Correction doesn't seem appropriate when there is a large flag hanging from the note. - */ + */ if (d == LEFT - && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem)) + && Stem::duration_log (stem) > 2 && !Stem::get_beam (stem)) + correct_stem_dirs = false; + + Interval hp = Stem::head_positions (stem); + if (correct_stem_dirs + && !hp.is_empty ()) { - correct_stem_dirs = false; + Real chord_start = hp[stem_dir]; + + /* + can't look at stem-end-position, since that triggers + beam slope computations. + */ + Real stem_end = hp[stem_dir] + + stem_dir * robust_scm2double (stem->get_property ("length"), 7); + + stem_posns[d] = Interval (min (chord_start, stem_end), + max (chord_start, stem_end)); + head_posns[d].unite (hp); } - - Interval hp = Stem::head_positions (stem); - Real chord_start = hp[sd]; - Real stem_end = Stem::stem_end_position (stem); - - stem_posns[d] = Interval(chord_start? stem_end); - head_posns[d].unite (hp); } } while (flip (&d) != LEFT); - /* don't correct if accidentals are sticking out of the right side. */ if (acc_right) - return ; + return; Real correction = 0.0; - if (!bar_yextent.empty_b()) + if (!bar_yextent.is_empty ()) { - stem_dirs[RIGHT] = - stem_dirs[LEFT]; + stem_dirs[RIGHT] = -stem_dirs[LEFT]; stem_posns[RIGHT] = bar_yextent; + stem_posns[RIGHT] *= 2; } - - if (correct_stem_dirs && stem_dirs[LEFT] *stem_dirs[RIGHT] == -1) + + if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == -1) { if (beams_drul[LEFT] && beams_drul[LEFT] == beams_drul[RIGHT]) { - + /* this is a knee: maximal correction. */ Real note_head_width = increment; - Grob * st = stems_drul[RIGHT]; - Grob * head = st ? Stem::support_head (st) : 0; + Grob *st = stems_drul[RIGHT]; + Grob *head = st ? Stem::support_head (st) : 0; Interval head_extent; if (head) { head_extent = head->extent (rcolumn, X_AXIS); - if (!head_extent.empty_b()) + if (!head_extent.is_empty ()) note_head_width = head_extent[RIGHT]; if (st) { - Real thick = gh_scm2double (st->get_grob_property ("thickness")) - * st->get_paper ()->get_realvar (ly_symbol2scm ("linethickness")); + Real thick = Stem::thickness (st); note_head_width -= thick; } } - correction = note_head_width* stem_dirs[LEFT]; - correction *= gh_scm2double (me->get_grob_property ("knee-spacing-correction")); + correction = note_head_width * stem_dirs[LEFT]; + correction *= robust_scm2double (me->get_property ("knee-spacing-correction"), 0); *fixed += correction; } else { - intersect = stem_posns[LEFT]; - intersect.intersect(stem_posns[RIGHT]); - correct_stem_dirs = correct_stem_dirs && !intersect.empty_b (); + intersect = stem_posns[LEFT]; + intersect.intersect (stem_posns[RIGHT]); + correct_stem_dirs = correct_stem_dirs && !intersect.is_empty (); if (correct_stem_dirs) { - correction =abs (intersect.length ()); + correction = abs (intersect.length ()); - /* Ugh. 7 is hardcoded. */ - correction = (correction/7) get_grob_property ("stem-spacing-correction")); - } - - if (!bar_yextent.empty_b()) - { - correction *= 0.5; + correction = min (correction / 7, 1.0); + correction *= stem_dirs[LEFT]; + correction + *= robust_scm2double (me->get_property ("stem-spacing-correction"), 0); } + + if (!bar_yextent.is_empty ()) + correction *= 0.5; } } - else if (correct_stem_dirs && stem_dirs[LEFT] *stem_dirs[RIGHT] == UP) + else if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == UP) { /* Correct for the following situation: - X X - | | + X X + | | | | | X | | | | ======== - ^ move the center one to the left. - + ^ move the center one to the left. + this effect seems to be much more subtle than the stem-direction stuff (why?), and also does not scale with the difference in stem length. - - */ - + */ + Interval hp = head_posns[LEFT]; - hp.intersect (head_posns[RIGHT]); - if (!hp.empty_b()) - return ; - - Direction lowest = - (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT; - - Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP] ; - Real corr = gh_scm2double (me->get_grob_property ("stem-spacing-correction")); - corr = (delta <= 1) ? 0.0 : 0.25; - - correction= -lowest * corr ; + hp.intersect (head_posns[RIGHT]); + if (!hp.is_empty ()) + return; + + Direction lowest + = (head_posns[LEFT][DOWN] > head_posns[RIGHT][UP]) ? RIGHT : LEFT; + + Real delta = head_posns[-lowest][DOWN] - head_posns[lowest][UP]; + Real corr = robust_scm2double (me->get_property ("same-direction-correction"), 0); + + if (delta > 1) + correction = -lowest * corr; } *space += correction; - /* there used to be a correction for bar_xextent() here, but + /* there used to be a correction for bar_xextent () here, but it's unclear what that was good for ? */ - } - +ADD_INTERFACE (Note_spacing, + "This object calculates spacing wishes for individual voices.", + + "knee-spacing-correction " + "left-items " + "right-items " + "same-direction-correction " + "stem-spacing-correction " -ADD_INTERFACE (Note_spacing,"note-spacing-interface", - "", - "left-items right-items stem-spacing-correction knee-spacing-correction"); + );