Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-03-09 Thread Pavel Roskin

Quoting Carl Sorensen c_soren...@byu.edu:


Ordinarily something like this would go in the regression tests:

input/regression/cross-staff-stem-engraver.ly

And then you would add it to your git repository, and post it on Rietveld
for a review.


OK, I'll do it.

--
Regards,
Pavel Roskin

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-03-08 Thread Pavel Roskin

Hello, Janek!

Quoting Janek Warchoł janek.lilyp...@gmail.com:


I'm attaching the Scheme code with a convoluted example (3 staves with
3 voices on each).


I don't have the time to dive into details, but the output looks good!
Be sure to post it on Rietveld for a review when you come back.


Thank you for your appreciation!  Sorry for delay - too little time  
and slow Internet here.  Could you please explain how I can put a  
standalone file to Rietveld?  I guess you mean that it should be added  
to Lilypond source?  You mean the ly directory or some place in the  
documentation?


--
Regards,
Pavel Roskin

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-03-08 Thread Carl Sorensen
On 3/8/12 2:44 PM, Pavel Roskin pro...@gnu.org wrote:

Hello, Janek!

Quoting Janek Warchoł janek.lilyp...@gmail.com:

 I'm attaching the Scheme code with a convoluted example (3 staves with
 3 voices on each).

 I don't have the time to dive into details, but the output looks good!
 Be sure to post it on Rietveld for a review when you come back.

Thank you for your appreciation!  Sorry for delay - too little time
and slow Internet here.  Could you please explain how I can put a
standalone file to Rietveld?  I guess you mean that it should be added
to Lilypond source?  You mean the ly directory or some place in the
documentation?

Ordinarily something like this would go in the regression tests:

input/regression/cross-staff-stem-engraver.ly

And then you would add it to your git repository, and post it on Rietveld
for a review.

HTH,

Carl


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-03-06 Thread Janek Warchoł
On Sat, Mar 3, 2012 at 5:26 AM, Pavel Roskin pro...@gnu.org wrote:
 Hello!

 On Tue, 28 Feb 2012 15:14:29 -0800
 Joe Neeman joenee...@gmail.com wrote:

 Don't use ly:axis-group-interface::add-element, because stems don't
 implement the axis-group-interface. (Removing this will also remove
 the axes warning.) Instead, use ly:grob-set-parent!. You'll probably
 want to set both the X parent and the Y parent. Then the X and Y
 offsets of new-stem will be measured relative to stem (instead of
 relative to the whole system, which is the default).

 OK, this is what I have now.  I think it's a pretty good solution.

 The code is capable of creating multiple cross-staff stems per moment.
 It's possible to connect more than two stems as long as they lie on the
 same line.  There is a workaround for a problem with Lilypond 2.14 that
 has a flag as part of the stem.

 I spent way too much time on the issue, but it helped me learn
 Scheme and Lilypond.

 I'll be on vacation next two weeks, so I'm not sure I'll be able to
 work on Lilypond.  So I want to post my results.

 I tried posting this on LSR, but it won't accept it.  I get a popup
 message saying empty output from Lilypond or something like that.  I
 guess it's because LSR still runs Lilypond 2.12.x.  Unfortunately, I
 have no time for backports.

 I'm attaching the Scheme code with a convoluted example (3 staves with
 3 voices on each).

I don't have the time to dive into details, but the output looks good!
Be sure to post it on Rietveld for a review when you come back.

cheers,
Janek

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-03-03 Thread Joe Neeman
On Fri, Mar 2, 2012 at 8:26 PM, Pavel Roskin pro...@gnu.org wrote:

 Hello!

 On Tue, 28 Feb 2012 15:14:29 -0800
 Joe Neeman joenee...@gmail.com wrote:

  Don't use ly:axis-group-interface::add-element, because stems don't
  implement the axis-group-interface. (Removing this will also remove
  the axes warning.) Instead, use ly:grob-set-parent!. You'll probably
  want to set both the X parent and the Y parent. Then the X and Y
  offsets of new-stem will be measured relative to stem (instead of
  relative to the whole system, which is the default).

 OK, this is what I have now.  I think it's a pretty good solution.


Looks good!



 The code is capable of creating multiple cross-staff stems per moment.
 It's possible to connect more than two stems as long as they lie on the
 same line.  There is a workaround for a problem with Lilypond 2.14 that
 has a flag as part of the stem.

 I spent way too much time on the issue, but it helped me learn
 Scheme and Lilypond.


That's ok; now that you're an expert, your next feature will be much
easier...

Cheers,
Joe
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-03-02 Thread Pavel Roskin
Hello!

On Tue, 28 Feb 2012 15:14:29 -0800
Joe Neeman joenee...@gmail.com wrote:

 Don't use ly:axis-group-interface::add-element, because stems don't
 implement the axis-group-interface. (Removing this will also remove
 the axes warning.) Instead, use ly:grob-set-parent!. You'll probably
 want to set both the X parent and the Y parent. Then the X and Y
 offsets of new-stem will be measured relative to stem (instead of
 relative to the whole system, which is the default).

OK, this is what I have now.  I think it's a pretty good solution.

The code is capable of creating multiple cross-staff stems per moment.
It's possible to connect more than two stems as long as they lie on the
same line.  There is a workaround for a problem with Lilypond 2.14 that
has a flag as part of the stem.

I spent way too much time on the issue, but it helped me learn
Scheme and Lilypond.

I'll be on vacation next two weeks, so I'm not sure I'll be able to
work on Lilypond.  So I want to post my results.

I tried posting this on LSR, but it won't accept it.  I get a popup
message saying empty output from Lilypond or something like that.  I
guess it's because LSR still runs Lilypond 2.12.x.  Unfortunately, I
have no time for backports.

I'm attaching the Scheme code with a convoluted example (3 staves with
3 voices on each).

The Scheme part is already used here:
https://gitorious.org/lilypond-music/lilypond-music/blobs/master/Schubert/Die_Forelle.ly

-- 
Regards,
Pavel Roskin
\version 2.14.2

% Values are close enough to ignore the difference
#(define (close-enough? x y)
   ( (abs (- x y)) 0.0001))

% Combine a list of extents
#(define (extent-combine extents)
   (if (pair? (cdr extents))
   (interval-union (car extents) (extent-combine (cdr extents)))
   (car extents)))

% Workaround for Lilypond 2.14 - calculate X-extent without the flag
#(define (stem-x-extent stem)
   (let* ((extent (ly:grob-extent stem stem X))
  (layout (ly:grob-layout stem))
  (thickness (ly:grob-property stem 'thickness))
  (thickness-unit (ly:output-def-lookup layout 'line-thickness)))
 (cons (car extent) (+ (car extent) (* thickness thickness-unit)

% Check if the stem is connectable to the stem span
#(define ((stem-connectable? ref anchor) stem)
   (cond
((inf? (car (ly:grob-extent stem ref X))) #f)
((not (close-enough? (car (ly:grob-extent anchor ref X))
(car (ly:grob-extent stem ref X #f)
(( 0 (* (ly:grob-property anchor 'direction)
(- (car (ly:grob-extent anchor ref Y))
  (car (ly:grob-extent stem ref Y) #f)
(else #t)))

% Connect stems if we have at least two visible stems and the anchor stem
% is one of them
#(define (stem-span-stencil span)
   (let* ((system (ly:grob-system span))
  (anchor (ly:grob-parent span X))
  (stems (filter (stem-connectable? system anchor)
 (ly:grob-object span 'stems
 (if (and (pair? stems)
  (pair? (cdr stems))
  (memq anchor stems))
 (let* ((yextents (map (lambda (st)
 (ly:grob-extent st system Y)) stems))
(yextent (extent-combine yextents))
(layout (ly:grob-layout anchor))
(blot (ly:output-def-lookup layout 'blot-diameter)))
   ; Hide spanned stems, but only if it won't hide flags
   (map (lambda (st)
  (if (close-enough? (cdr (stem-x-extent st))
(cdr (ly:grob-extent st st X)))
  (set! (ly:grob-property st 'transparent) #t)))
 stems)
   (ly:round-filled-box (stem-x-extent anchor) yextent blot))
 #f)))

% Create a stem span as a child of the cross-staff stem (the anchor)
#(define ((make-stem-span! stems trans) anchor)
   (let* ((span (ly:engraver-make-grob trans 'Stem '(
 (ly:grob-set-parent! span X anchor)
 (set! (ly:grob-object span 'stems) stems)
 (set! (ly:grob-property span 'X-offset) 0)
 (set! (ly:grob-property span 'stencil) stem-span-stencil)))

% Create stem spans for cross-staff stems
#(define (make-stem-spans! ctx stems trans)
   (if (and (pair? stems)
(pair? (cdr stems)))
   (let* ((anchors (filter (lambda (st)
 (ly:grob-property st 'cross-staff)) stems)))
 (map (make-stem-span! stems trans) anchors

% Connect cross-staff stems to the stems above in the system
#(define (Span_stem_engraver ctx)
   (let ((stems '()))
 `((acknowledgers
(stem-interface
 . ,(lambda (trans grob source)
  (set! stems (cons grob stems)
   (process-acknowledged
. ,(lambda (trans)
 (make-stem-spans! ctx stems trans)
 (set! stems '()))

\layout {
  \context {
\StaffGroup
\consists #Span_stem_engraver
  }
}

\score {
  \new StaffGroup 
\new Staff = staffone 
  \new Voice {
\voiceOne c''4
  }
 

Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-02-28 Thread Pavel Roskin
Hello, Joe!

 In case it wasn't clear from what I said before, engravers in lilypond
 don't do the actual layout. Instead, they build the grobs and set up
 the connections between them. Most of the layout is done in callback
 functions.

Yes, I realize it now.  Your suggestion to create a new stem worked
fine.  I can get the relative coordinates of themes on different staves.

The attached file shows where I am now.  There are many minor issues,
and I hope I can solve most of them, but I'll certainly appreciate help.

I'm getting a message Weird stem, and I don't see how to avoid it.
Perhaps I should create a totally new grob with a unique name, such as
StemSpan.  Is that possible?  What would be needed?

How do I make the new stem start at the same point as the original
one.  I tried this:

(set! (ly:grob-property stem 'axes) (list X))

I don't understand how it works.  Not setting axes leads to:

programming error: axes should be nonempty

Maybe I should use some other parent for the new stem?

The new stems are offset horizontally on purpose to see where they
are.  But the vertical position is wrong and I don't see how to fix it.

I'll appreciate if you have a look at the code.

-- 
Regards,
Pavel Roskin
\version 2.14.2

#(define (grobParentByInterface grob intf axis)
   (if (memq intf (ly:grob-interfaces grob))
   grob
   (grobParentByInterface (ly:grob-parent grob axis) intf axis)))

#(define (extent-combine extents)
   (if (pair? (cdr extents))
   (interval-union (car extents) (extent-combine (cdr extents)))
   (car extents)))

#(define (crossStaffLength grob)
   (let* ((papercolumn (grobParentByInterface grob 'paper-column-interface X))
  (elementarray (ly:grob-object papercolumn 'elements))
  (yref (ly:grob-common-refpoint-of-array grob elementarray Y))
  (elements (ly:grob-object grob 'spanned-stems))
  (extents (map (lambda (x) (ly:grob-extent x yref Y)) elements))
  (foo (ly:message extents: ~a extents))
  (maxextent (extent-combine extents))
  (foo (ly:message maxextent: ~a maxextent))
  (length (- (car maxextent) (cdr maxextent
 length))

#(define (stemSpanStencil grob)
   (let* ((length (crossStaffLength grob)))
 (make-filled-box-stencil '(0.2 . 0.4) (cons 0 length

#(define (reparentStems ctx stems trans)
   (if (and (pair? stems) (pair? (cdr stems)))
   (let* ((stem (car stems))
  (parent (ly:grob-parent stem Y))
  (new-stem (ly:engraver-make-grob trans 'Stem '(
 (ly:message stems: ~a stems)
 (ly:message new-stem: ~a new-stem)
 (set! (ly:grob-property stem 'axes) (list X))
 (ly:axis-group-interface::add-element stem new-stem)
 (set! (ly:grob-object new-stem 'spanned-stems) stems)
 (set! (ly:grob-object new-stem 'Y-offset) 5)
 (set! (ly:grob-property new-stem 'stencil) stemSpanStencil)
 (set! (ly:grob-property new-stem 'length) crossStaffLength

#(define (Cross_staff_stem_engraver ctx)
  (let ((stems '()))

`((acknowledgers
   (stem-interface
. ,(lambda (trans grob source)
 (set! stems (cons grob stems)

  (process-acknowledged
   . ,(lambda (trans)
 (reparentStems ctx stems trans) (set! stems '()))

\layout {
  \context {
\PianoStaff
\consists #Cross_staff_stem_engraver
  }
}

\new PianoStaff 
  \new Staff { \stemUp g' a' b' c'' }
  \new Staff { \stemUp f' e' f' d' r }

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-02-28 Thread Joe Neeman
On Tue, Feb 28, 2012 at 2:56 PM, Pavel Roskin pro...@gnu.org wrote:

 Hello, Joe!

  In case it wasn't clear from what I said before, engravers in lilypond
  don't do the actual layout. Instead, they build the grobs and set up
  the connections between them. Most of the layout is done in callback
  functions.

 Yes, I realize it now.  Your suggestion to create a new stem worked
 fine.  I can get the relative coordinates of themes on different staves.

 The attached file shows where I am now.  There are many minor issues,
 and I hope I can solve most of them, but I'll certainly appreciate help.

 I'm getting a message Weird stem, and I don't see how to avoid it.
 Perhaps I should create a totally new grob with a unique name, such as
 StemSpan.  Is that possible?  What would be needed?


It would be nice to do this eventually, because having a different grob
name would make it easier for the users to tweak it. I think it would
involve editing scm/define-grobs.scm, but there may be a way to do it from
an .ly file also


 How do I make the new stem start at the same point as the original
 one.  I tried this:

 (set! (ly:grob-property stem 'axes) (list X))

 I don't understand how it works.  Not setting axes leads to:

 programming error: axes should be nonempty


Don't use ly:axis-group-interface::add-element, because stems don't
implement the axis-group-interface. (Removing this will also remove the
axes warning.) Instead, use ly:grob-set-parent!. You'll probably want to
set both the X parent and the Y parent. Then the X and Y offsets of
new-stem will be measured relative to stem (instead of relative to the
whole system, which is the default).

Cheers,
Joe
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-02-28 Thread Pavel Roskin

Quoting Joe Neeman joenee...@gmail.com:


I'm getting a message Weird stem, and I don't see how to avoid it.
Perhaps I should create a totally new grob with a unique name, such as
StemSpan.  Is that possible?  What would be needed?



It would be nice to do this eventually, because having a different grob
name would make it easier for the users to tweak it. I think it would
involve editing scm/define-grobs.scm, but there may be a way to do it from
an .ly file also


It turn out the message comes from ly:stem::offset-callback and can be  
suppressed by redefining X-offset.



Don't use ly:axis-group-interface::add-element, because stems don't
implement the axis-group-interface. (Removing this will also remove the
axes warning.) Instead, use ly:grob-set-parent!. You'll probably want to
set both the X parent and the Y parent. Then the X and Y offsets of
new-stem will be measured relative to stem (instead of relative to the
whole system, which is the default).


That was it!  I have a working solution now!  It will need to be  
improved to ignore rests and other invisible stems, check the  
cross-staff property, avoid connecting incompatible stems and so on.   
The old stems should be made transparent.  But all that should be  
simple.  Once I have something easy to use, I plan to submit it to the  
LSR.


Actually, it looks like System is a better Y-parent for the new stem.   
Perhaps I'll try to use the common reference of the stems to be  
connected.


Here's what I have now (attached).

Thank you for your help!

--
Regards,
Pavel Roskin

\version 2.14.2

#(define (extent-combine extents)
   (if (pair? (cdr extents))
   (interval-union (car extents) (extent-combine (cdr extents)))
   (car extents)))

#(define (stemSpanExtent grob)
   (let* ((yref (ly:grob-parent grob Y))
  (stems (ly:grob-object grob 'spanned-stems))
  (extents (map (lambda (x) (ly:grob-extent x yref Y)) stems))
  (maxextent (extent-combine extents)))
 maxextent))

#(define (stemSpanStencil grob)
   (let* ((stem (car (ly:grob-object grob 'spanned-stems)))
  (xextent (ly:grob-extent stem stem X))
  (yextent (stemSpanExtent grob)))
 (make-filled-box-stencil xextent yextent)))

#(define (reparentStems ctx stems trans)
   (if (and (pair? stems)
(pair? (cdr stems)))
   (let* ((stem (car stems))
  (new-stem (ly:engraver-make-grob trans 'Stem '(
 (ly:grob-set-parent! new-stem X stem)
 (set! (ly:grob-object new-stem 'spanned-stems) stems)
 (set! (ly:grob-property new-stem 'X-offset) 0)
 (set! (ly:grob-property new-stem 'stencil) stemSpanStencil

#(define (Cross_staff_stem_engraver ctx)
  (let ((stems '()))

`((acknowledgers
   (stem-interface
. ,(lambda (trans grob source)
 (set! stems (cons grob stems)

  (process-acknowledged
   . ,(lambda (trans)
 (reparentStems ctx stems trans) (set! stems '()))

\layout {
  \context {
\PianoStaff
\consists #Cross_staff_stem_engraver
  }
}

\new PianoStaff 
  \new Staff { \stemUp g'4 a' b' g' \stemDown c' }
  \new Staff { \stemUp f'4 e' f' d' \stemDown c''' }

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-02-26 Thread Joe Neeman
On Sat, Feb 25, 2012 at 9:14 PM, Pavel Roskin pro...@gnu.org wrote:


 Using an engraver to catch the stems sounds like a good approach.  I could
 even do some filtering in the engraver.  And it should be possible to
 replace stems with one rather than connect them together.  It would
 simplify SVG output, and I suspect the connect point could be seen in some
 renderings of the postscript output.

 Thank you for your help!  I'm getting really close!


Glad to hear it :)

In case it wasn't clear from what I said before, engravers in lilypond
don't do the actual layout. Instead, they build the grobs and set up the
connections between them. Most of the layout is done in callback functions.

Cheers,
Joe
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-02-25 Thread Joe Neeman
On Fri, Feb 24, 2012 at 3:04 PM, Pavel Roskin pro...@gnu.org wrote:

 Hello!

 I'm moving the thread to lilypond-devel and changing the subject.  I
 hope it's OK.

 On Thu, 23 Feb 2012 15:37:02 +
 Neil Puttock n.putt...@gmail.com wrote:

  On 23 February 2012 15:18, Pavel Roskin pro...@gnu.org wrote:
 
   I actually wanted to make a stopgap solution in Scheme.  I wrote
   some code that looks for other stems and sets the stem length so it
   would touch the stem above (attached).  But the code is unreliable.
Some stems are not calculated yet.
 
  I'd love to be proven wrong, but I don't think you'll have much
  success with this approach.  At the very least, you'll need a Scheme
  engraver at the correct context level to collect the stems at the
  right time.  If a simple length override were possible with the
  current infrastructure, we wouldn't need a Span_arpeggio_engraver to
  typeset cross-staff arpeggios.

 I tried writing an engraver that could be added to the StaffGroup
 context.  Collecting stems is not a problem.  The problem is that even
 the finalize handler is called to early.  I'm getting a warning:

 programming error: vertical alignment called before line breaking
 continuing, cross fingers


It's a little hard to guess what's going wrong without knowing exactly what
you've tried. Are you trying to do layout logic in the engraver (ie. by
collecting stems, reading their properties, doing calculations and then
setting more properties)? If you are, the first step is to move the layout
logic into grob callbacks.

Cheers,
Joe
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-02-25 Thread Pavel Roskin

Quoting Joe Neeman joenee...@gmail.com:


On Fri, Feb 24, 2012 at 3:04 PM, Pavel Roskin pro...@gnu.org wrote:

I tried writing an engraver that could be added to the StaffGroup
context.  Collecting stems is not a problem.  The problem is that even
the finalize handler is called to early.  I'm getting a warning:

programming error: vertical alignment called before line breaking
continuing, cross fingers



It's a little hard to guess what's going wrong without knowing exactly what
you've tried. Are you trying to do layout logic in the engraver (ie. by
collecting stems, reading their properties, doing calculations and then
setting more properties)?


Yes, except that I'm not setting anything yet.  The current file is attached.


If you are, the first step is to move the layout
logic into grob callbacks.


I guess I'll need to redefine the stencil for stems.  Or maybe you  
mean some other callback?


I did a similar thing before.  I put all the logic into a function  
that was used to redefine the length property of the stem.  It worked  
for a simple case of two stems, but failed when three stems needed to  
be connected.  The Y-extent for at least one of the stems wasn't  
calculated yet.


I also tried setting Y-extent, but it's read-only.

--
Regards,
Pavel Roskin

\version 2.14.0

#(define ((connectableStem? xref yref stemone dir below?) stemtwo)
   (cond
((not (memq 'stem-interface (ly:grob-interfaces stemtwo))) #f)
((equal? stemone stemtwo) #f)
((inf? (car (ly:grob-extent stemtwo xref X))) #f)
((not (equal? (ly:grob-extent stemone xref X) (ly:grob-extent stemtwo xref X))) #f)
((not (equal? dir (ly:grob-property stemtwo 'direction))) #f)
((below? (car (ly:grob-extent stemtwo yref Y)) (car (ly:grob-extent stemone yref Y))) #f)
(else #t)))

#(define ((lowerStem? ref below?) lower upper)
   (below? (car (ly:grob-extent lower ref Y)) (car (ly:grob-extent upper ref Y

#(define (crossStaffLength grob ctx stems)
   (let* ((papercolumn (ly:context-property ctx 'currentMusicalColumn))
  (foo (ly:message papercolumn: ~a papercolumn))
  (xref (ly:grob-common-refpoint grob (car stems) X))
  (foo (ly:message xref: ~a xref))
  (yref (ly:grob-common-refpoint grob (car stems) Y))
  (foo (ly:message yref: ~a yref))
  (dir (ly:grob-property grob 'direction))
  (below? (if (= dir UP)  ))
  (foo (ly:message stems: ~a stems))
  (goodstems (filter (connectableStem? xref yref grob dir below?) stems))
  (foo (ly:message stems1: ~a stems))
  (beststems (sort goodstems (lowerStem? yref below?)))
  (foo (ly:message stems2: ~a stems))
  (peer (car (append beststems (list grob
  (foo (ly:message peer: ~a peer)))
 (ly:message grob: ~a (ly:grob-extent grob yref Y))
 (ly:message peer: ~a (ly:grob-extent peer yref Y))
 (if (equal? grob peer)
 (begin (ly:programming-error No suitable stem to connect to) 4)
 (if ( dir 0)
 (* 2 (- (car (ly:grob-extent peer yref Y))
 (car (ly:grob-extent grob yref Y
 (* 2 (- (cdr (ly:grob-extent grob yref Y))
 (cdr (ly:grob-extent peer yref Y

#(define (connectStems ctx stems)
   (ly:message stems: ~a stems)
   (if (and (pair? stems) (pair? (cdr stems)))
   (let ((stem (car stems))
 (otherstems (cdr stems)))
 (ly:message otherstems: ~a otherstems)
 (ly:message length: ~a
   (crossStaffLength stem ctx otherstems)

#(define (Cross_staff_stem_engraver ctx)
  (let ((stems '()))

`((acknowledgers
   (stem-interface
. ,(lambda (trans grob source)
 (ly:message acknowledger: ~a grob)
 (set! stems (cons grob stems

   (note-column-interface
. ,(lambda (trans grob source)
 (ly:message acknowledger: ~a grob

  (process-music
   . ,(lambda (trans) (ly:message process-music) (connectStems ctx stems)))

  (process-acknowledged
   . ,(lambda (trans) (ly:message process-acknowledged)))

  (stop-translation-timestep
   . ,(lambda (trans) (ly:message stop-translation-timestep) (connectStems ctx stems)))

  (finalize
   . ,(lambda (trans) (ly:message finalize) (connectStems ctx stems))

\layout {
  \context {
\StaffGroup
\consists #Cross_staff_stem_engraver
  }
}

\new StaffGroup 
  \new Staff { g' }
  \new Staff { c' }

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-02-25 Thread Joe Neeman
On Sat, Feb 25, 2012 at 5:49 AM, Pavel Roskin pro...@gnu.org wrote:

 Quoting Joe Neeman joenee...@gmail.com:

  On Fri, Feb 24, 2012 at 3:04 PM, Pavel Roskin pro...@gnu.org wrote:

 I tried writing an engraver that could be added to the StaffGroup
 context.  Collecting stems is not a problem.  The problem is that even
 the finalize handler is called to early.  I'm getting a warning:

 programming error: vertical alignment called before line breaking
 continuing, cross fingers


 It's a little hard to guess what's going wrong without knowing exactly
 what
 you've tried. Are you trying to do layout logic in the engraver (ie. by
 collecting stems, reading their properties, doing calculations and then
 setting more properties)?


 Yes, except that I'm not setting anything yet.  The current file is
 attached.


The problem is where you ask for the stem Y-extent relative to yref (the
VerticalAlignment): in order to calculate the vertical distance between the
various staves and the VerticalAlignment, line breaking needs to be done
first. (And line breaking doesn't happen until after all the engravers are
done.)



  If you are, the first step is to move the layout
 logic into grob callbacks.


 I guess I'll need to redefine the stencil for stems.  Or maybe you mean
 some other callback?


You could redefine stencil, but I think redefining length would be easier.

I did a similar thing before.  I put all the logic into a function that was
 used to redefine the length property of the stem.  It worked for a simple
 case of two stems, but failed when three stems needed to be connected.  The
 Y-extent for at least one of the stems wasn't calculated yet.


Can you be more specific about the Y-extent not being calculated yet? When
you ask for the Y-extent, the calculation should be triggered if it hasn't
already happened.

As a general approach, setting length sounds like a reasonable way forward.
In addition, I'd suggest creating a new stem in process-acknowledged, then
doing

(set! (ly:grob-object new-stem 'spanned-stems) old-stems)
(set! (ly:grob-property new-stem 'length) your-length-callback)

In your length callback, you read back the spanned-stems object and do your
calculations. Is this similar to what you tried?

Cheers,
Joe
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Cross-staff stem engraver (was: New frog in an empty pond?)

2012-02-25 Thread Pavel Roskin

Quoting Joe Neeman joenee...@gmail.com:


The problem is where you ask for the stem Y-extent relative to yref (the
VerticalAlignment): in order to calculate the vertical distance between the
various staves and the VerticalAlignment, line breaking needs to be done
first. (And line breaking doesn't happen until after all the engravers are
done.)


I see.  It means that using an engraver it not a solution per se.   
Good to know.



I guess I'll need to redefine the stencil for stems.  Or maybe you mean
some other callback?



You could redefine stencil, but I think redefining length would be easier.


Of course, that's why I tried it first :)


Can you be more specific about the Y-extent not being calculated yet? When
you ask for the Y-extent, the calculation should be triggered if it hasn't
already happened.


Now I understand what's going on!  Apparently this triggering is not  
transitive (which is reasonable).  I'm attaching the minimal example  
when I get


grob Y-extent: (+inf.0 . -inf.0)

I think it should be possible to work around.  I'll be perfectly fine  
if my code doesn't work with chords spanning three and more staves if  
that limitation needs to exist.



As a general approach, setting length sounds like a reasonable way forward.
In addition, I'd suggest creating a new stem in process-acknowledged, then
doing

(set! (ly:grob-object new-stem 'spanned-stems) old-stems)
(set! (ly:grob-property new-stem 'length) your-length-callback)

In your length callback, you read back the spanned-stems object and do your
calculations. Is this similar to what you tried?


I never used an engraver and the length override at the save time.   
When doing the length override, I tried this approach:


- find PaperColumn among the grob parents
- scan its elements for stems
- use stems extents to find stems connectable to the given stem
- find the closest stem
- use the length that would extend the given stem to the closest stem

It actually worked, except some corner cases.  Now I understand why.

Using an engraver to catch the stems sounds like a good approach.  I  
could even do some filtering in the engraver.  And it should be  
possible to replace stems with one rather than connect them together.   
It would simplify SVG output, and I suspect the connect point could be  
seen in some renderings of the postscript output.


Thank you for your help!  I'm getting really close!

--
Regards,
Pavel Roskin

\version 2.14.2

#(define (grobParentByInterface grob intf axis)
   (if (memq intf (ly:grob-interfaces grob))
   grob
   (grobParentByInterface (ly:grob-parent grob axis) intf axis)))

#(define (crossStaffLength grob)
   (let* ((papercolumn (grobParentByInterface grob 'paper-column-interface X))
  (elementarray (ly:grob-object papercolumn 'elements))
  (elements (ly:grob-array-list elementarray)))
 (map (lambda (x)
(ly:grob-extent x x Y)) elements)
 (ly:message grob Y-extent: ~a (ly:grob-extent grob grob Y))
 3))

\score {
  \new StaffGroup 
\new Staff = staffone 
  \new Voice {
c'
  }

\new Staff = stafftwo 
  \new Voice {
\clef bass
\override Stem #'cross-staff = ##t
\override Stem #'length = #crossStaffLength
c
  }

\new Staff = staffthree 
  \new Voice {
\override Stem #'cross-staff = ##t
\override Stem #'length = #crossStaffLength
\clef bass
g,
  }

  
  \layout { }
}
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel