]> git.donarmstrong.com Git - lilypond.git/commitdiff
Merge branch 'lilypond/translation' into staging
authorFrancisco Vila <francisco.vila@hispalinux.es>
Fri, 25 Nov 2011 12:14:21 +0000 (13:14 +0100)
committerFrancisco Vila <francisco.vila@hispalinux.es>
Fri, 25 Nov 2011 12:14:21 +0000 (13:14 +0100)
38 files changed:
Documentation/contributor/programming-work.itexi
Documentation/extending/programming-interface.itely
input/regression/beam-multiplicity-over-rests.ly
input/regression/beamlet-point-toward-beat.ly [new file with mode: 0644]
input/regression/beamlet-test.ly [new file with mode: 0644]
input/regression/span-bar-spacing.ly
lily/auto-beam-engraver.cc
lily/axis-group-interface.cc
lily/bar-line.cc
lily/beam-engraver.cc
lily/beaming-pattern.cc
lily/dot-column.cc
lily/footnote-engraver.cc
lily/grob.cc
lily/include/axis-group-interface.hh
lily/include/beaming-pattern.hh
lily/include/grob.hh
lily/include/lily-parser.hh
lily/include/pure-from-neighbor-interface.hh
lily/include/span-bar.hh
lily/include/system.hh
lily/lily-parser.cc
lily/page-layout-problem.cc
lily/pure-from-neighbor-engraver.cc
lily/pure-from-neighbor-interface.cc
lily/semi-tie-column.cc
lily/span-bar-engraver.cc
lily/span-bar-stub-engraver.cc
lily/span-bar.cc
lily/staff-symbol-referencer-scheme.cc
lily/stem.cc
lily/system.cc
lily/tie-column.cc
ly/engraver-init.ly
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/define-music-properties.scm
scm/output-lib.scm

index 0e0d3c723e0c0cc31242df27356a56936ea96537..31b4193f8cc8f330e3944b87f760bee4fdb2cff0 100644 (file)
@@ -15,6 +15,7 @@
 * Iterator tutorial::
 * Engraver tutorial::
 * Callback tutorial::
+* Understanding pure properties::
 * LilyPond scoping::
 * Scheme->C interface::
 * LilyPond miscellany::
@@ -1775,10 +1776,302 @@ information.
 
 TODO -- This is a placeholder for a tutorial on callback functions.
 
+
+@node Understanding pure properties
+@section Understanding pure properties
+
+@menu
+* Purity in LilyPond::
+* Writing a pure function::
+* How purity is defined and stored::
+* Where purity is used::
+* Case studies::
+* Debugging tips::
+@end menu
+
+Pure properties are some of the most difficult properties to understand
+in LilyPond but, once understood, it is much easier to work with
+horizontal spacing.  This document provides an overview of what it means
+for something to be @q{pure} in LilyPond, what this purity guarantees,
+and where pure properties are stored and used.  It finishes by
+discussing a few case studies for the pure programmer to save you some
+time and to prevent you some major headaches.
+
+
+@node Purity in LilyPond
+@subsection Purity in LilyPond
+Pure properties in LilyPond that do not have any @q{side effects}.
+That is, looking up a pure property should never result in calls to the
+following functions:
+@itemize
+@item @code{set_property}
+@item @code{set_object}
+@item @code{suicide}
+@end itemize
+This means that, if the property is calculated via a callback, this callback
+must not only avoid the functions above but make sure that any functions
+it calls also avoid the functions above.  Also, to date in LilyPond, a pure
+function will always return the same value before line breaking (or, more
+precisely, before any version of @code{break_into_pieces} is called).  This
+convention makes it possible to cache pure functions and be more flexible
+about the order in which functions are called. For example; Stem #'length has
+a pure property that will @emph{never} trigger one of the functions listed
+above and will @emph{always} return the same value before line breaking,
+independent of where it is called.  Sometimes, this will be the actual length
+of the Stem.  But sometimes it will not.  For example; stem that links up
+with a beam will need its end set to the Y position of the beam at the stem's
+X position.  However, the beam's Y positions can only be known after the score
+is broken up in to several systems (a beam that has a shallow slope on a
+compressed line of music, for example, may have a steeper one on an
+uncompressed line).  Thus, we only call the impure version of the properties
+once we are @emph{absolutely certain} that all of the parameters needed to
+calculate their final value have been calculated.  The pure version provides a
+useful estimate of what this Stem length (or any property) will be, and
+the art of creating good pure properties is trying to get the estimation
+as close to the actual value as possible.
+
+Of course, like Gregory Peck and Tintin, some Grobs will have properties
+that will always be pure.  For example, the height of a note-head in
+not-crazy music will never depend on line breaking or other parameters
+decided late in the typesetting process.  Inversely, in rare cases,
+certain properties are difficult to estimate with pure values.  For
+example, the height of a Hairpin at a certain cross-section of its
+horizontal span is difficult to know without knowing the horizontal
+distance that the hairpin spans, and LilyPond provides an
+over-estimation by reporting the pure height as the entire height of the
+Hairpin.
+
+Purity, like for those living in a convent, is more like a contract than
+an @emph{a priori}.  If you write a pure-function, you are promising
+the user (and the developer who may have to clean up after you) that
+your function will not be dependent on factors that change at different
+stages of the compilation process (compilation of a score, not of
+LilyPond).
+
+One last oddity is that purity, in LilyPond, is currently limited
+exclusively to things that have to do with Y-extent and positioning.
+There is no concept of @q{pure X} as, by design, X is always the
+independent variable (i.e. from column X1 to column X2, what will be the
+Y height of a given grob).  Furthermore, there is no purity for
+properties like color, text, and other things for which a meaningful notion
+of estimation is either not necessary or has not yet been found.  For example,
+even if a color were susceptible to change at different points of the
+compilation process, it is not clear what a pure estimate of this color
+would be or how this pure color could be used.  Thus, in this document and
+in the source, you will see purity discussed almost interchangeably with
+Y-axis positioning issues.
+
+
+@node Writing a pure function
+@subsection Writing a pure function
+Pure functions take, at a minimum, three arguments: the @var{grob}, the
+starting column at which the function is being evaluated (hereafter
+referred to as @var{start}), and the end column at which the grob is
+being evaluated (hereafter referred to as @var{end}).  For items,
+@var{start} and @var{end} must be provided (meaning they are not optional)
+but will not have a meaningful impact on the result, as items only occupy
+one column and will thus yield a value or not (if they are not in the range
+from @var{start} to @var{end}).  For spanners however, @var{start} and
+@var{end} are important, as we may can get a better pure estimation of a
+slice of the spanner than considering it on the whole.  This is useful
+during line breaking, for example, when we want to estimate the Y-extent
+of a spanner broken at given starting and ending columns.
+
+If the pure function you're writing takes more than three arguments
+(say, for example, a chained offset callback), this is not a problem:
+just make sure that the grob is the first argument and that start and
+end are the last two arguments.
+
+
+@node How purity is defined and stored
+@subsection How purity is defined and stored
+Purity can currently be defined two different ways in LilyPond that
+correspond to two types of scenarios.  In one scenario, we know that a
+callback is pure, but we are not necessarily certain what properties
+will use this callback.  In another, we want a property to be pure, but
+we don't want to guarantee that its callback function will be pure in
+all circumstances.
+
+In the first scenario, we register the callback in define-grobs.scm in
+one of four places depending on what the function does.
+
+@itemize
+@item @code{pure-print-functions}: If finding a print function's vertical
+extent does not have any @q{side effects} we register it here. We then
+don't have to set the pure Y-extent property, which will be taken from the
+stencil.
+
+@item @code{pure-print-to-height-conversions}: If a stencil can
+eventually be used to glean a grob's Y-extent but is not pure (meaning
+it will have a different height at different stages of the compilation
+process), we add it to this list along with a function for the pure
+Y-extent.
+
+@item @code{pure-conversions-alist}: This list contains pairs of
+functions and their pure equivalents.  It is onto but not one-to-one.
+
+@item @code{pure-functions}: Like pure-print-functions in that they work
+for both pure and impure values, but they do not return a stencil.
+@end itemize
+
+At all stages of the compilation process, when LilyPond wants the pure
+version of a property, it will consult these lists and see if it can get
+this property for a given Grob.  Note that you do @emph{not} need to
+register the pure property in the grob itself.  For example, there is no
+property @q{pure-Y-extent}.  Rather, by registering these functions as
+defined above, every time LilyPond needs a pure property, it will check
+to see if a Grob contains one of these functions and, if so, will use
+its value.  If LilyPond cannot get a pure function, it will return a
+value of @code{##f} for the property.
+
+LilyPond is smart enough to know if a series of chained functions are
+pure.  For example, if a Y-offset property has four chained functions
+and all of them have pure equivalents, LilyPond will read the four pure
+equivalents when calculating the pure property.  However, if even one is
+impure, LilyPond will not return a pure property for the offset (instead
+returning something like @code{#f} or @code{'()}) and will likely wreak
+havoc on your score.
+
+In the second scenario, we create an unpure-pure-container (unpure is
+not a word, but hey, neither was Lilypond until the 90s).  For example:
+
+@example
+#(define (foo grob)
+  '(-1 . 1))
+
+#(define (bar grob start end)
+  '(-2 . 2))
+
+\override Stem #'length = #(ly:make-unpure-pure-container foo bar)
+@end example
+
+This is useful if we want to:
+
+@itemize
+@item create overrides that have pure alternatives (should not be used
+in development, but useful for users)
+
+@item use return values that are not functions (i.e. pairs or booleans)
+for either pure or unpure values.
+
+@item allow a function to be considered pure in a limited amount of
+circumstances.  This is useful if we are sure that, when associated with
+one grob a function will be pure but not necessarily with another grob
+that has different callbacks.
+@end itemize
+
+Items can only ever have two pure heights: their actual pure height if
+they are between @q{start} and @q{end}, or an empty interval if they are
+not.  Thus, their pure property is cached to speed LilyPond up.  Pure
+heights for spanners are generally not cached as they change depending
+on the start and end values.  They are only cached in certain particular
+cases.  Before writing a lot of caching code, make sure that it is a
+value that will be reused a lot.
+
+
+@node Where purity is used
+@subsection Where purity is used
+Pure Y values must be used in any functions that are called before
+line breaking.  Examples of this can be seen in
+@code{Separation_items::boxes} to construct horizontal skylines and in
+@code{Note_spacing::stem_dir_correction} to correct for optical
+illusions in spacing.  Pure properties are also used in the calculation
+of other pure properties.  For example, the @code{Axis_group_interface}
+has pure functions that look up other pure functions.
+
+Purity is also implicitly used in any functions that should only ever
+return pure values.  For example, extra-spacing-height is only ever used
+before line-breaking and thus should never use values that would only be
+available after line breaking.  In this case, there is no need to create
+callbacks with pure equivalents because these functions, by design, need
+to be pure.
+
+To know if a property will be called before and/or after line-breaking
+is sometimes tricky and can, like all things in coding, be found by
+using a debugger and/or adding @var{printf} statements to see where they
+are called in various circumstances.
+
+
+@node Case studies
+@subsection Case studies
+In each of these case studies, we expose a problem in pure properties, a
+solution, and the pros and cons of this solution.
+
+@subheading Time signatures
+A time signature needs to prevent accidentals from passing over or under
+it, but its extent does not necessarily extend to the Y-position of
+accidentals.  LilyPond's horizontal spacing sometimes makes a line of
+music compact and, when doing so, allows certain columns to pass over
+each other if they will not collide.  This type of passing over is not
+desirable with time signatures in traditional engraving.  But how do we
+know if this passing over will happen before line breaking, as we are
+not sure what the X positions will be?  We need a pure estimation of how
+much extra spacing height the time signatures would need to prevent this
+form of passing over without making this height so large as to
+overly-distort the Y-extent of an system, which could result in a very
+@q{loose} looking score with lots of horizontal space between columns.
+So, to approximate this extra spacing height, we use the Y-extent of a
+time signature's next-door-neighbor grobs via the pure-from-neighbor
+interface.
+
+@itemize
+@item pros: By extending the extra spacing height of a time signature to
+that of its next-door-neighbors, we make sure that grobs to the right of
+it that could pass above or below it do not.
+
+@item cons: This over-estimation of the vertical height could prevent
+snug vertical spacing of systems, as the system will be registered as
+being taller at the point of the time signature than it actually is.
+This approach can be used for clefs and bar lines as well.
+@end itemize
+
+@subheading Stems
+As described above, Stems need pure height approximations when they are
+beamed, as we do not know the beam positions before line breaking.  To
+estimate this pure height, we take all the stems in a beam and find
+their pure heights as if they were not beamed.  Then, we find the union
+of all these pure heights and take the intersection between this
+interval (which is large) and an interval going from the note-head of a
+stem to infinity in the direction of the stem so that the interval stops
+at the note head.
+
+@itemize
+@item pros: This is guaranteed to be at least as long as the beamed
+stem, as a beamed stem will never go over the ideal length of the
+extremal beam of a stem.
+
+@item cons: Certain stems will be estimated as being too long, which
+leads to the same problem of too-much-vertical-height as described
+above.
+
+@end itemize
+
+
+@node Debugging tips
+@subsection Debugging tips
+A few questions to ask yourself when working with pure properties:
+
+@itemize
+@item Is the property really pure?  Are you sure that its value could
+not be changed later in the compiling process due to other changes?
+
+@item Can the property be made to correspond even more exactly with the
+eventual impure property?
+
+@item For a spanner, is the pure property changing correctly depending
+on the starting and ending points of the spanner?
+
+@item For an Item, will the item's pure height need to act in horizontal
+spacing but not in vertical spacing?  If so, use extra-spacing-height
+instead of pure height.
+
+@end itemize
+
+
 @node LilyPond scoping
 @section LilyPond scoping
 
-The Lilypond language has a concept of scoping, i.e. you can do
+The Lilypond language has a concept of scoping, i.e. you can do:
 
 @example
 foo = 1
index f028159c3c978d366312c8824b93310145941b10..c5a1111a5a0d91436580911075e415f24b4f6a48 100644 (file)
@@ -116,11 +116,15 @@ are copied while setting @code{origin} to the @code{location} parameter.
 used as the return value of the scheme function.  It may contain
 LilyPond code blocks enclosed in hashed braces
 (@tie{}@w{@code{#@{@dots{}#@}}}@tie{}), like described in @ref{Lilypond
-code blocks}.  Within LilyPond code blocks, use @code{$} to reference
-function arguments (eg., @samp{$arg1}) or to start an inline Scheme
-expression containing function arguments (eg., @w{@samp{$(cons arg1
-arg2)}}).  If your function returns a music expression, it is cloned and
-given the correct @code{origin}.
+code blocks}.  Within LilyPond code blocks, use @code{#} to reference
+function arguments (eg., @samp{#arg1}) or to start an inline Scheme
+expression containing function arguments (eg., @w{@samp{#(cons arg1
+arg2)}}).  Where normal Scheme expressions using @code{#} don't do the
+trick, you might need to revert to immediate Scheme expressions using
+@code{$}, for example as @samp{$music}.
+
+If your function returns a music expression, it is cloned and given the
+correct @code{origin}.
 @end multitable
 
 @noindent
@@ -129,13 +133,13 @@ can't recognize the arguments reliably otherwise.  Currently these are
 @code{ly:pitch?} and @code{ly:duration?}.
 
 Suitability of arguments for all other predicates is determined by
-actually calling the predicate after Lilypond has already converted
-them into a Scheme expression.  As a consequence, the argument can be
+actually calling the predicate after Lilypond has already converted them
+into a Scheme expression.  As a consequence, the argument can be
 specified in Scheme syntax if desired (introduced with @code{#} or as
-the result of calling a scheme function), but Lilypond will also
-convert a number of Lilypond constructs into Scheme before actually
-checking the predicate on them.  Currently, those include music,
-simple strings (with or without quotes), numbers, full markups and markup
+the result of calling a scheme function), but Lilypond will also convert
+a number of Lilypond constructs into Scheme before actually checking the
+predicate on them.  Currently, those include music, postevents, simple
+strings (with or without quotes), numbers, full markups and markup
 lists, score, book, bookpart, context definition and output definition
 blocks.
 
@@ -146,20 +150,14 @@ optional argument by evaluating its predicate, Lilypond would not be
 able to ``backup'' when it decides the expression does not fit the
 parameter.  So some forms of music might need to be enclosed in braces
 to make them acceptable to Lilypond.  There are also some other
-complications that may cause a predicate function to be called several
-times on successive versions of an argument (like @code{3} and
-@code{3\cm}) or several interpretations (like @code{"a" 4} in lyric
-mode, which can either be a string followed by a number, or a lyric
-event of duration @code{4}).
-
-Music arguments preceding @code{ly:duration?} arguments must also be
-lookahead-free.  This may also hold for the last argument of a scheme
-function that is used as the last part of another expression, since
-otherwise Lilypond won't know whether following postevents or
-durations apply to the argument of the Scheme function, or to the
-containing music expression.
-
-For a list of available type predicates, see
+ambiguities that Lilypond sorts out by checking with predicate
+functions: is @samp{-3} a fingering postevent or a negative number?  Is
+@code{"a" 4} in lyric mode a string followed by a number, or a lyric
+event of duration @code{4}?  Lilypond decides by asking the predicates.
+That means that a lenient predicate like @code{scheme?} might be good
+for surprising interpretations.
+
+For a list of available predefined type predicates, see
 @ruser{Predefined type predicates}.
 
 @seealso
@@ -178,11 +176,20 @@ Installed Files:
 Scheme functions can be called pretty much anywhere where a Scheme
 expression starting with @code{#} can be written.  You call a scheme
 function by writing its name preceded by @code{\}, followed by its
-arguments.  The last argument can't be an optional argument.  If there
-are several optional arguments in a row, they are filled with values
-left to right.  Once an optional argument can't match input, it and all
-immediately following optional arguments are replaced with their default
-values, and the matching continues with the next non-optional argument.
+arguments.  Once an optional argument predicate does not match an
+argument, Lilypond skips this and all following optional arguments,
+replacing them with their specified default, and @q{backs up} the
+argument that did not match to the place of the next mandatory argument.
+Since the backed up argument needs to go somewhere, optional arguments
+are not actually considered optional unless followed by a mandatory
+argument.
+
+There is one exception: if you write @code{\default} in the place of an
+optional argument, this and all following optional arguments are skipped
+and replaced by their default.  This works even when no mandatory
+argument follows since @code{\default} does not need to get backed up.
+The @code{mark} and @code{key} commands make use of that trick to
+provide their default behavior when just followed by @code{\default}.
 
 Apart from places where a Scheme value is required, there are a few
 places where @code{#} expressions are currently accepted and evaluated
@@ -353,7 +360,7 @@ manualBeam =
      (parser location beg-end)
      (pair?)
    #@{
-     \once \override Beam #'positions = $beg-end
+     \once \override Beam #'positions = #beg-end
    #@})
 
 \relative c' @{
@@ -372,7 +379,7 @@ manualBeam =
      (parser location beg end)
      (number? number?)
    #{
-     \once \override Beam #'positions = $(cons beg end)
+     \once \override Beam #'positions = #(cons beg end)
    #})
 
 \relative c' {
@@ -393,9 +400,9 @@ AltOn =
      (parser location mag)
      (number?)
    #{
-     \override Stem #'length = $(* 7.0 mag)
+     \override Stem #'length = #(* 7.0 mag)
      \override NoteHead #'font-size =
-       $(inexact->exact (* (/ 6.0 (log 2.0)) (log mag)))
+       #(inexact->exact (* (/ 6.0 (log 2.0)) (log mag)))
    #})
 
 AltOff = {
@@ -418,9 +425,9 @@ withAlt =
      (parser location mag music)
      (number? ly:music?)
    #{
-     \override Stem #'length = $(* 7.0 mag)
+     \override Stem #'length = #(* 7.0 mag)
      \override NoteHead #'font-size =
-       $(inexact->exact (* (/ 6.0 (log 2.0)) (log mag)))
+       #(inexact->exact (* (/ 6.0 (log 2.0)) (log mag)))
      $music
      \revert Stem #'length
      \revert NoteHead #'font-size
index 5578931633d59e426e17c503d27179cab35e964d..f16c8c6467ecc6c53c58d9624d53e207a98b5190 100644 (file)
@@ -1,14 +1,14 @@
-\version "2.14.0"
+\version "2.15.17"
 
 \header {
-  texidoc = "When a beam goes over a rest, there should not be any 
-beamlets pointing towards the rest unless absolutely necessary."
+  texidoc = "When a beam goes over a rest, beamlets should be as necessary
+  to show the beat structure."
 }
 
 \relative c' {
   c8[ r16 c32 c32]
   c32[ r16 c32 c8]
-  c32[ r16 c64 c8 c64]
+  c32[ r16 c64 c64 ~ c16.. c64]
   c32[ c32 r16 c8]
   c16[ r32 c32 r16 c16]
   c16[ r16 c32 r32 c16]
diff --git a/input/regression/beamlet-point-toward-beat.ly b/input/regression/beamlet-point-toward-beat.ly
new file mode 100644 (file)
index 0000000..b1a761f
--- /dev/null
@@ -0,0 +1,14 @@
+\version "2.15.20"
+
+\header {
+  texidoc = "
+Beamlets should point in the direction of the beat to which they
+belong.
+"
+}
+
+
+\relative c' {
+b16. b32 b32 b16.
+b16.[ b32 b   b b b16.]
+}
diff --git a/input/regression/beamlet-test.ly b/input/regression/beamlet-test.ly
new file mode 100644 (file)
index 0000000..dc8d5f0
--- /dev/null
@@ -0,0 +1,36 @@
+\version "2.15.20"
+
+\header {
+  texidoc = "
+Beamlets should point away from complete beat units and toward off-beat or
+broken beat units.  This should work in tuplets as well as in ordinary time.
+"
+}
+
+\relative c'' {
+    \times 2/3 {
+      c8. c16 c8
+    }
+   \times 2/3 {
+      c8 c16 c8.
+    }
+  \times 4/5 {
+    c8[ c8. c16 c8 c8]
+  }
+  \times 4/5 {
+    c8[ c8 c16 c8. c8]
+  }
+  \times 4/5 {
+    c8 c16 c8. c8 c8
+  }
+  \times 4/5 {
+    c8 c8 c8. c16 c8
+  }
+  c8.[ c16 c8 c8]
+  c8[ c16 c8. c8]
+  c8[ c8. c16 c8]
+  c8.[ c16 c8. c16]
+  \times 4/5 { c8 [ c16 c8 c16 c8 c8 ] }
+  \times 4/5 { a8 a32 a8 a16. a8 a8 }
+}
+
index 2d3287c38efd01554314410cdb1c8cb25001fa49..08d0616d2453ec96dd990e52a621f8dfa1f4a485 100644 (file)
@@ -1,9 +1,14 @@
 \header {
-  texidoc = "SpanBars participate in the horizontal collision system;
-the accidentals should not collide with the bar lines."
+  texidoc = "Because @code{BarLine} grobs take their
+extra-positioning-height from their neighbors via the
+@code{pure-from-neighbor-interface}, the left edge of an
+accidental should never fall to the left of the right
+edge of a bar line.  This spacing should also take place when
+@code{SpanBar} grobs are present.
+"
 }
 
-\version "2.14.0"
+\version "2.15.21"
 
 upper = \relative c' {
   \key f \minor \time 12/8 
index 76a3023e10bb1d251619270e1040cf8e85459d1a..b3cd6f4bab61d02c7dcbeaa030119cbe2da0c7a7 100644 (file)
@@ -404,7 +404,10 @@ Auto_beam_engraver::acknowledge_stem (Grob_info info)
   if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
     return;
 
-  Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
+  Duration *stem_duration = unsmob_duration (ev->get_property ("duration"));
+  Moment dur = stem_duration->get_length ();
+
+  //Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
   Moment measure_now = measure_position (context ());
   bool recheck_needed = false;
 
@@ -425,7 +428,8 @@ Auto_beam_engraver::acknowledge_stem (Grob_info info)
 
   grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
                        durlog - 2,
-                       Stem::is_invisible (stem));
+                       Stem::is_invisible (stem),
+                       stem_duration->factor ());
   stems_->push_back (stem);
   last_add_mom_ = now;
   extend_mom_ = max (extend_mom_, now) + get_event_length (ev, now);
index f3a099b5df5dfb238fccf7a09768155c0fa8dedf..de9e48a1b4f6a949e5233d065e630d4103737252 100644 (file)
@@ -456,8 +456,13 @@ SCM
 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
+  return internal_calc_pure_relevant_grobs (me, "elements");
+}
 
-  extract_grob_set (me, "elements", elts);
+SCM
+Axis_group_interface::internal_calc_pure_relevant_grobs (Grob *me, string grob_set_name)
+{
+  extract_grob_set (me, grob_set_name.c_str (), elts);
 
   vector<Grob *> relevant_grobs;
   SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
index a22cd053b818177ea45b46be6889df0a001a51a4..b9e23a95e4f97ffc9cb8527734289148152527de 100644 (file)
@@ -467,6 +467,7 @@ ADD_INTERFACE (Bar_line,
                "kern "
                "thin-kern "
                "hair-thickness "
+               "has-span-bar "
                "thick-thickness "
                "glyph "
                "glyph-name "
index 654a7f57d17293c0caf5144a4c5344f0eec3b772..e28d15683619b5b866cedf837d0ddbc355d3ec4b 100644 (file)
@@ -268,7 +268,10 @@ Beam_engraver::acknowledge_stem (Grob_info info)
     }
 
   last_stem_added_at_ = now;
-  int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
+
+  Duration *stem_duration = unsmob_duration (ev->get_property ("duration"));
+  int durlog = stem_duration->duration_log ();
+  //int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
   if (durlog <= 2)
     {
       ev->origin ()->warning (_ ("stem does not fit in beam"));
@@ -287,7 +290,8 @@ Beam_engraver::acknowledge_stem (Grob_info info)
   Moment stem_location = now - beam_start_mom_ + beam_start_location_;
   beam_info_->add_stem (stem_location,
                         max (durlog - 2, 0),
-                        Stem::is_invisible (stem));
+                        Stem::is_invisible (stem),
+                        stem_duration->factor ());
   Beam::add_stem (beam_, stem);
 }
 
index ada6770a5c2e25d4db7493ce8d1d03861abf7f07..916ec145af96d0e438fe33db217439bc2d68660c 100644 (file)
@@ -39,16 +39,18 @@ Beam_rhythmic_element::Beam_rhythmic_element ()
   beam_count_drul_[LEFT] = 0;
   beam_count_drul_[RIGHT] = 0;
   invisible_ = false;
+  factor_ = Rational (1);
 
 }
 
-Beam_rhythmic_element::Beam_rhythmic_element (Moment m, int i, bool inv)
+Beam_rhythmic_element::Beam_rhythmic_element (Moment m, int i, bool inv, Rational factor)
 {
   start_moment_ = m;
   rhythmic_importance_ = 0;
   beam_count_drul_[LEFT] = i;
   beam_count_drul_[RIGHT] = i;
   invisible_ = inv;
+  factor_ = factor;
 }
 
 void
@@ -98,15 +100,9 @@ Beaming_pattern::flag_direction (Beaming_options const &options, vsize i) const
   if (count <= left_count && count <= right_count)
     return CENTER;
 
-  // Try to avoid sticking-out flags as much as possible by pointing my flags
-  // at the neighbour with the most flags.
-  else if (right_count > left_count)
-    return RIGHT;
-  else if (left_count > right_count)
-    return LEFT;
-
   // If all else fails, point the beamlet away from the important moment.
-  return (infos_[i].rhythmic_importance_ <= infos_[i + 1].rhythmic_importance_) ? RIGHT : LEFT;
+  return (infos_[i].rhythmic_importance_ <= infos_[i + 1].rhythmic_importance_)
+         ? RIGHT : LEFT;
 }
 
 void
@@ -135,68 +131,141 @@ Beaming_pattern::beamify (Beaming_options const &options)
 
   find_rhythmic_importance (options);
 
+  vector <Direction> flag_directions;
+  // Get the initial flag directions
+  for (vsize i = 0; i < infos_.size (); i++)
+    flag_directions.push_back (flag_direction (options, i));
+
+  // Correct flag directions for subdivision
   for (vsize i = 1; i < infos_.size () - 1; i++)
     {
-      Direction non_flag_dir = other_dir (flag_direction (options, i));
-      if (non_flag_dir)
+      if ((flag_directions[i] == CENTER) && (flag_directions[i - 1] == LEFT))
+        flag_directions[i] = RIGHT;
+      if ((flag_directions[i] == CENTER) && (flag_directions[i + 1] == RIGHT))
+        flag_directions[i] = LEFT;
+    }
+
+  // Set the count on each side of the stem
+  // We need to run this code twice to make both the
+  // left and the right counts work properly
+  for (int i = 0; i < 2; i++)
+    for (vsize i = 1; i < infos_.size () - 1; i++)
+      {
+        Direction non_flag_dir = other_dir (flag_directions[i]);
+        if (non_flag_dir)
+          {
+            int importance = infos_[i + 1].rhythmic_importance_;
+            int count = (importance < 0 && options.subdivide_beams_)
+                        ? 1 : min (min (infos_[i].count (non_flag_dir),
+                                        infos_[i + non_flag_dir].count (-non_flag_dir)),
+                                   infos_[i - non_flag_dir].count (non_flag_dir));
+
+            infos_[i].beam_count_drul_[non_flag_dir] = count;
+          }
+      }
+}
+
+/*
+   Get the group start position, the next group starting position, and the
+   next beat starting position, given start_moment, base_moment,
+   grouping, and factor
+*/
+void
+find_location (SCM grouping, Moment base_moment, Moment start_moment,
+               Rational factor, Moment *group_pos, Moment *next_group_pos,
+               Moment *next_beat_pos)
+{
+  *group_pos = Moment (0);
+  *next_group_pos = Moment (0);
+  *next_beat_pos = base_moment;
+
+  while (*next_beat_pos <= start_moment)
+    *next_beat_pos += base_moment;
+
+  while (*next_group_pos < *next_beat_pos)
+    {
+      int count = 1;  //default -- 1 base moments in a beam
+      if (scm_is_pair (grouping))
         {
-          int importance = (non_flag_dir == LEFT)
-                           ? infos_[i].rhythmic_importance_ : infos_[i + 1].rhythmic_importance_;
-          int count = (importance < 0 && options.subdivide_beams_)
-                      ? 1 : min (infos_[i].count (non_flag_dir),
-                                 infos_[i + non_flag_dir].count (-non_flag_dir));
+          count = scm_to_int (scm_car (grouping));
+          grouping = scm_cdr (grouping);
+        }
 
-          infos_[i].beam_count_drul_[non_flag_dir] = count;
+      // If we have a tuplet, the count should be determined from
+      // the maximum tuplet size for beamed tuplets.
+      int tuplet_count = factor.num ();
+      if (tuplet_count > 1)
+        {
+          // We use 1/8 as the base moment for the tuplet because it's
+          // the largest beamed value.  If the tuplet is shorter, it's
+          // OK, the code still works
+          int test_count = (tuplet_count * Moment (Rational (1, 8)) / base_moment).num ();
+          if (test_count > count) count = test_count;
         }
+      *group_pos = *next_group_pos;
+      *next_group_pos = *group_pos + count * base_moment;
     }
 }
 
 void
 Beaming_pattern::find_rhythmic_importance (Beaming_options const &options)
 {
-  Moment measure_pos (0);
+  Moment group_pos (0);  // 0 is the start of the first group
+  Moment next_group_pos (0);
+  Moment next_beat_pos (options.base_moment_);
+  int tuplet_count = 1;
+
   SCM grouping = options.grouping_;
   vsize i = 0;
 
+  // Find where we are in the beat structure of the measure
+  if (infos_.size ())
+    find_location (grouping, options.base_moment_, infos_[i].start_moment_,
+                   infos_[i].factor_, &group_pos, &next_group_pos, &next_beat_pos);
+
   // Mark the importance of stems that start at a beat or a beat group.
   while (i < infos_.size ())
     {
-      // If a beat grouping is not specified, default to 2 beats per group.
-      int count = 2;
-      if (scm_is_pair (grouping))
-        {
-          count = scm_to_int (scm_car (grouping));
-          grouping = scm_cdr (grouping);
-        }
-
+      tuplet_count = infos_[i].factor_.den ();
+      if ((next_beat_pos > next_group_pos)
+          || (infos_[i].start_moment_ > next_beat_pos))
+        // Find the new group ending point
+        find_location (grouping, options.base_moment_, infos_[i].start_moment_,
+                       infos_[i].factor_, &group_pos, &next_group_pos, &next_beat_pos);
       // Mark the start of this beat group
-      if (infos_[i].start_moment_ == measure_pos)
+      if (infos_[i].start_moment_ == group_pos)
         infos_[i].rhythmic_importance_ = -2;
-
-      // Mark the start of each unit up to the end of this beat group.
-      for (int unit = 1; unit <= count; unit++)
+      // Work through the end of the beat group or the end of the beam
+      while (i < infos_.size () && infos_[i].start_moment_ < next_group_pos)
         {
-          Moment next_measure_pos = measure_pos + options.base_moment_;
-
-          while (i < infos_.size () && infos_[i].start_moment_ < next_measure_pos)
+          Moment dt = infos_[i].start_moment_ - group_pos;
+          Rational tuplet = infos_[i].factor_;
+          Moment tuplet_moment (tuplet);
+          // set the beat end (if not in a tuplet) and increment the next beat
+          if (tuplet_count == 1 && infos_[i].start_moment_ == next_beat_pos)
             {
-              Moment dt = infos_[i].start_moment_ - measure_pos;
-
-              // The rhythmic importance of a stem between beats depends on its fraction
-              // of a beat: those stems with a lower denominator are deemed more
-              // important.
-              // FIXME: This is not the right way to do things for tuplets. For example,
-              // in an 8th-note triplet with a quarter-note beat, 1/3 of a beat should be
-              // more important than 1/2.
-              if (infos_[i].rhythmic_importance_ >= 0)
-                infos_[i].rhythmic_importance_ = (int) (dt / options.base_moment_).den ();
-
-              i++;
+              infos_[i].rhythmic_importance_ = -1;
+              next_beat_pos += options.base_moment_;
             }
+          // The rhythmic importance of a stem between beats depends on its fraction
+          // of a beat: those stems with a lower denominator are deemed more
+          // important.  For tuplets, we need to make sure that we use
+          // the fraction of the tuplet, instead of the fraction of
+          // a beat.
+          Moment ratio = (dt / options.base_moment_ / tuplet_moment);
+          if (infos_[i].rhythmic_importance_ >= 0)
+            infos_[i].rhythmic_importance_ = (int) ratio.den ();
+          i++;
+        }
 
-          measure_pos = next_measure_pos;
-          if (i < infos_.size () && infos_[i].start_moment_ == measure_pos)
+      if (i < infos_.size () && infos_[i].start_moment_ == next_beat_pos)
+        {
+          if (tuplet_count == 1)
             infos_[i].rhythmic_importance_ = -1;
+          next_beat_pos += options.base_moment_;
+          if (infos_[i].start_moment_ == next_group_pos)
+            infos_[i].rhythmic_importance_ = -2;
+          i++;
         }
     }
 }
@@ -228,9 +297,9 @@ Beaming_pattern::unbeam_invisible_stems ()
 }
 
 void
-Beaming_pattern::add_stem (Moment m, int b, bool invisible)
+Beaming_pattern::add_stem (Moment m, int b, bool invisible, Rational factor)
 {
-  infos_.push_back (Beam_rhythmic_element (m, b, invisible));
+  infos_.push_back (Beam_rhythmic_element (m, b, invisible, factor));
 }
 
 Beaming_pattern::Beaming_pattern ()
@@ -265,8 +334,14 @@ Beaming_pattern::invisibility (int i) const
   return infos_.at (i).invisible_;
 }
 
+Rational
+Beaming_pattern::factor (int i) const
+{
+  return infos_.at (i).factor_;
+}
+
 /*
-    Split a beamin pattern at index i and return a new
+    Split a beaming pattern at index i and return a new
     Beaming_pattern containing the removed elements
 */
 Beaming_pattern *
@@ -281,7 +356,8 @@ Beaming_pattern::split_pattern (int i)
       count = max (beamlet_count (j, LEFT), beamlet_count (j, RIGHT));
       new_pattern->add_stem (start_moment (j),
                              count,
-                             invisibility (j));
+                             invisibility (j),
+                             factor (j));
     }
   for (vsize j = i + 1; j < infos_.size ();)
     infos_.pop_back ();
index 320f0e4051100a66dfc8f62a067fa9616252523b..c0c2986b0888757cd269e91635e6ea5da511a546 100644 (file)
@@ -230,5 +230,6 @@ ADD_INTERFACE (Dot_column,
                "dots "
                "positioning-done "
                "direction "
+               "note-collision "
               );
 
index 6b62af0d47f72131fd06878a6004b1f59a8bdb41..89cecc3a0080b2fc48b1fa8f1b7dacafe6f88c32 100644 (file)
@@ -23,6 +23,7 @@
 #include "item.hh"
 #include "pointer-group-interface.hh"
 #include "spanner.hh"
+#include "system.hh"
 
 #include "translator.icc"
 
@@ -37,6 +38,7 @@ class Footnote_engraver : public Engraver
   vector<Drul_array<Spanner *> > annotated_spanners_;
 
   void stop_translation_timestep ();
+  void finalize ();
 
   void footnotify (Grob *, Stream_event *);
 };
@@ -54,6 +56,12 @@ Footnote_engraver::stop_translation_timestep ()
   events_.clear ();
 }
 
+void
+Footnote_engraver::finalize ()
+{
+  annotated_spanners_.resize (0);
+}
+
 Footnote_engraver::Footnote_engraver ()
 {
 }
index a21ab9c5d9ae9c4bcd6f438f6db5b896fd0d3536..567bd0015f4309d8c77f75933347b7d082d11d05 100644 (file)
@@ -644,6 +644,18 @@ Grob::get_vertical_axis_group_index (Grob *g)
 
 bool
 Grob::vertical_less (Grob *g1, Grob *g2)
+{
+  return internal_vertical_less (g1, g2, false);
+}
+
+bool
+Grob::pure_vertical_less (Grob *g1, Grob *g2)
+{
+  return internal_vertical_less (g1, g2, true);
+}
+
+bool
+Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
 {
   Grob *vag = get_root_vertical_alignment (g1);
   if (!vag)
@@ -658,6 +670,12 @@ Grob::vertical_less (Grob *g1, Grob *g2)
 
   extract_grob_set (vag, "elements", elts);
 
+  if (ag1 == ag2 && !pure)
+    {
+      Grob *common = g1->common_refpoint (g2, Y_AXIS);
+      return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
+    }
+
   for (vsize i = 0; i < elts.size (); i++)
     {
       if (elts[i] == ag1)
@@ -768,6 +786,7 @@ ADD_INTERFACE (Grob,
                "extra-X-extent "
                "extra-Y-extent "
                "extra-offset "
+               "forced-spacing "
                "interfaces "
                "layer "
                "meta "
index 93d70ee54c54f7f7bd2cdc42dac7e8361ba3482b..35ed244c0f36960cf4759dd6bda3ed779ba96863 100644 (file)
@@ -45,6 +45,7 @@ struct Axis_group_interface
   DECLARE_SCHEME_CALLBACK (calc_pure_relevant_items, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_pure_relevant_spanners, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_pure_y_common, (SCM));
+  static SCM internal_calc_pure_relevant_grobs (Grob *, string);
   static Interval relative_group_extent (vector<Grob *> const &list,
                                          Grob *common, Axis);
   static Interval relative_maybe_bound_group_extent (vector<Grob *> const &list,
index 58ec08443b6ed112476457b5cf9257f029aee128..1fe990aaa49392a569b8c5196698c72d38112b5b 100644 (file)
@@ -43,7 +43,9 @@ struct Beam_rhythmic_element
   int rhythmic_importance_;
   bool invisible_;
 
-  Beam_rhythmic_element (Moment, int, bool);
+  Rational factor_;
+
+  Beam_rhythmic_element (Moment, int, bool, Rational);
   Beam_rhythmic_element ();
 
   int count (Direction d) const;
@@ -61,9 +63,10 @@ public:
 
   void beamify (Beaming_options const &);
   void de_grace ();
-  void add_stem (Moment d, int beams, bool invisible);
+  void add_stem (Moment d, int beams, bool invisible, Rational factor);
   int beamlet_count (int idx, Direction d) const;
   bool invisibility (int idx) const;
+  Rational factor (int idx) const;
   Moment start_moment (int idx) const;
   Moment end_moment (int idx) const;
   Beaming_pattern *split_pattern (int idx);
index 36f912a2894629a4b789366bd359d486ee60a238..681e4be2904f7da774b4ed4c52a5246ce8f0cda0 100644 (file)
@@ -144,6 +144,8 @@ public:
   static Grob *get_root_vertical_alignment (Grob *g);
   static Grob *get_vertical_axis_group (Grob *g);
   static bool vertical_less (Grob *g1, Grob *g2);
+  static bool pure_vertical_less (Grob *g1, Grob *g2);
+  static bool internal_vertical_less (Grob *g1, Grob *g2, bool pure);
   static int get_vertical_axis_group_index (Grob *g);
 
   virtual Interval_t<int> spanned_rank_interval () const;
index a98c760712cb6914f2a2c7b5ed36ddcbf3c272ec..0859f72d61c84885cec6c5301325c29075ff56f7 100644 (file)
@@ -35,8 +35,6 @@ class Lily_parser
   DECLARE_SMOBS (Lily_parser);
   friend int yyparse (void *);
 
-  vector<Input> define_spots_;
-
   char const *here_str0 () const;
   Simultaneous_music *get_chord (Pitch tonic,
                                  vector<Pitch> *adds, vector<Pitch> *subs,
index eb461393677a6e84d2448b480926acfdacbcbdb7..01145a3715cc384632f0703fca25094c7739f4ff 100644 (file)
@@ -26,7 +26,7 @@
 class Pure_from_neighbor_interface
 {
 public:
-  DECLARE_SCHEME_CALLBACK (filter_elements, (SCM));
+  DECLARE_SCHEME_CALLBACK (calc_pure_relevant_grobs, (SCM));
   DECLARE_GROB_INTERFACE ();
 
 };
index 1f017321c517876e1cf420272b063972a115a199..d594d74c549011cad38f7602e3ae25c53f83df5d 100644 (file)
@@ -36,6 +36,7 @@ public:
   static Interval get_spanned_interval (Grob *);
   static void add_bar (Grob *, Grob *);
   static void evaluate_glyph (Grob *);
+  static void notify_grobs_of_my_existence (Grob *);
   DECLARE_SCHEME_CALLBACK (width, (SCM smob));
   DECLARE_SCHEME_CALLBACK (print, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_glyph_name, (SCM));
index 165b589c378aefe145392657f253c92b7da87473..5ce98171b81647d3cd4d8b9cd9e3f48038a73e10 100644 (file)
@@ -37,8 +37,6 @@ class System : public Spanner
   void init_elements ();
   friend class Paper_score;     // ugh.
   Paper_score *pscore_; // ugh.
-  bool checked_footnotes_;
-  vector<Grob *> footnote_grobs_; // TODO: make this a grob array
 
 public:
   Paper_score *paper_score () const;
@@ -52,17 +50,17 @@ public:
   vector<Real> get_footnote_heights_in_range (vsize st, vsize end);
   vector<Real> get_in_note_heights_in_range (vsize st, vsize end);
   vector<Real> internal_get_note_heights_in_range (vsize st, vsize end, bool foot);
-  void get_footnote_grobs_in_range (vector<Grob *> &out, vsize st, vsize end);
-  vector<Grob *> *footnote_grobs ();
+  vector<Grob *> get_footnote_grobs_in_range (vsize st, vsize end);
   vsize num_footnotes ();
   void do_break_substitution_and_fixup_refpoints ();
   void post_processing ();
-  void populate_footnote_grob_vector ();
   SCM get_paper_system ();
   SCM get_paper_systems ();
   SCM get_broken_system_grobs ();
   SCM get_broken_footnote_stencils ();
 
+  DECLARE_SCHEME_CALLBACK (footnotes_before_line_breaking, (SCM));
+  DECLARE_SCHEME_CALLBACK (footnotes_after_line_breaking, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_pure_relevant_grobs, (SCM));
   DECLARE_SCHEME_CALLBACK (height, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_pure_height, (SCM, SCM, SCM));
index 196b36f55792ee98a2957b1fc2ca76417fb68a5b..557bc42696b278211a1b2bcd8674dea769af9170 100644 (file)
@@ -131,12 +131,6 @@ Lily_parser::parse_file (string init, string name, string out_name)
 
   scm_set_current_module (mod);
 
-  if (!define_spots_.empty ())
-    {
-      define_spots_.back ().warning (_ ("braces do not match"));
-      error_level_ = 1;
-    }
-
   error_level_ = error_level_ | lexer_->error_level_;
   clear ();
 }
@@ -156,13 +150,6 @@ Lily_parser::parse_string (string ly_code)
   do_yyparse ();
   scm_set_current_module (mod);
 
-  if (!define_spots_.empty ())
-    {
-      if (define_spots_.empty ()
-          && !error_level_)
-        programming_error ("define_spots_ don't match, but error_level_ not set.");
-    }
-
   error_level_ = error_level_ | lexer_->error_level_;
 }
 
@@ -185,13 +172,6 @@ Lily_parser::parse_string_expression (string ly_code)
   
   scm_set_current_module (mod);
 
-  if (!define_spots_.empty ())
-    {
-      if (define_spots_.empty ()
-          && !error_level_)
-        programming_error ("define_spots_ don't match, but error_level_ not set.");
-    }
-
   error_level_ = error_level_ | lexer_->error_level_;
   return result;
 }
index 562f0aeb901c98e1eaa121af331ff0d8af45d9c5..319ba396f14a5d087e6e64938f53623a2136cfd4 100644 (file)
@@ -206,9 +206,10 @@ Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book
             }
           Stencil mol;
           Stencil in_note_mol;
-          for (vsize i = 0; i < sys->footnote_grobs ()->size (); i++)
+          extract_grob_set (sys, "footnotes-after-line-breaking", footnote_grobs);
+          for (vsize i = 0; i < footnote_grobs.size (); i++)
             {
-              Grob *footnote = sys->footnote_grobs ()->at (i);
+              Grob *footnote = footnote_grobs[i];
               SCM footnote_markup = footnote->get_property ("footnote-text");
               if (Spanner *orig = dynamic_cast<Spanner *>(footnote))
                 if (orig->is_broken ())
index 14d94730b8e48d42a813bd66b25c1a5523fb885d..506127c50dd25b95e553d589ef27eeaf556e3068 100644 (file)
 
 class Pure_from_neighbor_engraver : public Engraver
 {
-  vector<Grob *> items_then_;
-  vector<Grob *> items_now_;
-  vector<Grob *> pures_then_;
-  vector<Grob *> pures_now_;
+  vector<Grob *> pure_relevants_;
+  vector<Grob *> need_pure_heights_from_neighbors_;
 
 public:
   TRANSLATOR_DECLARATIONS (Pure_from_neighbor_engraver);
 protected:
   DECLARE_ACKNOWLEDGER (pure_from_neighbor);
   DECLARE_ACKNOWLEDGER (item);
-  void stop_translation_timestep ();
+  void finalize ();
 };
 
 Pure_from_neighbor_engraver::Pure_from_neighbor_engraver ()
@@ -49,40 +47,71 @@ void
 Pure_from_neighbor_engraver::acknowledge_item (Grob_info i)
 {
   SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
-  if (!Pure_from_neighbor_interface::has_interface (i.grob ())
+  if (!Pure_from_neighbor_interface::has_interface (i.item ())
       && to_boolean (scm_call_1 (pure_relevant_p, i.item ()->self_scm ())))
-    items_now_.push_back (i.item ());
+    pure_relevants_.push_back (i.item ());
 }
 
-// note that this can get out of hand if there are lots of vertical axis groups...
-
 void
 Pure_from_neighbor_engraver::acknowledge_pure_from_neighbor (Grob_info i)
 {
-  pures_now_.push_back (i.item ());
+  need_pure_heights_from_neighbors_.push_back (i.item ());
 }
 
 void
-Pure_from_neighbor_engraver::stop_translation_timestep ()
+Pure_from_neighbor_engraver::finalize ()
 {
-  if (pures_now_.size ())
-    {
-      for (vsize i = 0; i < pures_now_.size (); i++)
-        for (vsize j = 0; j < items_then_.size (); j++)
-          Pointer_group_interface::add_grob (pures_now_[i], ly_symbol2scm ("elements"), items_then_[j]);
+  if (!need_pure_heights_from_neighbors_.size ())
+    return;
+
+  vector_sort (need_pure_heights_from_neighbors_, Grob::less);
+  vector_sort (pure_relevants_, Grob::less);
+
+  /*
+    first, clump need_pure_heights_from_neighbors into
+    vectors of grobs that have the same column.
+  */
 
-      for (vsize i = 0; i < pures_then_.size (); i++)
-        for (vsize j = 0; j < items_now_.size (); j++)
-          Pointer_group_interface::add_grob (pures_then_[i], ly_symbol2scm ("elements"), items_now_[j]);
+  vsize l = 0;
+  vector<vector<Grob *> > need_pure_heights_from_neighbors;
+  do
+    {
+      vector<Grob *> temp;
+      temp.push_back (need_pure_heights_from_neighbors_[l]);
+      for (;
+           (l < need_pure_heights_from_neighbors_.size () - 1
+            && (need_pure_heights_from_neighbors_[l]->spanned_rank_interval ()[LEFT]
+                == need_pure_heights_from_neighbors_[l + 1]->spanned_rank_interval ()[LEFT]));
+           l++)
+        temp.push_back (need_pure_heights_from_neighbors_[l + 1]);
+      need_pure_heights_from_neighbors.push_back (temp);
+      l++;
+    }
+  while (l < need_pure_heights_from_neighbors_.size ());
 
-      items_then_.clear ();
-      items_then_.insert (items_then_.end (), items_now_.begin (), items_now_.end ());
-      items_now_.clear ();
+  /*
+    then, loop through the pure_relevants_ list, adding the items
+    to the elements of need_pure_heights_from_neighbors_ on either side.
+  */
 
-      pures_then_.clear ();
-      pures_then_.insert (pures_then_.end (), pures_now_.begin (), pures_now_.end ());
-      pures_now_.clear ();
+  int pos[2] = {-1, 0};
+  for (vsize i = 0; i < pure_relevants_.size (); i++)
+    {
+      if (pos[1] < (int) need_pure_heights_from_neighbors.size ()
+          && (pure_relevants_[i]->spanned_rank_interval ()[LEFT]
+              > need_pure_heights_from_neighbors[pos[1]][0]->spanned_rank_interval ()[LEFT]))
+        {
+          pos[0] = pos[1];
+          pos[1]++;
+        }
+      for (int j = 0; j < 2; j++)
+        if (pos[j] >= 0 && pos[j] < (int) need_pure_heights_from_neighbors.size ())
+          for (vsize k = 0; k < need_pure_heights_from_neighbors[pos[j]].size (); k++)
+            Pointer_group_interface::add_grob (need_pure_heights_from_neighbors[pos[j]][k], ly_symbol2scm ("neighbors"), pure_relevants_[i]);
     }
+
+  need_pure_heights_from_neighbors_.clear ();
+  pure_relevants_.clear ();
 }
 
 #include "translator.icc"
index d0c85aab9e7b0483566e3ae601c170349ea7c8c0..e6bc924aea6a9bc21619caab5804394d8c621a70 100644 (file)
   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "axis-group-interface.hh"
 #include "grob.hh"
 #include "grob-array.hh"
+#include "moment.hh"
+#include "paper-column.hh"
 #include "pointer-group-interface.hh"
 #include "pure-from-neighbor-interface.hh"
 #include "spanner.hh"
 #include "system.hh"
 
-MAKE_SCHEME_CALLBACK (Pure_from_neighbor_interface, filter_elements, 1);
+MAKE_SCHEME_CALLBACK (Pure_from_neighbor_interface, calc_pure_relevant_grobs, 1);
 SCM
-Pure_from_neighbor_interface::filter_elements (SCM smob)
+Pure_from_neighbor_interface::calc_pure_relevant_grobs (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
-  extract_grob_set (me, "elements", elts);
+  extract_grob_set ((me->original () && me->original ()->is_live ()
+                      ? me->original ()
+                      : me),
+                     "neighbors",
+                     elts);
+
   vector<Grob *> new_elts;
-  Interval_t<int> srl = me->get_system ()->spanned_rank_interval ();
-  for (vsize i = 0; i < elts.size (); i++)
-    if (srl.contains (elts[i]->spanned_rank_interval ()[LEFT]))
-      new_elts.push_back (elts[i]);
-
-  SCM elements_scm = me->get_object ("elements");
-  if (Grob_array::unsmob (elements_scm))
-    {
-      vector<Grob *> &arr
-        = unsmob_grob_array (elements_scm)->array_reference ();
-      arr = new_elts;
-    }
-
-  return SCM_BOOL_T;
+  new_elts.insert (new_elts.end (), elts.begin (), elts.end ());
+
+  SCM neighbors_scm = me->get_object ("neighbors");
+  if (Grob_array::unsmob (neighbors_scm))
+     {
+       vector<Grob *> &arr
+         = unsmob_grob_array (neighbors_scm)->array_reference ();
+       arr = new_elts;
+     }
+
+  return Axis_group_interface::internal_calc_pure_relevant_grobs (me, "neighbors");
 }
 
 ADD_INTERFACE (Pure_from_neighbor_interface,
@@ -53,7 +58,7 @@ ADD_INTERFACE (Pure_from_neighbor_interface,
                "heights of the objects' neighbors.",
 
                /* properties */
-               "elements-filtered "
+               "neighbors "
                "pure-relevant-grobs "
                "pure-Y-common "
               );
index c2910d5cd1b8772f127258dea214d338711fc979..b1008bb934d628c2660a7fdda09f094d8fd1374f 100644 (file)
@@ -37,6 +37,7 @@ ADD_INTERFACE (Semi_tie_column,
                "positioning-done "
                "head-direction "
                "tie-configuration "
+               "ties "
               );
 
 /*
index 4addffcac66b218f8c1efd91feb9e16d0a7252dd..c856e4098e73fda38f2b7c32a796b766e41ebf7f 100644 (file)
@@ -71,7 +71,7 @@ Span_bar_engraver::process_acknowledged ()
     {
       Grob *vag = Grob::get_root_vertical_alignment (bars_[0]);
       if (vag)
-        vector_sort (bars_, Grob::vertical_less);
+        vector_sort (bars_, Grob::pure_vertical_less);
       spanbar_ = make_item ("SpanBar", SCM_EOL);
 
       spanbar_->set_parent (bars_[0], X_AXIS);
@@ -90,7 +90,7 @@ Span_bar_engraver::stop_translation_timestep ()
       SCM vis = bars_[0]->internal_get_property (vissym);
       if (ly_is_equal (spanbar_->internal_get_property (vissym), vis))
         spanbar_->set_property (vissym, vis);
-
+      Span_bar::notify_grobs_of_my_existence (spanbar_);
       spanbar_ = 0;
     }
   bars_.resize (0);
index 47b0c1a4d55d007cadd2f40025119b3015ab978b..a266cde724e9577c0bd948c3dcf5c7ef5139b0ff 100644 (file)
 #include "span-bar.hh"
 #include "engraver.hh"
 
+/*
+  Note that span bar stubs exist for pure height calculations ONLY.
+  They should never be visually present on the page and should never
+  be engraved in contexts where BarLines are engraved.
+*/
+
 class Span_bar_stub_engraver : public Engraver
 {
   vector<Grob *> spanbars_;
@@ -52,8 +58,6 @@ Span_bar_stub_engraver::acknowledge_span_bar (Grob_info i)
   spanbars_.push_back (i.grob ());
 }
 
-// note that this can get out of hand if there are lots of vertical axis groups...
-
 void
 Span_bar_stub_engraver::acknowledge_hara_kiri_group_spanner (Grob_info i)
 {
@@ -118,7 +122,7 @@ Span_bar_stub_engraver::process_acknowledged ()
           gi.rerouting_daddy_context_ = affected_contexts[j];
           announce_grob (gi);
           if (!keep_extent[j])
-            it->set_property ("Y-extent", ly_interval2scm (Interval (infinity_f, -infinity_f)));
+            it->suicide ();//it->set_property ("Y-extent", ly_interval2scm (Interval (infinity_f, -infinity_f)));
         }
     }
   spanbars_.clear ();
index 0a46aa69798e1a0b5a1b80161aa774997f7ea4ee..d64b6ba87c8f227aadb79a332cce0e7c4db7e27b 100644 (file)
@@ -214,6 +214,18 @@ Span_bar::calc_glyph_name (SCM smob)
   return ly_string2scm (type);
 }
 
+void
+Span_bar::notify_grobs_of_my_existence (Grob *me)
+{
+  extract_grob_set (me, "elements", elts);
+  vector<Grob *> sortable (elts.begin (), elts.end ());
+  vector_sort (sortable, Grob::vertical_less);
+  for (vsize i = 0; i < sortable.size (); i++)
+    sortable[i]->set_property ("has-span-bar",
+                               scm_cons (scm_from_bool (i != 0),
+                                         scm_from_bool (i != sortable.size () - 1)));
+}
+
 Interval
 Span_bar::get_spanned_interval (Grob *me)
 {
index 35cd814f0c10fba5870530609b2b7bf9eee5e9b7..5e7cab3583a82386fc0250cd82ced8ceaedae118 100644 (file)
@@ -71,3 +71,14 @@ LY_DEFINE (ly_staff_symbol_staff_space, "ly:staff-symbol-staff-space",
   Real staff_space = Staff_symbol_referencer::staff_space (g);
   return scm_from_double (staff_space);
 }
+
+LY_DEFINE (ly_staff_symbol_staff_radius, "ly:staff-symbol-staff-radius",
+           1, 0, 0, (SCM grob),
+           "Returns the radius of the staff associated with"
+           " @var{grob}.")
+{
+  LY_ASSERT_SMOB (Grob, grob, 1);
+  Grob *g = unsmob_grob (grob);
+  Real staff_radius = Staff_symbol_referencer::staff_radius (g);
+  return scm_from_double (staff_radius);
+}
index c217d0de15b4a53d9ab40f4af38e8a4b24030e65..7ef1b938426113110d16ce92fa07c94de5a4a1dd 100644 (file)
@@ -1095,6 +1095,7 @@ ADD_INTERFACE (Stem,
                "length "
                "length-fraction "
                "max-beam-connect "
+               "melody-spanner "
                "neutral-direction "
                "no-stem-extend "
                "note-heads "
index 176e338a54074c48bddaed4674353f0e04aa44d3..cc4cbda54960abf192f5a5ad99ea42cf5e66740d 100644 (file)
@@ -47,7 +47,6 @@ System::System (System const &src)
   all_elements_ = 0;
   pscore_ = 0;
   rank_ = 0;
-  checked_footnotes_ = false;
   init_elements ();
 }
 
@@ -56,7 +55,6 @@ System::System (SCM s)
 {
   all_elements_ = 0;
   rank_ = 0;
-  checked_footnotes_ = false;
   init_elements ();
 }
 
@@ -191,6 +189,7 @@ System::do_break_substitution_and_fixup_refpoints ()
     {
       System *child = dynamic_cast<System *> (broken_intos_[i]);
       child->all_elements_->remove_duplicates ();
+
       for (vsize j = 0; j < child->all_elements_->size (); j++)
         {
           Grob *g = child->all_elements_->grob (j);
@@ -229,37 +228,32 @@ System::get_paper_systems ()
   return lines;
 }
 
-void
-System::populate_footnote_grob_vector ()
-{
-  extract_grob_set (this, "all-elements", all_elts);
-  for (vsize i = 0; i < all_elts.size (); i++)
-    if (all_elts[i]->internal_has_interface (ly_symbol2scm ("footnote-interface")))
-      footnote_grobs_.push_back (all_elts[i]);
-
-  sort (footnote_grobs_.begin (), footnote_grobs_.end (), Grob::less);
-  checked_footnotes_ = true;
-}
-
-void
-System::get_footnote_grobs_in_range (vector<Grob *> &out, vsize start, vsize end)
+vector<Grob *>
+System::get_footnote_grobs_in_range (vsize start, vsize end)
 {
-  if (!checked_footnotes_)
-    populate_footnote_grob_vector ();
-
-  for (vsize i = 0; i < footnote_grobs_.size (); i++)
+  vector<Grob *> out;
+  extract_grob_set (this, "footnotes-before-line-breaking", footnote_grobs);
+  for (vsize i = 0; i < footnote_grobs.size (); i++)
     {
-      int pos = footnote_grobs_[i]->spanned_rank_interval ()[LEFT];
-      if (Spanner *s = dynamic_cast<Spanner *>(footnote_grobs_[i]))
+      Grob *at_bat = footnote_grobs[i];
+      int pos = at_bat->spanned_rank_interval ()[LEFT];
+      bool end_of_line_visible = true;
+      if (Spanner *s = dynamic_cast<Spanner *>(at_bat))
         {
           Direction spanner_placement = robust_scm2dir (s->get_property ("spanner-placement"), LEFT);
           if (spanner_placement == CENTER)
             spanner_placement = LEFT;
 
           pos = s->spanned_rank_interval ()[spanner_placement];
+          if (s->original ())
+            {
+              Spanner *orig = dynamic_cast<Spanner *>(s->original ());
+              at_bat = spanner_placement == LEFT ? orig->broken_intos_[0] : orig->broken_intos_.back ();
+              pos = at_bat->spanned_rank_interval ()[RIGHT];
+            }
         }
 
-      if (Item *item = dynamic_cast<Item *>(footnote_grobs_[i]))
+      if (Item *item = dynamic_cast<Item *>(at_bat))
         {
           if (!Item::break_visible (item))
             continue;
@@ -275,12 +269,17 @@ System::get_footnote_grobs_in_range (vector<Grob *> &out, vsize start, vsize end
       if (pos < int (start))
         continue;
       if (pos > int (end))
-        break;
-      if (!footnote_grobs_[i]->is_live ())
+        continue;
+      if (pos == int (start) && end_of_line_visible)
+        continue;
+      if (pos == int (end) && !end_of_line_visible)
+        continue;
+      if (!at_bat->is_live ())
         continue;
 
-      out.push_back (footnote_grobs_[i]);
+      out.push_back (at_bat);
     }
+  return out;
 }
 
 vector<Real>
@@ -298,8 +297,7 @@ System::get_in_note_heights_in_range (vsize start, vsize end)
 vector<Real>
 System::internal_get_note_heights_in_range (vsize start, vsize end, bool foot)
 {
-  vector<Grob *> footnote_grobs;
-  get_footnote_grobs_in_range (footnote_grobs, start, end);
+  vector<Grob *> footnote_grobs = get_footnote_grobs_in_range (start, end);
   vector<Real> out;
 
   for (vsize i = footnote_grobs.size (); i--;)
@@ -331,13 +329,66 @@ System::internal_get_note_heights_in_range (vsize start, vsize end, bool foot)
 vsize
 System::num_footnotes ()
 {
-  return footnote_grobs_.size ();
+  extract_grob_set (this, "footnotes-after-line-breaking", footnote_grobs);
+  return footnote_grobs.size ();
+}
+
+bool
+grob_2D_less (Grob *g1, Grob *g2)
+{
+  int sri[] = {0,0};
+  Grob *gs[] = {g1, g2};
+
+  for (int i = 0; i < 2; i++)
+    {
+      sri[i] = gs[i]->spanned_rank_interval ()[LEFT];
+      if (Spanner *s = dynamic_cast<Spanner *> (gs[i]))
+        {
+          if (s->broken_intos_.size ())
+            s = (scm_to_int (s->broken_intos_[0]->get_property ("spanner-placement")) == LEFT
+                 ? s->broken_intos_[0]
+                 : s->broken_intos_.back ());
+          gs[i] = s;
+          if (robust_scm2double (s->get_property ("X-offset"), 0.0) > 0)
+            sri[i] = s->spanned_rank_interval ()[RIGHT];
+        }
+    }
+
+  if (sri[0] == sri[1])
+    return Grob::vertical_less (gs[0], gs[1]);
+
+ return sri[0] < sri[1];
 }
 
-vector<Grob *>*
-System::footnote_grobs ()
+MAKE_SCHEME_CALLBACK (System, footnotes_before_line_breaking, 1);
+SCM
+System::footnotes_before_line_breaking (SCM smob)
 {
-  return &footnote_grobs_;
+  Grob *me = unsmob_grob (smob);
+  vector<Grob *> footnotes;
+  SCM grobs_scm = Grob_array::make_array ();
+  extract_grob_set (me, "all-elements", elts);
+  for (vsize i = 0; i < elts.size (); i++)
+    if (elts[i]->internal_has_interface (ly_symbol2scm ("footnote-interface")))
+      footnotes.push_back (elts[i]);
+
+  unsmob_grob_array (grobs_scm)->set_array (footnotes);
+  return grobs_scm;
+}
+
+MAKE_SCHEME_CALLBACK (System, footnotes_after_line_breaking, 1);
+SCM
+System::footnotes_after_line_breaking (SCM smob)
+{
+  Spanner *sys_span = unsmob_spanner (smob);
+  System *sys = dynamic_cast<System *> (sys_span);
+  Interval_t<int> sri = sys->spanned_rank_interval ();
+  vector<Grob *> footnote_grobs = sys->get_footnote_grobs_in_range (sri[LEFT], sri[RIGHT]);
+  vector_sort (footnote_grobs, grob_2D_less);
+
+  SCM grobs_scm = Grob_array::make_array ();
+  unsmob_grob_array (grobs_scm)->set_array (footnote_grobs);
+  return grobs_scm;
 }
 
 void
@@ -356,8 +407,6 @@ System::break_into_pieces (vector<Column_x_positions> const &breaking)
       Interval iv (pure_height (this, st, end));
       system->set_property ("pure-Y-extent", ly_interval2scm (iv));
 
-      get_footnote_grobs_in_range (system->footnote_grobs_, st, end);
-
       system->set_bound (LEFT, c[0]);
       system->set_bound (RIGHT, c.back ());
       SCM system_labels = SCM_EOL;
@@ -1001,6 +1050,8 @@ ADD_INTERFACE (System,
                "all-elements "
                "columns "
                "footnote-stencil "
+               "footnotes-before-line-breaking "
+               "footnotes-after-line-breaking "
                "in-note-direction "
                "in-note-padding "
                "in-note-stencil "
index c73694d334e2f581f66727a1cfd012a26dac5033..3f0957d1cd86929c43b3fee1bfe3b5fcf67fd0d4 100644 (file)
@@ -122,5 +122,6 @@ ADD_INTERFACE (Tie_column,
                /* properties */
                "positioning-done "
                "tie-configuration "
+               "ties "
               );
 
index 21c0447868c1cc501e80ea2a8bf3b050f8d47491..a416974ba08169371f411782dfeda42ac6163171 100644 (file)
@@ -54,6 +54,7 @@
 
   \consists "Output_property_engraver"
   \consists "Bar_engraver"
+  \consists "Pure_from_neighbor_engraver"
   %% Bar_engraver must be first so default bars aren't overwritten
   %% with empty ones.
 
index c7960fe76a6cf483efab30742c6a97baf2fc3724..2b67674fb11c15590f30f29a12bfdfdee042173f 100644 (file)
@@ -244,7 +244,6 @@ Positive means move the center to the right.")
 the vertical edges: @code{(@var{left-height} . @var{right-height})}.")
      (edge-text ,pair? "A pair specifying the texts to be set at the
 edges: @code{(@var{left-text} . @var{right-text})}.")
-     (elements-filtered ,boolean? "Callback to filter an element list.")
      (round-up-exceptions ,list? "A list of pairs where car is the numerator
 and cdr the denominator of a moment.  Each pair in this list means that
 the multi-measure rests of the corresponding length will be rounded up to
@@ -303,6 +302,8 @@ larger.  Fractional values are allowed.")
      (force-hshift ,number? "This specifies a manual shift for notes
 in collisions.  The unit is the note head width of the first voice
 note.  This is used by @rinternals{note-collision-interface}.")
+     (forced-spacing ,number? "Spacing forced between grobs, used in
+various ligature engravers.")
      (fraction ,fraction? "Numerator and denominator of a time
 signature object.")
      (french-beaming ,boolean? "Use French beaming style for this
@@ -681,7 +682,6 @@ positions are requested, the closest one is taken.")
 shift dotted up-note to the right, rather than shifting just the
 dot.")
 
-
 ;;
 ;; r
 ;;
@@ -1031,6 +1031,10 @@ in addition to notes and stems.")
      (figures ,ly:grob-array? "Figured bass objects for continuation line.")
      (flag ,ly:grob? "A pointer to a @code{Flag} object.")
      (footnote-stencil ,ly:stencil? "The stencil of a system's footnotes.")
+     (footnotes-before-line-breaking ,ly:grob-array? "Footnote grobs of
+a whole system.")
+     (footnotes-after-line-breaking ,ly:grob-array? "Footnote grobs of
+a broken system.")
      (full-score-pure-minimum-translations ,list? "A list of translations
 for a full score's worth of grobs.")
 
@@ -1038,6 +1042,8 @@ for a full score's worth of grobs.")
 column.")
      (grace-spacing ,ly:grob? "A run of grace notes.")
 
+     (has-span-bar ,pair? "A pair of booleans indicating whether a a span bar
+is drawn above, or respectively below, this staff.")
      (heads ,ly:grob-array? "An array of note heads.")
 
      (items-worth-living ,ly:grob-array? "An array of interesting items.  If
@@ -1053,6 +1059,11 @@ empty in a particular staff, then that staff is erased.")
      (left-neighbor ,ly:grob? "The right-most column that has a spacing-wish
 for this column.")
 
+     (melody-spanner ,ly:grob? "The @code{MelodyItem} object for a stem.")
+
+     (neighbors ,ly:grob-array? "The X-axis neighbors of a grob. Used by the
+pure-from-neighbor-interface to determine various grob heights.")
+
      (normal-stems ,ly:grob-array? "An array of visible stems.")
      (note-columns ,ly:grob-array? "An array of @code{NoteColumn} grobs.")
      (note-head ,ly:grob? "A single note head.")
@@ -1095,6 +1106,7 @@ results, use @code{LEFT} and @code{RIGHT}.")
      (stems ,ly:grob-array? "An array of stem objects.")
 
      (tie ,ly:grob? "A pointer to a @code{Tie} object.")
+     (ties ,ly:grob-array? "A grob array of @code{Tie} objects.")
      (tremolo-flag ,ly:grob? "The tremolo object on a stem.")
      (tuplet-number ,ly:grob? "The number for a bracket.")
      (tuplets ,ly:grob-array? "An array of smaller tuplet brackets.")
@@ -1139,6 +1151,9 @@ entries @code{name} and @code{interfaces}.")
      (minimum-distances ,list? "A list of rods that have the format
 @code{(@var{obj} . @var{dist})}.")
 
+     (note-collision ,ly:grob? "The @code{NoteCollision} object of a
+dot column.")
+
      (positioning-done ,boolean? "Used to signal that a positioning element
 did its job.  This ensures that a positioning is only done once.")
      (pure-Y-extent ,number-pair? "The estimated height of a system.")
index 85dd188f5118c961cd3c15144ac60ff858b4fc66..9cb0e6b5e1a6c9818f4c53c8006c858f1c6eef7c 100644 (file)
        (break-align-anchor . ,ly:bar-line::calc-anchor)
        (break-align-symbol . staff-bar)
        (break-visibility . ,bar-line::calc-break-visibility)
+       (extra-spacing-height . ,pure-from-neighbor-interface::account-for-span-bar)
        (gap . 0.4)
        (glyph . "|")
        (glyph-name . ,bar-line::calc-glyph-name)
                        (right-edge . (extra-space . 0.0))))
        (stencil . ,ly:bar-line::print)
        (meta . ((class . Item)
+                 (object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
+                                      (pure-relevant-grobs . ,ly:pure-from-neighbor-interface::calc-pure-relevant-grobs)))
                 (interfaces . (bar-line-interface
                                break-aligned-interface
-                               font-interface))))))
+                               font-interface
+                               pure-from-neighbor-interface))))))
 
     (BarNumber
      . (
        (break-align-anchor . ,ly:break-aligned-interface::calc-extent-aligned-anchor)
        (break-align-symbol . clef)
        (break-visibility . ,begin-of-line-visible)
+       ;(extra-spacing-height . ,pure-from-neighbor-interface::extra-spacing-height)
        (glyph-name . ,ly:clef::calc-glyph-name)
        (non-musical . #t)
        (space-alist . ((cue-clef . (extra-space . 2.0))
        (stencil . ,ly:clef::print)
        (Y-offset . ,ly:staff-symbol-referencer::callback)
        (meta . ((class . Item)
+                 ;(object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
+                 ;                     (pure-relevant-grobs . ,ly:pure-from-neighbor-interface::calc-pure-relevant-grobs)))
                 (interfaces . (break-aligned-interface
                                clef-interface
                                font-interface
+                               ;pure-from-neighbor-interface
                                staff-symbol-referencer-interface))))))
 
     (ClusterSpanner
        (break-align-anchor . ,ly:break-aligned-interface::calc-extent-aligned-anchor)
        (break-align-symbol . cue-clef)
        (break-visibility . ,begin-of-line-visible)
+       ;(extra-spacing-height . ,pure-from-neighbor-interface::extra-spacing-height)
        (font-size . -4)
        (glyph-name . ,ly:clef::calc-glyph-name)
        (non-musical . #t)
        (stencil . ,ly:clef::print)
        (Y-offset . ,ly:staff-symbol-referencer::callback)
        (meta . ((class . Item)
+                 ;(object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
+                 ;                     (pure-relevant-grobs . ,ly:pure-from-neighbor-interface::calc-pure-relevant-grobs)))
                 (interfaces . (break-aligned-interface
                                clef-interface
                                font-interface
+                               ;pure-from-neighbor-interface
                                staff-symbol-referencer-interface))))))
 
     (CueEndClef
        (break-align-anchor . ,ly:break-aligned-interface::calc-extent-aligned-anchor)
        (break-align-symbol . cue-end-clef)
        (break-visibility . ,begin-of-line-invisible)
+       ;(extra-spacing-height . ,pure-from-neighbor-interface::extra-spacing-height)
        (font-size . -4)
        (glyph-name . ,ly:clef::calc-glyph-name)
        (non-musical . #t)
        (stencil . ,ly:clef::print)
        (Y-offset . ,ly:staff-symbol-referencer::callback)
        (meta . ((class . Item)
+                 ;(object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
+                 ;                     (pure-relevant-grobs . ,ly:pure-from-neighbor-interface::calc-pure-relevant-grobs)))
                 (interfaces . (break-aligned-interface
                                clef-interface
                                font-interface
+                               ;pure-from-neighbor-interface
                                staff-symbol-referencer-interface))))))
 
     (Custos
                        (first-note . (fixed-space . 2.5))))
        (stencil . ,ly:key-signature-interface::print)
        (extra-spacing-width . (0.0 . 1.0))
+       ;(extra-spacing-height . ,pure-from-neighbor-interface::extra-spacing-height-including-staff)
        (Y-offset . ,ly:staff-symbol-referencer::callback)
        (meta . ((class . Item)
+                 ;(object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
+                 ;                     (pure-relevant-grobs . ,ly:pure-from-neighbor-interface::calc-pure-relevant-grobs)))
                 (interfaces . (break-aligned-interface
                                font-interface
                                key-signature-interface
+                               ;pure-from-neighbor-interface
                                staff-symbol-referencer-interface))))))
 
 
        (non-musical . #t)
        (stencil . ,ly:span-bar::print)
        (X-extent . ,ly:span-bar::width)
-       (Y-extent . #f)
+       (Y-extent . (+inf.0 . -inf.0))
        (meta . ((class . Item)
                 (interfaces . (bar-line-interface
                                font-interface
 
     (SpanBarStub
      . (
-       (elements-filtered . ,ly:pure-from-neighbor-interface::filter-elements)
         (X-extent . ,grob::x-parent-width)
-       (Y-extent . ,span-bar-stub::height)
+       (Y-extent . ,(ly:make-unpure-pure-container #f ly:axis-group-interface::pure-height))
        (meta . ((class . Item)
                 (object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
-                                     (pure-relevant-grobs . ,ly:axis-group-interface::calc-pure-relevant-grobs)))
+                                     (pure-relevant-grobs . ,ly:pure-from-neighbor-interface::calc-pure-relevant-grobs)))
                 (interfaces . (pure-from-neighbor-interface))))))
 
     (StaffGrouper
        (X-extent . ,ly:axis-group-interface::width)
        (Y-extent . ,ly:system::height)
        (meta . ((class . System)
-                (object-callbacks . ((pure-relevant-grobs . ,ly:system::calc-pure-relevant-grobs)
+                (object-callbacks . ((footnotes-before-line-breaking . ,ly:system::footnotes-before-line-breaking)
+                                     (footnotes-after-line-breaking . ,ly:system::footnotes-after-line-breaking)
+                                     (pure-relevant-grobs . ,ly:system::calc-pure-relevant-grobs)
                                      (pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)))
                 (interfaces . (axis-group-interface
                                system-interface))))))
        (break-align-symbol . time-signature)
        (break-align-anchor-alignment . ,LEFT)
        (break-visibility . ,all-visible)
+       ;(extra-spacing-height . ,pure-from-neighbor-interface::extra-spacing-height-including-staff)
        (extra-spacing-height . (-1.0 . 1.0))
        (extra-spacing-width . (0.0 . 0.8))
        (non-musical . #t)
        (stencil . ,ly:time-signature::print)
        (style . C)
        (meta . ((class . Item)
+                 ;(object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
+                 ;                     (pure-relevant-grobs . ,ly:pure-from-neighbor-interface::calc-pure-relevant-grobs)))
                 (interfaces . (break-aligned-interface
                                font-interface
+                               ;pure-from-neighbor-interface
                                time-signature-interface))))))
 
     (TrillPitchAccidental
     (,ly:side-position-interface::y-aligned-side . ,ly:side-position-interface::pure-y-aligned-side)
     (,ly:slur::height . ,ly:slur::pure-height)
     (,ly:slur::outside-slur-callback . ,ly:slur::pure-outside-slur-callback)
-    (,span-bar-stub::height . ,ly:axis-group-interface::pure-height)
     (,ly:stem::calc-stem-begin-position . ,ly:stem::pure-calc-stem-begin-position)
     (,ly:stem::calc-stem-end-position . ,ly:stem::pure-calc-stem-end-position)
     (,stem::length . ,stem::pure-length)
index a6b910c7db6c60527c3ffeaef8bff2792696f211..8911239473dd3cbf7977843d5bf87d4a6cf726df 100644 (file)
@@ -60,8 +60,10 @@ TODO: Use SpanEvents?")
 cautionary accidental.")
      (change-to-id ,string? "Name of the context to change to.")
      (change-to-type ,symbol? "Type of the context to change to.")
+     (class ,symbol? "The class name of an event class.")
      (compress-procedure ,procedure? "Compress this music expression.
 Arg@tie{}1: the music, arg@tie{}2: factor.")
+     (context ,ly:context? "The context to which an event is sent.")
      (context-id ,string? "Name of context.")
      (context-type ,symbol?  "Type of context.")
      (create-new ,boolean? "Create a fresh context.")
@@ -84,6 +86,7 @@ simultaneous music, or the alternatives of repeated music.")
 a sequential iterator.  Takes a single music parameter.")
      (error-found ,boolean?
                  "If true, a parsing error was found in this expression.")
+     (events ,list? "A list of events contained in this event.")
 
      (figure ,integer? "A bass figure.")
      (footnote-text ,markup? "Text to appear in a footnote.")
@@ -96,6 +99,7 @@ always be printed on this note.")
 property, e.g., @code{(beamed-lengths details)}.")
      (grob-value ,scheme? "The value of the grob property to set.")
 
+     (id ,symbol? "The ID of an event.")
      (input-tag ,scheme? "Arbitrary marker to relate input and output.")
      (inversion ,boolean? "If set, this chord note is inverted.")
      (iterator-ctor ,procedure? "Function to construct a
@@ -111,6 +115,9 @@ This property can only be defined as initializer in
 whether to allow, forbid or force a line break.")
 
      (metronome-count ,number-or-pair? "How many beats in a minute?")
+     (moment ,ly:moment? "The moment at which an event happens.")
+     (music-cause ,ly:music? "The music object that is the cause of
+an event.")
 
      (name ,symbol? "Name of this music object.")
      (no-continuation ,boolean? "If set, disallow continuation lines.")
@@ -119,6 +126,8 @@ whether to allow, forbid or force a line break.")
      (octavation ,integer? "This pitch was octavated by how many octaves?
 For chord inversions, this is negative.")
      (once ,boolean? "Apply this operation only during one time step?")
+     (ops ,scheme? "The operations to apply during the creation of a
+context.")
      (origin ,ly:input-location? "Where was this piece of music defined?")
      (original-chord ,ly:music? "Original chord of a repeated chord.
 Used by repeated chords in \\relative mode, to determine the first note octave")
@@ -146,6 +155,7 @@ on some grob property.")
 It must take a single argument, being the context.")
      (property-operations ,list? "Do these operations for instantiating
 the context.")
+     (property-path ,symbol? "The path of a property.")
 
      (quoted-context-id ,string? "The ID of the context to direct quotes to,
 e.g., @code{cue}.")
index 82703e14ac1f0bc9ae336ce5f227aa274edcae41..57f89eeb3bab661fa1661c2dff0b6d8fe3d3c012 100644 (file)
@@ -390,9 +390,40 @@ and duration-log @var{log}."
           (equal? (ly:item-break-dir g) RIGHT))
       (ly:grob-translate-axis! g 3.5 X)))
 
-(define-public (span-bar-stub::height grob)
-  (ly:grob-property grob 'elements-filtered)
-  (ly:axis-group-interface::height grob))
+(define-public (pure-from-neighbor-interface::extra-spacing-height grob)
+  (let* ((height (ly:grob::stencil-height grob))
+         (from-neighbors (interval-union
+                            height
+                            (ly:axis-group-interface::pure-height
+                              grob
+                              0
+                              10000000))))
+    (coord-operation - from-neighbors height)))
+
+(define-public (pure-from-neighbor-interface::account-for-span-bar grob)
+  (define (other-op x) (x (cons cdr car)))
+  (let* ((esh (pure-from-neighbor-interface::extra-spacing-height grob))
+         (hsb (ly:grob-property grob 'has-span-bar)))
+    (if (pair? hsb)
+      (cons-map
+        (lambda (x)
+          (if (and ((other-op x) hsb)
+                   (not (and (eq? x car)
+                             (not (ly:grob-property grob 'allow-span-bar)))))
+              (x esh)
+              0))
+        (cons car cdr))
+      '(0 . 0))))
+
+(define (pure-from-neighbor-interface::extra-spacing-height-including-staff grob)
+  (let ((esh (pure-from-neighbor-interface::extra-spacing-height grob))
+        (to-staff (coord-operation -
+                                   (interval-widen
+                                     '(0 . 0)
+                                     (ly:staff-symbol-staff-radius grob))
+                                   (ly:grob::stencil-height grob))))
+    (interval-union esh to-staff)))
+
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Tuplets