X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fnote-spacing.cc;h=1d0d21fbbb502e5ae977c0e90781cfe63233aca2;hb=refs%2Fheads%2Fdon%2Ffix_configure_ac;hp=61af354f4cb1ebc161c289437a86e0333156db2a;hpb=b80683cc94b0c22bbe3fccb94a9b2e23787fd10b;p=lilypond.git diff --git a/lily/note-spacing.cc b/lily/note-spacing.cc index 61af354f4c..1d0d21fbbb 100644 --- a/lily/note-spacing.cc +++ b/lily/note-spacing.cc @@ -1,26 +1,39 @@ /* - note-spacing.cc -- implement Note_spacing + This file is part of LilyPond, the GNU music typesetter. - source file of the GNU LilyPond music typesetter + Copyright (C) 2001--2012 Han-Wen Nienhuys - (c) 2001--2007 Han-Wen Nienhuys + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . */ #include "note-spacing.hh" +#include "accidental-placement.hh" +#include "bar-line.hh" #include "directional-element-interface.hh" #include "grob-array.hh" -#include "paper-column.hh" #include "moment.hh" #include "note-column.hh" -#include "warn.hh" -#include "stem.hh" +#include "output-def.hh" +#include "paper-column.hh" +#include "pointer-group-interface.hh" #include "separation-item.hh" #include "spacing-interface.hh" #include "staff-spacing.hh" -#include "accidental-placement.hh" -#include "output-def.hh" -#include "pointer-group-interface.hh" +#include "staff-symbol-referencer.hh" +#include "stem.hh" +#include "warn.hh" /* TODO: detect hshifts due to collisions, and account for them in @@ -29,31 +42,31 @@ Spring Note_spacing::get_spacing (Grob *me, Item *right_col, - Real base_space, Real increment) + Real base_space, Real increment) { - vector note_columns = Spacing_interface::left_note_columns (me); + vector note_columns = Spacing_interface::left_note_columns (me); Real left_head_end = 0; for (vsize i = 0; i < note_columns.size (); i++) { - SCM r = note_columns[i]->get_object ("rest"); - Grob *g = unsmob_grob (r); - Grob *col = note_columns[i]->get_column (); - - if (!g) - g = Note_column::first_head (note_columns[i]); - - /* - Ugh. If Stem is switched off, we don't know what the - first note head will be. - */ - if (g) - { - if (g->common_refpoint (col, X_AXIS) != col) - programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect"); - else - left_head_end = g->extent (col, X_AXIS)[RIGHT]; - } + SCM r = note_columns[i]->get_object ("rest"); + Grob *g = unsmob_grob (r); + Grob *col = note_columns[i]->get_column (); + + if (!g) + g = Note_column::first_head (note_columns[i]); + + /* + Ugh. If Stem is switched off, we don't know what the + first note head will be. + */ + if (g) + { + if (g->common_refpoint (col, X_AXIS) != col) + programming_error ("Note_spacing::get_spacing (): Common refpoint incorrect"); + else + left_head_end = g->extent (col, X_AXIS)[RIGHT]; + } } /* @@ -66,43 +79,140 @@ Note_spacing::get_spacing (Grob *me, Item *right_col, adjust things so there are no collisions. */ Drul_array skys = Spacing_interface::skylines (me, right_col); - Real min_dist = max (0.0, skys[LEFT].distance (skys[RIGHT])); - Real min_desired_space = left_head_end + (min_dist - left_head_end) / 2; - - /* if the right object sticks out a lot, include a bit of extra space. - But only for non-musical-columns; this shouldn't apply to accidentals */ - if (!Paper_column::is_musical (right_col)) - min_desired_space = max (min_desired_space, - left_head_end + LEFT * skys[RIGHT].max_height ()); - - Real ideal = base_space - increment + min_desired_space; + Real distance = skys[LEFT].distance (skys[RIGHT], robust_scm2double (right_col->get_property ("skyline-vertical-padding"), 0.0)); + Real min_dist = max (0.0, distance); + Real min_desired_space = left_head_end + (min_dist - left_head_end + base_space - increment) / 2; + Real ideal = base_space - increment + left_head_end; + + /* If we have a NonMusical column on the right, we measure the ideal distance + to the bar-line (if present), not the start of the column. */ + if (!Paper_column::is_musical (right_col) + && !skys[RIGHT].is_empty () + && to_boolean (me->get_property ("space-to-barline"))) + { + Grob *bar = Pointer_group_interface::find_grob (right_col, + ly_symbol2scm ("elements"), + Bar_line::non_empty_barline); + + if (bar) + { + Real shift = bar->extent (right_col, X_AXIS)[LEFT]; + ideal -= shift; + min_desired_space -= max (shift, 0.0); + } + else + ideal -= right_col->extent (right_col, X_AXIS)[RIGHT]; + } + ideal = max (ideal, min_desired_space); stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space); - Spring ret (ideal, min_dist); - ret.set_inverse_compress_strength (max (0.0, ideal - max (min_dist, min_desired_space))); + /* TODO: grace notes look bad when things are stretched. Should we increase + their stretch strength? */ + Spring ret (max (0.0, ideal), min_dist); + ret.set_inverse_compress_strength (max (0.0, ideal - min_desired_space)); ret.set_inverse_stretch_strength (max (0.1, base_space - increment)); return ret; } +static Real +knee_correction (Grob *note_spacing, Grob *right_stem, Real increment) +{ + Real note_head_width = increment; + Grob *head = right_stem ? Stem::support_head (right_stem) : 0; + Grob *rcolumn = dynamic_cast (head)->get_column (); + + Interval head_extent; + if (head) + { + head_extent = head->extent (rcolumn, X_AXIS); + + if (!head_extent.is_empty ()) + note_head_width = head_extent[RIGHT]; + + note_head_width -= Stem::thickness (right_stem); + } + + return -note_head_width * get_grob_direction (right_stem) + * robust_scm2double (note_spacing->get_property ("knee-spacing-correction"), 0); +} + +static Real +different_directions_correction (Grob *note_spacing, + Drul_array stem_posns, + Direction left_stem_dir) +{ + Real ret = 0.0; + Interval intersect = stem_posns[LEFT]; + intersect.intersect (stem_posns[RIGHT]); + + if (!intersect.is_empty ()) + { + ret = abs (intersect.length ()); + + /* + Ugh. 7 is hardcoded. + */ + ret = min (ret / 7, 1.0) + * left_stem_dir + * robust_scm2double (note_spacing->get_property ("stem-spacing-correction"), 0); + } + return ret; +} + +static Real +same_direction_correction (Grob *note_spacing, Drul_array head_posns) +{ + /* + Correct for the following situation: + + X X + | | + | | + | X | + | | | + ======== + + ^ move the center one to the left. -/** - Correct for optical illusions. See [Wanske] p. 138. The combination - up-stem + down-stem should get extra space, the combination - down-stem + up-stem less. - TODO: have to check whether the stems are in the same staff. + 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.is_empty ()) + return 0; + + 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 (note_spacing->get_property ("same-direction-correction"), 0); + + return (delta > 1) ? -lowest * corr : 0; +} + +/* + Correct for optical illusions. See [Wanske] p. 138. The combination + up-stem + down-stem should get extra space, the combination + down-stem + up-stem less. + + TODO: have to check whether the stems are in the same staff. */ void Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn, - Real increment, - Real *space, Real *fixed) + Real increment, + Real *space, Real *fixed) { Drul_array stem_dirs (CENTER, CENTER); Drul_array stem_posns; Drul_array head_posns; Drul_array props (me->get_object ("left-items"), - me->get_object ("right-items")); + me->get_object ("right-items")); Drul_array beams_drul (0, 0); Drul_array stems_drul (0, 0); @@ -112,91 +222,62 @@ Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn, Interval bar_xextent; Interval bar_yextent; - bool correct_stem_dirs = true; - Direction d = LEFT; bool acc_right = false; - do + Grob *bar = Spacing_interface::extremal_break_aligned_grob (me, RIGHT, + rcolumn->break_status_dir (), + &bar_xextent); + if (bar && dynamic_cast (bar)->get_column () == rcolumn) + bar_yextent = Staff_spacing::bar_y_positions (bar); + + for (LEFT_and_RIGHT (d)) { - vector const &items (ly_scm2link_array (props [d])); + vector const &items (ly_scm2link_array (props [d])); for (vsize i = 0; i < items.size (); i++) - { - 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->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 = Separation_item::extremal_break_aligned_grob (it, LEFT, &bar_xextent); - - if (last) - bar_yextent = Staff_spacing::bar_y_positions (last); - - break; - } - - return; - } - - if (Stem::is_invisible (stem)) - { - correct_stem_dirs = false; - continue; - } - - stems_drul[d] = stem; - beams_drul[d] = Stem::get_beam (stem); - - Direction stem_dir = get_grob_direction (stem); - if (stem_dirs[d] && stem_dirs[d] != stem_dir) - { - correct_stem_dirs = false; - continue; - } - 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)) - correct_stem_dirs = false; - - Interval hp = Stem::head_positions (stem); - if (correct_stem_dirs - && !hp.is_empty ()) - { - 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); - } - } + { + Item *it = dynamic_cast (items[i]); + if (!Note_column::has_interface (it)) + continue; + if (d == RIGHT && it->get_column () != rcolumn) + continue; + + /* + Find accidentals which are sticking out of the right side. + */ + if (d == RIGHT) + acc_right = acc_right || Note_column::accidentals (it); + + Grob *stem = Note_column::get_stem (it); + + if (!stem || !stem->is_live () || Stem::is_invisible (stem)) + return; + + stems_drul[d] = stem; + beams_drul[d] = Stem::get_beam (stem); + + Direction stem_dir = get_grob_direction (stem); + if (stem_dirs[d] && stem_dirs[d] != stem_dir) + return; + + 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)) + return; + + Interval hp = Stem::head_positions (stem); + if (!hp.is_empty ()) + { + Real ss = Staff_symbol_referencer::staff_space (stem); + stem_posns[d] = stem->pure_height (stem, 0, INT_MAX) * (2 / ss); + 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; Real correction = 0.0; @@ -207,92 +288,29 @@ Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn, stem_posns[RIGHT] *= 2; } - if (correct_stem_dirs && stem_dirs[LEFT] * stem_dirs[RIGHT] == -1) + if (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; - - Interval head_extent; - if (head) - { - head_extent = head->extent (rcolumn, X_AXIS); - - if (!head_extent.is_empty ()) - note_head_width = head_extent[RIGHT]; - - note_head_width -= Stem::thickness (st); - } - - correction = note_head_width * stem_dirs[LEFT]; - correction *= robust_scm2double (me->get_property ("knee-spacing-correction"), 0); - *fixed += correction; - } + { + correction = knee_correction (me, stems_drul[RIGHT], increment); + } else - { - 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 ()); - - /* - Ugh. 7 is hardcoded. - */ - 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) - { - /* - Correct for the following situation: - - X X - | | - | | - | X | - | | | - ======== - - ^ 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. + { + correction = different_directions_correction (me, stem_posns, stem_dirs[LEFT]); - */ - - Interval hp = head_posns[LEFT]; - 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; + if (!bar_yextent.is_empty ()) + correction *= 0.5; + } } + /* + Only apply same direction correction if there are no + accidentals sticking out of the right hand side. + */ + else if (stem_dirs[LEFT] * stem_dirs[RIGHT] == 1 + && !acc_right) + correction = same_direction_correction (me, head_posns); + *fixed += correction; *space += correction; /* there used to be a correction for bar_xextent () here, but @@ -301,14 +319,14 @@ Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn, } 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 " - - ); + "This object calculates spacing wishes for individual voices.", + + /* properties */ + "knee-spacing-correction " + "left-items " + "right-items " + "same-direction-correction " + "stem-spacing-correction " + "space-to-barline " + );