[prev in list] [next in list] [prev in thread] [next in thread] 

List:       lilypond-user
Subject:    Re: Start, end, gradient of Glissando
From:       Thomas Morley <thomasmorley65 () gmail ! com>
Date:       2021-10-30 13:54:21
Message-ID: CABsfGyWUm67GixbVWeyew-j215wQFdtT4yX9c1Z8v95OemW2SA () mail ! gmail ! com
[Download RAW message or body]

Am So., 24. Okt. 2021 um 18:19 Uhr schrieb Aaron Hill
<lilypond@hillvisions.com>:
>
> On 2021-10-24 8:05 am, Thomas Morley wrote:
> > Am So., 24. Okt. 2021 um 16:35 Uhr schrieb Aaron Hill
> >> Let me take a stab at converting the print routine to Scheme.
> >
> > Would be great !!
>
> Sorry for the delay... took me some time to figure out why there was an
> error with cross-staff right-side Y coordinate after porting the logic
> from C++.  I think it's all working as expected, and I have added a few
> new test cases.  (NOTE: I only have 2.22.0 here, so I had to fixup the
> version statement.)  The one thing I did not include was adjusting the
> line ends for the arrows, but I assume the crosses would be best put at
> the arrow points anyway.
>
> There is something to note in the original logic.  If the padding is
> large enough that the line spanner would disappear, then the stencil
> returned is null.  I am also likewise returning the empty list instead
> of a point-pair, so the code that consumes the results would need to
> check for this case.
>
>
> -- Aaron Hill

Hi Aaron,

I now found the time to take a closer look.

There is a problem with cross-staff Glissando and non-zero paddings,
cause by taking relative coords in X/Y direction into account too
late.
Afaict, this needs to come first - changed.

Furthermore, to avoid the "Pythagorean theorem" I changed
         (span-length (sqrt (+ (* span-dx span-dx) (* span-dy span-dy)))))
to
             (span-length (ly:length span-dx span-dy)))


Some other questions, (I attach the file with my comments and TODOs,
etc as well):

(1) You do
           (common-x (ly:grob-common-refpoint grob
                      (ly:grob-common-refpoint
                       (ly:spanner-bound grob LEFT)
                       (ly:spanner-bound grob RIGHT) X) X))
Though, the inner ly:grob-common-refpoint returns already the System
grob, why searching again?
Furthermore, a bit later there's
                    (system (ly:grob-system grob))
which again will return the System grob.
Is there any case where those three are not equal?

(2) There is
                    (dy (if simple-y?
                            0
                            (ly:grob-relative-coordinate grob common-y Y)))
Though if `grob' and `common-y' are equal it returns always zero, afaict.
Is there any example where they are not equal?

(3) Why take 'simple-Y into account?
Well, it's true per default for Glissando, alas it makes no difference afaict.
Btw, we don't have any regtest for it



Many thanks again,
  Harm

["start-end-gradient-03b-harm.ly" (text/x-lilypond)]

\version "2.22.0" % "2.23.3"

%% rewritten by Aaron Hill, many thanks!
%% https://lists.gnu.org/archive/html/lilypond-user/2021-10/msg00354.html

%% Comments/TODOs by Harm

\paper {
  indent = 0
  ragged-right = ##f
  line-width = 120
}

\layout {
  \context {
    \Voice
    \override Glissando.layer = 1000
    \override Glissando.bound-details.left.padding =  5
    \override Glissando.bound-details.right.padding = 5
    \override Glissando.breakable = ##t
  }
}

%% cross stensil
#(define*
   (make-cross-stencil coords #:optional (thick 0.1) (sz 0.2))
   (ly:stencil-add
     (make-line-stencil
       thick
       (- (car coords) sz)
       (- (cdr coords) sz)
       (+ (car coords) sz)
       (+ (cdr coords) sz))
     (make-line-stencil
       thick
       (- (car coords) sz)
       (+ (cdr coords) sz)
       (+ (car coords) sz)
       (- (cdr coords) sz))))

%% glissando stencil
#(define glissando-stencil-proc (lambda (grob) (ly:line-spanner::print grob)))

%% get start/end points
#(define gliss-data
  (lambda (grob)
    (let* ((simple-y? (and (ly:grob-property grob 'simple-Y)
                           (not (ly:grob-property grob 'cross-staff))))
           (lbi (ly:grob-property grob 'left-bound-info))
           (rbi (ly:grob-property grob 'right-bound-info))
;;;;;;;;;;;
;; TODO (1)
;;;;;;;;;;;
;;  Common refpoint of the spanner-bounds is already grob System
;;  Why searching again, doesn't it will return grob System again?
;;  Is there any ly-counter-example?
;;  See the test-code below inside `pretty-print'
           (common-x (ly:grob-common-refpoint grob
                      (ly:grob-common-refpoint
                       (ly:spanner-bound grob LEFT)
                       (ly:spanner-bound grob RIGHT) X) X))
           (scaling (magstep (ly:grob-property grob 'font-size 0)))
           (left-padding (ly:assoc-get 'padding lbi 0))
           (left-stencil (ly:assoc-get 'stencil lbi #f))
           (left-common-y (ly:assoc-get 'common-y lbi grob))
           (right-padding (ly:assoc-get 'padding rbi 0))
           (right-stencil (ly:assoc-get 'stencil rbi #f))
           (right-common-y (ly:assoc-get 'common-y rbi grob))
           (common-y (ly:grob-common-refpoint left-common-y right-common-y Y))
           (normalized-endpoints
             (ly:grob-property grob 'normalized-endpoints '(0 . 1)))
           (span-left-x (ly:assoc-get 'X lbi 0))
           (span-left-y (ly:assoc-get 'Y lbi 0))
           (span-right-x (ly:assoc-get 'X rbi 0))
           (span-right-y (ly:assoc-get 'Y rbi 0)))

           ;; Take relative coords in X/Y direction into account.
           ;; This needs to come before other modifications of x/y values!
           ;; Especially `dy-right' is important for cross-staff spanners
           (let* ((dx (ly:grob-relative-coordinate grob common-x X))
;;;;;;;;;;;
;; TODO (2)
;;;;;;;;;;;
;;  Do we need to calculate `dy' at all?
;;  Will (ly:grob-relative-coordinate grob common-y Y) ever be not zero, if
;;  grob and common-Y are equal?
;;  Is it possible at all they are ever unequal? ly-counter-example?!
                  (dy (if simple-y?
                          0
                          (ly:grob-relative-coordinate grob common-y Y)))
;;;;;;;;;;;
;; TODO (1)
;;;;;;;;;;;
;; Why call (ly:grob-system grob)?
;; Afaict, it's the same as `common-x' form above (and see TODO there and
;; test-code inside of `pretty-print' below)
                  (system (ly:grob-system grob))
                  (dy-right (ly:grob-relative-coordinate grob system Y)))
;; test-code
;(pretty-print
;  (let* ((bounds-refpt-X
;           (ly:grob-common-refpoint
;             (ly:spanner-bound grob LEFT)
;             (ly:spanner-bound grob RIGHT)
;             X))
;         (common-X
;           (ly:grob-common-refpoint
;             grob
;             bounds-refpt-X
;             X))
;         (sys (ly:grob-system grob))
;         (lst (list bounds-refpt-X common-X sys))
;             )
;    (delete-duplicates lst equal?)
;  )
;)
             (set! span-left-x (- span-left-x dx))
             (set! span-left-y (- span-left-y dy))
             (set! span-right-x (- span-right-x dx))
             (set! span-right-y (- span-right-y dy dy-right)))

;;;;;;;;;;;
;; TODO (3)
;;;;;;;;;;;
;; Is there any need to modify `span-left-y' and `span-right-y' wrt to
;; `simple-y?'
;; In all tested cases `span-left-y' and `span-right-y' keep their value, see:
;;
; (pretty-print
;   (list
;     (ly:grob-relative-coordinate left-common-y common-y Y)
;     (ly:grob-relative-coordinate right-common-y common-y Y)
;   )
; )
;; Is there any counter-example?
      (if (not simple-y?)
          (begin
            (set! span-left-y (+ span-left-y
              (ly:grob-relative-coordinate left-common-y common-y Y)))
            (set! span-right-y (+ span-right-y
              (ly:grob-relative-coordinate right-common-y common-y Y)))))

      ;; For broken Spanner modify `span-left-y' and `span-right-y' with scaled
      ;; parts of `y-length'. The scaling factors are taken from the pair
      ;; `normalized-endpoints', car for left, cdr for right.
      ;; For unbroken Glissandi `normalized-endpoints' defaults to '(0 . 1)
      ;; and `span-left-y' and `span-right-y' stay unchanged then
      (let ((y-length (- span-right-y span-left-y)))
        (set! span-left-y (+ span-left-y
          (* (car normalized-endpoints) y-length)))
        (set! span-right-y (- span-right-y
          (* (- 1 (cdr normalized-endpoints)) y-length))))

      ;; Take into account:
      ;;  - padding vaues
      ;;  - (text-)stencils
      (let* ((span-dx (- span-right-x span-left-x))
             (span-dy (- span-right-y span-left-y))
             ;; Avoid "Pythagorean theorem", use ly:length instead
             (span-length (ly:length span-dx span-dy)))
       (if (> (+ left-padding right-padding) span-length)
           ;; If padding is large enough that the spanner disappears, return '()
           ;; NB needs to be checked by the caller of this procedure
           '()
           (let* (;; the cosinus of the enclosed angle
                  (grad-dx (/ span-dx span-length))
                  ;; the sinus of the enclosed angle
                  (grad-dy (/ span-dy span-length)))
             ;; cosinus and sinus of the enclosed angle are constants, where the
             ;; x and y-values are due to radius 1
             ;; To respect left/right-padding values, which are on that radius,
             ;; we need to modify our span-left/right-x/y values:
             ;; Simply multiply the relevant padding with (co)sinus, i.e.
             ;; `grad-dx' resp. `grad-dy' following the "Intercept theorem"
             ;; (Don't forget `scaling' for possible (text-)stencils).
             ;;
             (set! span-left-x
                   (+ span-left-x (* left-padding scaling grad-dx)))
             (set! span-left-y
                   (+ span-left-y (* left-padding scaling grad-dy)))
             (set! span-right-x
                   (- span-right-x (* right-padding scaling grad-dx)))
             (set! span-right-y
                   (- span-right-y (* right-padding scaling grad-dy)))

             ;; If a (text-)stencil is present at start/end of the spanner,
             ;; the relevant x/x values needs to get modified
             (if (and (ly:stencil? left-stencil)
                      (not (ly:stencil-empty? left-stencil)))
                 (let ((factor
                         (/ (cdr (ly:stencil-extent left-stencil X)) grad-dx)))
                   (set! span-left-x (+ span-left-x (* factor grad-dx)))
                   (set! span-left-y (+ span-left-y (* factor grad-dy)))))

             (if (and (ly:stencil? right-stencil)
                      (not (ly:stencil-empty? right-stencil)))
                 (let ((factor
                         (/ (car (ly:stencil-extent right-stencil X)) grad-dx)))
                   (set! span-right-x (+ span-right-x (* factor grad-dx)))
                   (set! span-right-y (+ span-right-y (* factor grad-dy)))))

               (cons (cons span-left-x span-left-y)
                     (cons span-right-x span-right-y))))))))

#(define gliss-stencil-with-crosses
  (lambda (grob)
    (let* ((cross-coords (gliss-data grob)))
      (ly:stencil-add
        ;; left cross
        (stencil-with-color
          (make-cross-stencil (car cross-coords) 0.2 0.2) blue)
        ;; right cross
        (stencil-with-color
          (make-cross-stencil (cdr cross-coords) 0.2 0.2) red)
        ;; glissando
        (stencil-with-color (glissando-stencil-proc grob) magenta)))))

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Examples
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% simple-Y

{
  \override Glissando.stencil = #gliss-stencil-with-crosses
  e''1\glissando s2 e'
}

%% Simple

{
  \override Glissando.stencil = #gliss-stencil-with-crosses
  e''1\glissando s2 e'
}

%% Padding

{
  \override Glissando.stencil = #gliss-stencil-with-crosses
  \override Glissando.bound-details.left.padding = #3
  \override Glissando.bound-details.right.padding = #5
  e''1\glissando s2 e'
}

%% Stencils

{
  \override Glissando.stencil = #gliss-stencil-with-crosses
  \override Glissando.font-size = #3
  \override Glissando.bound-details.left.text =
    \markup \vcenter \box \left-align "lorem"
  \override Glissando.bound-details.right.text =
    \markup \vcenter \box \right-align "ipsum"
  e''1\glissando s2 e'
}

%% cross-staff

\new PianoStaff
  <<
    \new Staff = "top"
      \with {
        \override VerticalAxisGroup.staff-staff-spacing.padding = 30
      }
      \relative c'' {
        \override Glissando.stencil = #gliss-stencil-with-crosses
        c1\glissando
        \change Staff = "bottom"
        s2
        g,,2
      }

    \new Staff = "bottom" { \clef "bass" s1*2 }
  >>

%% with line break

{
  \override Glissando.stencil = #gliss-stencil-with-crosses

  e'''1\glissando
  \break
  s2 \once \override NoteColumn.glissando-skip = ##t c''
  \break
  s2 b
}


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic