Re: Cross-staff stem engraver (was: New frog in an empty pond?)
Quoting Carl Sorensen : 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?)
On 3/8/12 2:44 PM, "Pavel Roskin" wrote: >Hello, Janek! > >Quoting Janek Warchoł : > >>> 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?)
Hello, Janek! Quoting Janek Warchoł : 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?)
On Sat, Mar 3, 2012 at 5:26 AM, Pavel Roskin wrote: > Hello! > > On Tue, 28 Feb 2012 15:14:29 -0800 > Joe Neeman 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?)
On Fri, Mar 2, 2012 at 8:26 PM, Pavel Roskin wrote: > Hello! > > On Tue, 28 Feb 2012 15:14:29 -0800 > Joe Neeman 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?)
Hello! On Tue, 28 Feb 2012 15:14:29 -0800 Joe Neeman 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?)
Quoting Joe Neeman : 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' \stemDown c' } \new Staff { \stemUp f'4 e' \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?)
On Tue, Feb 28, 2012 at 2:56 PM, Pavel Roskin 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?)
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' 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?)
On Sat, Feb 25, 2012 at 9:14 PM, Pavel Roskin 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?)
Quoting Joe Neeman : 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
Re: Cross-staff stem engraver (was: New frog in an empty pond?)
On Sat, Feb 25, 2012 at 5:49 AM, Pavel Roskin wrote: > Quoting Joe Neeman : > > On Fri, Feb 24, 2012 at 3:04 PM, Pavel Roskin 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?)
Quoting Joe Neeman : On Fri, Feb 24, 2012 at 3:04 PM, Pavel Roskin 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?)
On Fri, Feb 24, 2012 at 3:04 PM, Pavel Roskin 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 wrote: > > > On 23 February 2012 15:18, Pavel Roskin 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
Cross-staff stem engraver (was: New frog in an empty pond?)
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 wrote: > On 23 February 2012 15:18, Pavel Roskin 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 Even though the common reference for the Y axis for the stems is a valid grob of type VerticalAlignment, the Y-extents don't reflect the distance between staves. The code for cross-staff arpeggios (span-arpeggio-engraver.cc) only collects data about stems and lets the standard arpeggio code (arpeggio.cc) draw the cross-staff arpeggio. If I understand correctly, an equivalent of that for stems would require modification to the stem engraver so that it would draw cross-staff stems. As for the grid lines, there are actually two engravers. One makes the points (GridPoint) and the other connects them (GridLine). GridPoint should be hopefully irrelevant, as we have stems for that. Printing grid lines is implemented in grid-line-interface.cc. As I understand, it's not an engraver (that would be grid-line-span-engraver.cc), it's an interface. I checked the Scheme implementation of ambitus engraver. It relies on existing implementations of AmbitusLine and other grobs. The placement is done in finalize, but ambitus is not cross-staff, so there is no issue with undefined staff-staff spacing. I guess I need to rely on an existing grob that supports cross-staff placement to draw the stem if I want to keep my modification in Scheme. Perhaps I'll try GridLine, as Arpeggio would need bogus stems and bogus noteheads to appear where I want it to be. BarLine also seems to be too sophisticated for the job. Maybe there is an example of grob definition in Scheme? I'll appreciate comments that would steer me away from dead ends. -- Regards, Pavel Roskin ___ lilypond-devel mailing list lilypond-devel@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-devel