From: hanwen Date: Sun, 19 May 2002 10:37:05 +0000 (+0000) Subject: collision note head merge X-Git-Tag: release/1.5.59~37 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=406e5130734b889e4d517354455a60963b1577b7;p=lilypond.git collision note head merge --- diff --git a/ChangeLog b/ChangeLog index 4c62d7d994..887100f5b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2002-05-19 Han-Wen + + * lily/note-collision.cc (check_meshing_chords): move file from + collision.cc, implement merged note heads (there you go, Drarn :-) + + * input/regression/collision-heads.ly: new file + 2002-05-18 Juergen Reuter * mf/parmesan-heads.mf, scm/grob-description.scm, diff --git a/Documentation/regression-test.tely b/Documentation/regression-test.tely index a546114a64..775d1df6a5 100644 --- a/Documentation/regression-test.tely +++ b/Documentation/regression-test.tely @@ -248,6 +248,8 @@ Grace note do weird things with timing. Fragile. @lilypondfile[printfilename]{collision-dots.ly} +@lilypondfile[printfilename]{collision-heads.ly} + @lilypondfile[printfilename]{collision-mesh.ly} @lilypondfile[printfilename]{number-staff-lines.ly} diff --git a/Documentation/user/refman.itely b/Documentation/user/refman.itely index ea6a73ff37..b0baf887d7 100644 --- a/Documentation/user/refman.itely +++ b/Documentation/user/refman.itely @@ -1458,7 +1458,7 @@ Unbeamed eighth notes and shorter by default have a slash through the stem. @lilypond[fragment,verbatim] -\relative c'' { +\relative c'' \context Voice { \grace c8 c4 \grace { [c16 c16] } c4 \grace { \property Voice.Stem \override #'flag-style = #'() diff --git a/input/regression/collision-heads.ly b/input/regression/collision-heads.ly new file mode 100644 index 0000000000..04ef2f7326 --- /dev/null +++ b/input/regression/collision-heads.ly @@ -0,0 +1,29 @@ +\header { + texidoc = + + "If @code{merge-differently-headed}, then +open note heads may be merged with black noteheads, but only +if the black note heads are from 8th or shorter notes. +" + +} +\score { \notes \context Staff\relative c''< +\context Voice = VA { + \voiceOne + c2 c8 c4. + + \property Staff.NoteCollision \override #'merge-differently-headed = ##t + c2 + c4. c8 + c2 +} +\context Voice = VB { + \voiceTwo + c8 c4. + c2 + c8 c4. + c2 + c4 +} + > + } diff --git a/lily/accidental-placement.cc b/lily/accidental-placement.cc index f4576cb022..edefc20fb2 100644 --- a/lily/accidental-placement.cc +++ b/lily/accidental-placement.cc @@ -17,7 +17,7 @@ source file of the GNU LilyPond music typesetter #include "accidental-placement.hh" #include "note-column.hh" #include "group-interface.hh" -#include "collision.hh" +#include "note-collision.hh" MAKE_SCHEME_CALLBACK(Accidental_placement,extent_callback, 2); SCM @@ -206,7 +206,7 @@ Accidental_placement::position_accidentals (Grob * me) for (int i = note_cols.size() ; i--;) { Grob *c = note_cols[i]->get_parent (X_AXIS); - if (Collision::has_interface (c)) + if (Note_collision_interface::has_interface (c)) { Link_array gs = Pointer_group_interface__extract_grobs (c, (Grob*)0, "elements"); diff --git a/lily/collision-engraver.cc b/lily/collision-engraver.cc index 2b8c4923fe..8e8c0f4fa0 100644 --- a/lily/collision-engraver.cc +++ b/lily/collision-engraver.cc @@ -7,7 +7,7 @@ */ #include "note-column.hh" -#include "collision.hh" +#include "note-collision.hh" #include "engraver.hh" #include "axis-group-interface.hh" @@ -40,7 +40,7 @@ Collision_engraver::create_grobs () } for (int i=0; i< note_column_l_arr_.size (); i++) - Collision::add_column (col_p_,note_column_l_arr_[i]); + Note_collision_interface::add_column (col_p_,note_column_l_arr_[i]); } void diff --git a/lily/collision.cc b/lily/collision.cc deleted file mode 100644 index ee4b039f13..0000000000 --- a/lily/collision.cc +++ /dev/null @@ -1,383 +0,0 @@ -/* - collision.cc -- implement Collision - - source file of the GNU LilyPond music typesetter - - (c) 1997--2002 Han-Wen Nienhuys -*/ - -#include "debug.hh" -#include "collision.hh" -#include "note-column.hh" -#include "rhythmic-head.hh" -#include "paper-def.hh" -#include "axis-group-interface.hh" -#include "item.hh" -#include "stem.hh" - -MAKE_SCHEME_CALLBACK (Collision,force_shift_callback,2); - -SCM -Collision::force_shift_callback (SCM element_smob, SCM axis) -{ - Grob *me = unsmob_grob (element_smob); - Axis a = (Axis) gh_scm2int (axis); - assert (a == X_AXIS); - - me = me->get_parent (a); - - if (! to_boolean (me->get_grob_property ("collision-done"))) - { - me->set_grob_property ("collision-done", SCM_BOOL_T); - do_shifts (me); - } - - return gh_double2scm (0.0); -} - - -void -check_meshing_chords (Grob*me, - Drul_array< Array < Real > > *offsets, - Drul_array< Array < Slice > > const &extents, - Drul_array > const &clash_groups) - -{ - if (!extents[UP].size () || ! extents[DOWN].size ()) - return ; - - - Grob *cu =clash_groups[UP][0]; - Grob *cd =clash_groups[DOWN][0]; - - Grob * nu_l= Note_column::first_head (cu); - Grob * nd_l = Note_column::first_head (cd); - - - - /* - this case (distant half collide), - - | - x | - | x - | - - the noteheads may be closer than this case (close half collide) - - | - | - x - x - | - | - - */ - - bool close_half_collide = false; - bool distant_half_collide = false; - bool full_collide = false; - - /* - TODO: - - filter out the 'o's in this configuration, since they're no part - in the collision. - - | - x|o - x|o - x - - - */ - Array ups = Stem::note_head_positions (Note_column::stem_l (cu)); - Array dps = Stem::note_head_positions (Note_column::stem_l (cd)); - - /* - they're too far apart to collide. - - */ - - if (ups[0] > dps.top () + 1) - return ; - - bool touch = (ups[0] - dps.top () >= 0); - - bool merge_possible = (ups[0] >= dps[0]) && (ups.top () >= dps.top ()); - - merge_possible = merge_possible && - Rhythmic_head::balltype_i (nu_l) == Rhythmic_head::balltype_i (nd_l); - - - /* - don't merge whole notes (or longer, like breve, longa, maxima) - */ - merge_possible = merge_possible && (Rhythmic_head::balltype_i (nu_l) > 0); - - if (!to_boolean (me->get_grob_property ("merge-differently-dotted"))) - merge_possible = merge_possible && Rhythmic_head::dot_count (nu_l) == Rhythmic_head::dot_count (nd_l); - - int i = 0, j=0; - while (i < ups.size () && j < dps.size ()) - { - if (abs (ups[i] - dps[j]) == 1) - { - merge_possible = false; - if (ups[i] > dps[j]) - close_half_collide = true; - else - distant_half_collide = true; - } - else if (ups[i]==dps[j]) - full_collide = true; - else if (ups[i] >dps[0] && ups[i] < dps.top ()) - merge_possible = false; - else if (dps[j] >ups[0] && dps[j] < ups.top ()) - merge_possible = false; - - if (ups[i] < dps[j]) - i++; - else if (ups[i] > dps[j]) - j++; - else - { - i++; - j++; - } - } - - Drul_array center_note_shifts; - center_note_shifts[LEFT] = 0.0; - center_note_shifts[RIGHT] = 0.0; - - - Real shift_amount = 1; - - if (touch) - shift_amount *= -1; - - /* - for full collisions, the right hand head may obscure dots, so - make sure the dotted heads go to the right. - */ - if ((Rhythmic_head::dot_count (nu_l) > Rhythmic_head::dot_count (nd_l) - && full_collide)) - shift_amount = 1; - - /* - TODO: these numbers are magic; should devise a set of grob props - to tune this behavior. */ - - if (merge_possible) - shift_amount *= 0.0; - else if (close_half_collide && !touch) - shift_amount *= 0.52; - else if (distant_half_collide && !touch) - shift_amount *= 0.4; - else if (distant_half_collide || close_half_collide || full_collide) - shift_amount *= 0.5; - - /* - we're meshing. - */ - else if (Rhythmic_head::dot_count (nu_l) || Rhythmic_head::dot_count (nd_l)) - shift_amount *= 0.1; - else - shift_amount *= 0.25; - - Direction d = UP; - do - { - for (int i=0; i < clash_groups[d].size (); i++) - (*offsets)[d][i] += d * shift_amount; - } - while ((flip (&d))!= UP); -} - - -/* - TODO: make callback of this. - - TODO: - - note-width is hardcoded, making it difficult to handle all note - heads sanely. We should really look at the widths of the colliding - columns, and have a separate setting for "align stems". - - - */ -void -Collision::do_shifts (Grob* me) -{ - SCM autos (automatic_shift (me)); - SCM hand (forced_shift (me)); - - Link_array done; - - - Real wid - = gh_scm2double (me->get_grob_property ("note-width")); - - for (; gh_pair_p (hand); hand =ly_cdr (hand)) - { - Grob * s = unsmob_grob (ly_caar (hand)); - Real amount = gh_scm2double (ly_cdar (hand)); - - s->translate_axis (amount *wid, X_AXIS); - done.push (s); - } - for (; gh_pair_p (autos); autos =ly_cdr (autos)) - { - Grob * s = unsmob_grob (ly_caar (autos)); - Real amount = gh_scm2double (ly_cdar (autos)); - - if (!done.find_l (s)) - s->translate_axis (amount * wid, X_AXIS); - } -} - -/** This complicated routine moves note columns around horizontally to - ensure that notes don't clash. - - This should be put into Scheme. - */ -SCM -Collision::automatic_shift (Grob *me) -{ - Drul_array > clash_groups; - Drul_array > shifts; - SCM tups = SCM_EOL; - - SCM s = me->get_grob_property ("elements"); - for (; gh_pair_p (s); s = ly_cdr (s)) - { - SCM car = ly_car (s); - - Grob * se = unsmob_grob (car); - if (Note_column::has_interface (se)) - clash_groups[Note_column::dir (se)].push (se); - } - - - Direction d = UP; - do - { - Array & shift (shifts[d]); - Link_array & clashes (clash_groups[d]); - - clashes.sort (Note_column::shift_compare); - - for (int i=0; i < clashes.size (); i++) - { - SCM sh - = clashes[i]->get_grob_property ("horizontal-shift"); - - if (gh_number_p (sh)) - shift.push (gh_scm2int (sh)); - else - shift.push (0); - } - - for (int i=1; i < shift.size (); i++) - { - if (shift[i-1] == shift[i]) - { - me->warning (_ ("Too many clashing notecolumns. Ignoring them.")); - return tups; - } - } - } - while ((flip (&d))!= UP); - - Drul_array< Array < Slice > > extents; - Drul_array< Array < Real > > offsets; - d = UP; - do - { - for (int i=0; i < clash_groups[d].size (); i++) - { - Slice s (Note_column::head_positions_interval (clash_groups[d][i])); - s[LEFT] --; - s[RIGHT]++; - extents[d].push (s); - offsets[d].push (d * 0.5 * i); - } - } - while ((flip (&d))!= UP); - - /* - do horizontal shifts of each direction - - | - x|| - x|| - x| - */ - - do - { - for (int i=1; i < clash_groups[d].size (); i++) - { - Slice prev =extents[d][i-1]; - prev.intersect (extents[d][i]); - if (prev.length ()> 0 || - (extents[-d].size () && d * (extents[d][i][-d] - extents[-d][0][d]) < 0)) - for (int j = i; j < clash_groups[d].size (); j++) - offsets[d][j] += d * 0.5; - } - } - while ((flip (&d))!= UP); - - - /* - Check if chords are meshing - */ - - check_meshing_chords (me, &offsets, extents, clash_groups); - - do - { - for (int i=0; i < clash_groups[d].size (); i++) - tups = gh_cons (gh_cons (clash_groups[d][i]->self_scm (), gh_double2scm (offsets[d][i])), - tups); - } - while (flip (&d) != UP); - return tups; -} - - -SCM -Collision::forced_shift (Grob *me) -{ - SCM tups = SCM_EOL; - - SCM s = me->get_grob_property ("elements"); - for (; gh_pair_p (s); s = ly_cdr (s)) - { - Grob * se = unsmob_grob (ly_car (s)); - - SCM force = se->remove_grob_property ("force-hshift"); - if (gh_number_p (force)) - { - tups = gh_cons (gh_cons (se->self_scm (), force), - tups); - } - } - return tups; -} - -void -Collision::add_column (Grob*me,Grob* ncol_l) -{ - ncol_l->add_offset_callback (Collision::force_shift_callback_proc, X_AXIS); - Axis_group_interface::add_element (me, ncol_l); - me->add_dependency (ncol_l); -} - - -ADD_INTERFACE (Collision, "note-collision-interface", - "An object that handles collisions between notes with different -stem directions and horizontal shifts. Most of the interesting -properties are to be set in @ref{note-column-interface}", - "merge-differently-dotted note-width collision-done"); diff --git a/lily/include/collision.hh b/lily/include/collision.hh deleted file mode 100644 index 5704ca9789..0000000000 --- a/lily/include/collision.hh +++ /dev/null @@ -1,38 +0,0 @@ -/* - collision.hh -- declare Collision - - source file of the GNU LilyPond music typesetter - - (c) 1997--2002 Han-Wen Nienhuys -*/ - - -#ifndef COLLISION_HH -#define COLLISION_HH - -#include "lily-proto.hh" -#include "lily-guile.hh" - - -/** - Resolve conflicts between various Note_columns (chords). - - TODO - - * multistaff support (see Chlapik: equal noteheads should be on the - same hpos.) - - * Make interface of this, similar to align-interface. - -*/ -class Collision // interface -{ -public: - static SCM automatic_shift (Grob*); - static SCM forced_shift (Grob*); - DECLARE_SCHEME_CALLBACK (force_shift_callback, (SCM element, SCM axis)); - static void do_shifts (Grob*); - static void add_column (Grob*me,Grob*ncol_l); - static bool has_interface(Grob*); -}; -#endif // COLLISION_HH diff --git a/lily/include/note-collision.hh b/lily/include/note-collision.hh new file mode 100644 index 0000000000..e8674d9aeb --- /dev/null +++ b/lily/include/note-collision.hh @@ -0,0 +1,40 @@ +/* + collision.hh -- declare Collision + + source file of the GNU LilyPond music typesetter + + (c) 1997--2002 Han-Wen Nienhuys +*/ + + +#ifndef COLLISION_HH +#define COLLISION_HH + +#include "lily-proto.hh" +#include "lily-guile.hh" + + +/** + Resolve conflicts between various Note_columns (chords). + + TODO + + * multistaff support (see Chlapik: equal noteheads should be on the + same hpos.) + + * Make interface of this, similar to align-interface. + +*/ +class Note_collision_interface +{ +public: + static SCM automatic_shift (Grob*, Drul_array< Link_array >); + static SCM forced_shift (Grob*); + + static Drul_array< Link_array > get_clash_groups (Grob *me); + DECLARE_SCHEME_CALLBACK (force_shift_callback, (SCM element, SCM axis)); + static void do_shifts (Grob*); + static void add_column (Grob*me,Grob*ncol_l); + static bool has_interface(Grob*); +}; +#endif // COLLISION_HH diff --git a/lily/include/note-head.hh b/lily/include/note-head.hh index 55a9aa0b5a..aba379dec6 100644 --- a/lily/include/note-head.hh +++ b/lily/include/note-head.hh @@ -27,7 +27,7 @@ public: DECLARE_SCHEME_CALLBACK (brew_ez_molecule, (SCM )); static bool has_interface (Grob*); static Real stem_attachment_coordinate (Grob *, Axis a); - + static int balltype_i (Grob*) ; }; #endif // NOTEHEAD_HH diff --git a/lily/include/rhythmic-head.hh b/lily/include/rhythmic-head.hh index af4af02ae8..1da23356b1 100644 --- a/lily/include/rhythmic-head.hh +++ b/lily/include/rhythmic-head.hh @@ -16,7 +16,7 @@ class Rhythmic_head { public: - static int balltype_i (Grob*) ; + static int duration_log (Grob*); static void set_dots (Grob*,Item *); static Item * stem_l (Grob*) ; static Item * dots_l (Grob*) ; diff --git a/lily/mensural-ligature-engraver.cc b/lily/mensural-ligature-engraver.cc index 9adb710d8f..9be0e8a0cf 100644 --- a/lily/mensural-ligature-engraver.cc +++ b/lily/mensural-ligature-engraver.cc @@ -340,7 +340,7 @@ Mensural_ligature_engraver::transform_heads () have_last_pitch = have_pitch; Grob_info info = primitives_arr_[i]; int duration_log = - Rhythmic_head::balltype_i (dynamic_cast (info.grob_l_)); + Note_head::balltype_i (dynamic_cast (info.grob_l_)); Note_req *nr = dynamic_cast (info.music_cause ()); if (!nr) { diff --git a/lily/note-collision.cc b/lily/note-collision.cc new file mode 100644 index 0000000000..fa977fda00 --- /dev/null +++ b/lily/note-collision.cc @@ -0,0 +1,431 @@ + +/* + collision.cc -- implement Collision + + source file of the GNU LilyPond music typesetter + + (c) 1997--2002 Han-Wen Nienhuys +*/ + +#include "debug.hh" +#include "note-collision.hh" +#include "note-column.hh" +#include "note-head.hh" +#include "rhythmic-head.hh" +#include "paper-def.hh" +#include "axis-group-interface.hh" +#include "item.hh" +#include "stem.hh" + +MAKE_SCHEME_CALLBACK (Note_collision_interface,force_shift_callback,2); + +SCM +Note_collision_interface::force_shift_callback (SCM element_smob, SCM axis) +{ + Grob *me = unsmob_grob (element_smob); + Axis a = (Axis) gh_scm2int (axis); + assert (a == X_AXIS); + + me = me->get_parent (a); + + if (! to_boolean (me->get_grob_property ("collision-done"))) + { + me->set_grob_property ("collision-done", SCM_BOOL_T); + do_shifts (me); + } + + return gh_double2scm (0.0); +} + + +void +check_meshing_chords (Grob*me, + Drul_array< Array < Real > > *offsets, + Drul_array< Array < Slice > > const &extents, + Drul_array > const &clash_groups) + +{ + if (!extents[UP].size () || ! extents[DOWN].size ()) + return ; + + + Grob *cu =clash_groups[UP][0]; + Grob *cd =clash_groups[DOWN][0]; + + Grob * nu_l= Note_column::first_head (cu); + Grob * nd_l = Note_column::first_head (cd); + + + + /* + this case (distant half collide), + + | + x | + | x + | + + the noteheads may be closer than this case (close half collide) + + | + | + x + x + | + | + + */ + + bool close_half_collide = false; + bool distant_half_collide = false; + bool full_collide = false; + + /* + TODO: + + filter out the 'o's in this configuration, since they're no part + in the collision. + + | + x|o + x|o + x + + + */ + Array ups = Stem::note_head_positions (Note_column::stem_l (cu)); + Array dps = Stem::note_head_positions (Note_column::stem_l (cd)); + + /* + they're too far apart to collide. + + */ + + if (ups[0] > dps.top () + 1) + return ; + + bool touch = (ups[0] - dps.top () >= 0); + + bool merge_possible = (ups[0] >= dps[0]) && (ups.top () >= dps.top ()); + + /* + don't merge whole notes (or longer, like breve, longa, maxima) + */ + + int upball_type = Note_head::balltype_i (nu_l); + int dnball_type = Note_head::balltype_i (nd_l); + + merge_possible = merge_possible && (upball_type > 0); + + if (!to_boolean (me->get_grob_property ("merge-differently-dotted"))) + merge_possible = merge_possible && Rhythmic_head::dot_count (nu_l) == Rhythmic_head::dot_count (nd_l); + + + if (!to_boolean (me->get_grob_property ("merge-differently-headed"))) + merge_possible = merge_possible && + upball_type == dnball_type; + else + /* + Can't merge quarter and half notes. + */ + merge_possible = merge_possible && + !((Rhythmic_head::duration_log (nu_l) == 1 + && Rhythmic_head::duration_log (nd_l) == 2) + ||(Rhythmic_head::duration_log (nu_l) == 2 + && Rhythmic_head::duration_log (nd_l) == 1)); + + int i = 0, j=0; + while (i < ups.size () && j < dps.size ()) + { + if (abs (ups[i] - dps[j]) == 1) + { + merge_possible = false; + if (ups[i] > dps[j]) + close_half_collide = true; + else + distant_half_collide = true; + } + else if (ups[i]==dps[j]) + full_collide = true; + else if (ups[i] >dps[0] && ups[i] < dps.top ()) + merge_possible = false; + else if (dps[j] >ups[0] && dps[j] < ups.top ()) + merge_possible = false; + + if (ups[i] < dps[j]) + i++; + else if (ups[i] > dps[j]) + j++; + else + { + i++; + j++; + } + } + + Drul_array center_note_shifts; + center_note_shifts[LEFT] = 0.0; + center_note_shifts[RIGHT] = 0.0; + + + Real shift_amount = 1; + + if (touch) + shift_amount *= -1; + + /* + for full collisions, the right hand head may obscure dots, so + make sure the dotted heads go to the right. + */ + if ((Rhythmic_head::dot_count (nu_l) > Rhythmic_head::dot_count (nd_l) + && full_collide)) + shift_amount = 1; + + /* + TODO: these numbers are magic; should devise a set of grob props + to tune this behavior. */ + + if (merge_possible) + { + shift_amount *= 0.0; + Grob *wipe_ball = 0; + + if (upball_type < dnball_type) + wipe_ball = nd_l; + else if (upball_type > dnball_type) + wipe_ball = nu_l; + + if (wipe_ball) + { + wipe_ball->set_grob_property ("transparent", SCM_BOOL_T); + wipe_ball->set_grob_property ("molecule", SCM_EOL); + } + } + else if (close_half_collide && !touch) + shift_amount *= 0.52; + else if (distant_half_collide && !touch) + shift_amount *= 0.4; + else if (distant_half_collide || close_half_collide || full_collide) + shift_amount *= 0.5; + + /* + we're meshing. + */ + else if (Rhythmic_head::dot_count (nu_l) || Rhythmic_head::dot_count (nd_l)) + shift_amount *= 0.1; + else + shift_amount *= 0.25; + + Direction d = UP; + do + { + for (int i=0; i < clash_groups[d].size (); i++) + (*offsets)[d][i] += d * shift_amount; + } + while ((flip (&d))!= UP); +} + +void +Note_collision_interface::do_shifts (Grob* me) +{ + Drul_array< Link_array > cg = get_clash_groups (me); + + SCM autos (automatic_shift (me, cg)); + SCM hand (forced_shift (me)); + + + + Direction d = UP; + Real wid = 0.0; + do + { + if(cg[d].size()) + { + Grob *h = cg[d][0]; + wid = Note_column::first_head(h)->extent(h,X_AXIS).length() ; + } + } + + while (flip (&d) != UP); + + + Link_array done; + for (; gh_pair_p (hand); hand =ly_cdr (hand)) + { + Grob * s = unsmob_grob (ly_caar (hand)); + Real amount = gh_scm2double (ly_cdar (hand)); + + s->translate_axis (amount *wid, X_AXIS); + done.push (s); + } + for (; gh_pair_p (autos); autos =ly_cdr (autos)) + { + Grob * s = unsmob_grob (ly_caar (autos)); + Real amount = gh_scm2double (ly_cdar (autos)); + + if (!done.find_l (s)) + s->translate_axis (amount * wid, X_AXIS); + } +} + +Drul_array< Link_array > +Note_collision_interface::get_clash_groups (Grob *me) +{ + Drul_array > clash_groups; + + SCM s = me->get_grob_property ("elements"); + for (; gh_pair_p (s); s = ly_cdr (s)) + { + SCM car = ly_car (s); + + Grob * se = unsmob_grob (car); + if (Note_column::has_interface (se)) + clash_groups[Note_column::dir (se)].push (se); + } + + Direction d = UP; + do + { + Link_array & clashes (clash_groups[d]); + clashes.sort (Note_column::shift_compare); + } + while ((flip (&d))!= UP); + + return clash_groups; +} + +/** This complicated routine moves note columns around horizontally to + ensure that notes don't clash. + + This should be put into Scheme. + */ +SCM +Note_collision_interface::automatic_shift (Grob *me, + Drul_array< Link_array > + clash_groups) +{ + Drul_array > shifts; + SCM tups = SCM_EOL; + + + Direction d = UP; + do + { + Array & shift (shifts[d]); + Link_array & clashes (clash_groups[d]); + + for (int i=0; i < clashes.size (); i++) + { + SCM sh + = clashes[i]->get_grob_property ("horizontal-shift"); + + if (gh_number_p (sh)) + shift.push (gh_scm2int (sh)); + else + shift.push (0); + } + + for (int i=1; i < shift.size (); i++) + { + if (shift[i-1] == shift[i]) + { + me->warning (_ ("Too many clashing notecolumns. Ignoring them.")); + return tups; + } + } + } + while ((flip (&d))!= UP); + + Drul_array< Array < Slice > > extents; + Drul_array< Array < Real > > offsets; + d = UP; + do + { + for (int i=0; i < clash_groups[d].size (); i++) + { + Slice s (Note_column::head_positions_interval (clash_groups[d][i])); + s[LEFT] --; + s[RIGHT]++; + extents[d].push (s); + offsets[d].push (d * 0.5 * i); + } + } + while ((flip (&d))!= UP); + + /* + do horizontal shifts of each direction + + | + x|| + x|| + x| + */ + + do + { + for (int i=1; i < clash_groups[d].size (); i++) + { + Slice prev =extents[d][i-1]; + prev.intersect (extents[d][i]); + if (prev.length ()> 0 || + (extents[-d].size () && d * (extents[d][i][-d] - extents[-d][0][d]) < 0)) + for (int j = i; j < clash_groups[d].size (); j++) + offsets[d][j] += d * 0.5; + } + } + while ((flip (&d))!= UP); + + + /* + Check if chords are meshing + */ + + check_meshing_chords (me, &offsets, extents, clash_groups); + + do + { + for (int i=0; i < clash_groups[d].size (); i++) + tups = gh_cons (gh_cons (clash_groups[d][i]->self_scm (), gh_double2scm (offsets[d][i])), + tups); + } + while (flip (&d) != UP); + return tups; +} + + +SCM +Note_collision_interface::forced_shift (Grob *me) +{ + SCM tups = SCM_EOL; + + SCM s = me->get_grob_property ("elements"); + for (; gh_pair_p (s); s = ly_cdr (s)) + { + Grob * se = unsmob_grob (ly_car (s)); + + SCM force = se->remove_grob_property ("force-hshift"); + if (gh_number_p (force)) + { + tups = gh_cons (gh_cons (se->self_scm (), force), + tups); + } + } + return tups; +} + +void +Note_collision_interface::add_column (Grob*me,Grob* ncol_l) +{ + ncol_l->add_offset_callback (Note_collision_interface::force_shift_callback_proc, X_AXIS); + Axis_group_interface::add_element (me, ncol_l); + me->add_dependency (ncol_l); +} + + +ADD_INTERFACE (Note_collision_interface, "note-collision-interface", + "An object that handles collisions between notes with different stem +directions and horizontal shifts. Most of the interesting properties +are to be set in @ref{note-column-interface}: these are +@code{force-hshift} and @{horizontal-shift}. +", + "merge-differently-dotted merge-differently-headed collision-done"); diff --git a/lily/note-head.cc b/lily/note-head.cc index 01ecae6a33..1967691f0e 100644 --- a/lily/note-head.cc +++ b/lily/note-head.cc @@ -121,7 +121,7 @@ internal_brew_molecule (Grob *me, bool ledger_take_space) UGH: use grob-property. */ - SCM log = gh_int2scm (Rhythmic_head::balltype_i (me)); + SCM log = gh_int2scm (Note_head::balltype_i (me)); SCM exp = scm_list_n (ly_symbol2scm ("find-notehead-symbol"), log, ly_quote_scm (style), SCM_UNDEFINED); @@ -191,7 +191,7 @@ SCM Note_head::brew_ez_molecule (SCM smob) { Grob *me = unsmob_grob (smob); - int l = Rhythmic_head::balltype_i (me); + int l = Note_head::balltype_i (me); int b = (l >= 2); @@ -246,6 +246,15 @@ Note_head::stem_attachment_coordinate (Grob *me, Axis a) return gh_number_p (result) ? gh_scm2double (result) : 0.0; } + +int +Note_head::balltype_i (Grob*me) +{ + SCM s = me->get_grob_property ("duration-log"); + return gh_number_p (s) ? gh_scm2int (s) get_grob_property ("duration-log"); - return gh_number_p (s) ? gh_scm2int (s) get_grob_property ("duration-log"); + return gh_number_p (s) ? gh_scm2int (s) : 0; +} + ADD_INTERFACE (Rhythmic_head,"rhythmic-head-interface", diff --git a/lily/stem.cc b/lily/stem.cc index 7b8db24062..2b1ad6440a 100644 --- a/lily/stem.cc +++ b/lily/stem.cc @@ -122,7 +122,7 @@ Stem::set_stemend (Grob*me, Real se) int Stem::type_i (Grob*me) { - return first_head (me) ? Rhythmic_head::balltype_i (first_head (me)) : 2; + return first_head (me) ? Note_head::balltype_i (first_head (me)) : 2; } /* @@ -238,7 +238,7 @@ Stem::add_head (Grob*me, Grob *n) bool Stem::invisible_b (Grob*me) { - return ! (head_count (me) && Rhythmic_head::balltype_i (support_head (me)) >= 1); + return ! (head_count (me) && Note_head::balltype_i (support_head (me)) >= 1); } Direction diff --git a/scm/grob-property-description.scm b/scm/grob-property-description.scm index 22fc2fc0d2..7f5e9b11cc 100644 --- a/scm/grob-property-description.scm +++ b/scm/grob-property-description.scm @@ -223,7 +223,17 @@ and will have no effect. (grob-property-description 'measure-length moment? "Length of a measure. Used in some spacing situations.") (grob-property-description 'measure-count integer? "number of measures for a multimeasure rest.") -(grob-property-description 'merge-differently-dotted boolean? " Merge noteheads in collisions, even if they have a different number of dots. This normal notation for some types of polyphonic music. The value of this setting is used by @ref{note-collision-interface} .") + +(grob-property-description 'merge-differently-headed boolean? "Merge +noteheads in collisions, even if they have different note heads. The +smaller of the two heads will be rendered invisible. This used +polyphonic guitar notation. The value of this setting is used by +@ref{note-collision-interface} .") + +(grob-property-description 'merge-differently-dotted boolean? " Merge +noteheads in collisions, even if they have a different number of +dots. This normal notation for some types of polyphonic music. The +value of this setting is used by @ref{note-collision-interface} .") (grob-property-description 'meta list? "Alist of meta information of this grob.") (grob-property-description 'minimum-distance number? "minimum distance between notes and rests.") @@ -254,7 +264,6 @@ FIXME: also pair? (cons LEFT RIGHT) (grob-property-description 'no-spacing-rods boolean? "read from grobs: boolean that makes Separation_item ignore this item (MOVE ME TO ITEM).") (grob-property-description 'no-stem-extend boolean? "should stem not be extended to middle staff line?.") (grob-property-description 'non-default boolean? "not set because of existence of a bar?.") -(grob-property-description 'note-width number? "unit for horizontal translation, measured in staff-space.") (grob-property-description 'note-heads list? "List of note head grobs") (grob-property-description 'number-threshold number? "only put numbers bigger than this threshold over multi measuer rest.") (grob-property-description 'old-accidentals list? "list of (pitch, accidental) pairs.") diff --git a/scripts/lilypond-book.py b/scripts/lilypond-book.py index cd0191b0c7..aaa1f57370 100644 --- a/scripts/lilypond-book.py +++ b/scripts/lilypond-book.py @@ -1246,6 +1246,7 @@ def compile_all_files (chunks): texfiles = string.join (tex, ' ') cmd = 'lilypond --header=texidoc %s %s %s' \ % (lilyopts, g_extra_opts, texfiles) + system (cmd) #