Hi Stefano,

On Thu, Feb 8, 2018 at 4:43 PM, Stefano Troncaro
<stefanotronc...@gmail.com> wrote:
> Hello again!
>
> I managed to modify David's translation of ly:hairpin::print to have it use
> two properties, Hairpin.rotate and Hairpin.straight-end, to achieve almost
> all the results I wanted.

Glad you got some use out of this!  I like the look of the
straight-edged hairpins.

>
> The idea is that Hairpin.rotate can be either a numerical value,
> representing the angle of rotation, or a procedure that returns the angle of
> rotation. In one of the examples I used the function discussed earlier in
> this thread to have it automatically detect the angle of a beam. However, I
> can't manage to make this idea work when the procedure given to
> Hairpin.rotate requires more than one variable. This is very inconvenient
> because for some cases the procedure would need to calculate again a lot of
> things that are already calculated in the process of making the stencil.

First of all, calling ly:grob-property calls any procedure the
property is set to.  The function ly:grob-property-data won't.

I don't know of any way in Scheme to overload functions or to count
arguments.  If you name your function, however, you can use
procedure-name.  (See the attached.)

HTH,
David
\version "2.19.80"

#(define (proc-number-or-false? obj)
   (or (procedure? obj)
       (number? obj)
       (eq? obj #f)))

#(define (define-grob-property symbol type? description)
   (if (not (equal? (object-property symbol 'backend-doc) #f))
       (ly:error (_ "symbol ~S redefined") symbol))

   (set-object-property! symbol 'backend-type? type?)
   (set-object-property! symbol 'backend-doc description)
   symbol)

#(map
  (lambda (x)
    (apply define-grob-property x))

  `(
     (circled-tip-radius ,number? "Radius for hairpin circled tip")
     (rotate ,proc-number-or-false? "Custom rotation: a number specifies angle in degrees, a procedure will receive the grob and return an angle, #f deactivates rotation")
     (straight-end ,boolean? "Straighten the end of the hairpin when it's rotated?")
     ))


#(define broken-neighbor
   (lambda (grob)
     (let* ((pieces (ly:spanner-broken-into (ly:grob-original grob)))
            (me-list (member grob pieces)))
       (if (> (length me-list) 1)
           (cadr me-list)
           '()))))

#(define (interval-dir-set i val dir)
   (cond ((= dir LEFT) (set-car! i val))
     ((= dir RIGHT) (set-cdr! i val))
     (else (ly:error "dir must be LEFT or RIGHT"))))

#(define (other-dir dir) (- dir))

#(define hairpin::print-scheme
   (lambda (grob)
     (let ((grow-dir (ly:grob-property grob 'grow-direction)))
       (if (not (ly:dir? grow-dir))
           (begin
            (ly:grob-suicide! grob)
            '()))

       (let* ((padding (ly:grob-property grob 'bound-padding 0.5))
              (bounds (cons (ly:spanner-bound grob LEFT)
                        (ly:spanner-bound grob RIGHT)))
              (broken (cons
                       (not (= (ly:item-break-dir (car bounds)) CENTER))
                       (not (= (ly:item-break-dir (cdr bounds)) CENTER)))))

         (if (cdr broken)
             (let ((next (broken-neighbor grob)))
               (if (ly:spanner? next)
                   (begin
                    (ly:grob-property next 'after-line-breaking)
                    (set-cdr! broken (grob::is-live? next)))
                   (set-cdr! broken #f))))

         (let* ((common
                 (ly:grob-common-refpoint (car bounds) (cdr bounds) X))
                (x-points (cons 0 0))
                (circled-tip (ly:grob-property grob 'circled-tip))
                (height (* (ly:grob-property grob 'height 0.2)
                          (ly:staff-symbol-staff-space grob)))
                (rad (ly:grob-property grob 'circled-tip-radius (* 0.525 height)))
                (thick (* (ly:grob-property grob 'thickness 1.0)
                         (ly:staff-symbol-line-thickness grob))))

           (define (inner dir)
             (let* ((b (interval-bound bounds dir))
                    (e (ly:generic-bound-extent b common)))
               (interval-dir-set
                x-points (ly:grob-relative-coordinate b common X) dir)

               (if (interval-bound broken dir)
                   (if (= dir LEFT)
                       (interval-dir-set
                        x-points (interval-bound e (other-dir dir)) dir)
                       (let* ((broken-bound-padding
                               (ly:grob-property grob 'broken-bound-padding 0.0))
                              (chp (ly:grob-object grob 'concurrent-hairpins)))
                         (let loop ((i 0))
                           (if (and (ly:grob-array? chp)
                                    (< i (ly:grob-array-length chp)))
                               (let ((span-elt (ly:grob-array-ref chp i)))
                                 (if (= (ly:item-break-dir (ly:spanner-bound span-elt RIGHT))
                                        LEFT)
                                     (set! broken-bound-padding
                                           (max broken-bound-padding
                                             (ly:grob-property span-elt 'broken-bound-padding 0.0))))
                                 (loop (1+ i)))))
                         (interval-dir-set
                          x-points
                          (- (interval-bound x-points dir)
                            (* dir broken-bound-padding))
                          dir)))

                   (if (grob::has-interface b 'text-interface)
                       (if (not (interval-empty? e))
                           (interval-dir-set x-points
                             (- (interval-bound e (other-dir dir))
                               (* dir padding))
                             dir))
                       (let* ((neighbor-found #f)
                              (adjacent '())
                              (neighbors (ly:grob-object grob 'adjacent-spanners))
                              (neighbors-len (if (ly:grob-array? neighbors)
                                                 (ly:grob-array-length neighbors)
                                                 0)))

                         (let inner-two ((i 0))
                           (if (and (< i neighbors-len)
                                    (not neighbor-found))
                               (begin
                                (set! adjacent (ly:grob-array-ref neighbors i))
                                (if (and (ly:spanner? adjacent)
                                         (eq? (ly:item-get-column (ly:spanner-bound adjacent (other-dir dir)))
                                              (ly:item-get-column b)))
                                    (set! neighbor-found #t))
                                (inner-two (1+ i)))))

                         (if neighbor-found
                             (if (grob::has-interface adjacent 'hairpin-interface)
                                 (if (and circled-tip (not (eq? grow-dir dir)))
                                     (interval-dir-set x-points
                                       (+ (interval-center e)
                                         (* dir
                                           (- rad (/ thick 2.0))))
                                       dir)
                                     (interval-dir-set x-points
                                       (- (interval-center e)
                                         (/ (* dir padding) 3.0))
                                       dir))
                                 (if (= dir RIGHT)
                                     (interval-dir-set x-points
                                       (- (interval-bound e (other-dir dir))
                                         (* dir padding))
                                       dir)))
                             (begin
                              (if (and (= dir RIGHT)
                                       (grob::has-interface b 'note-column-interface)
                                       (ly:grob-array? (ly:grob-object b 'rest)))
                                  (interval-dir-set x-points
                                    (interval-bound e (other-dir dir))
                                    dir)
                                  (interval-dir-set x-points
                                    (interval-bound e dir)
                                    dir))

                              (if (eq? (ly:grob-property b 'non-musical) #t)
                                  (interval-dir-set x-points
                                    (- (interval-bound x-points dir)
                                      (* dir padding))
                                    dir)))))))))


           (inner LEFT)
           (inner RIGHT)

           (let* ((width (- (interval-bound x-points RIGHT)
                           (interval-bound x-points LEFT)))
                  (width (if (< width 0)
                             (begin
                              (ly:warning
                               (if (< grow-dir 0)
                                   "decrescendo too small"
                                   "crescendo too small"))
                              0)
                             width))
                  (continued (interval-bound broken (other-dir grow-dir)))
                  (continuing (interval-bound broken grow-dir))

                  (starth (if (< grow-dir 0)
                              (if continuing
                                  (* 2 (/ height 3))
                                  height)
                              (if continued
                                  (/ height 3)
                                  0.0)))
                  (endh (if (< grow-dir 0)
                            (if continued
                                (/ height 3)
                                0.0)
                            (if continuing
                                (* 2 (/ height 3))
                                height)))
                  (mol empty-stencil)
                  (x 0.0)
                  (tip-dir (other-dir grow-dir)))

             (if (and circled-tip
                      (not (interval-bound broken tip-dir)))
                 (if (> grow-dir 0)
                     (set! x (* rad 2.0))
                     (if (< grow-dir 0)
                         (set! width (- width (* rad 2.0))))))

             ;add support for rotation and straightened end-points
             (let* ((rotate (ly:grob-property-data grob 'rotate))
                    (straighten (ly:grob-property grob 'straight-end #f))
                    (ang (cond
                          ((number? rotate) rotate)
                          ((procedure? rotate)
                           (if (eq? (procedure-name rotate) 'hairpin-follow-beam)
                               (rotate grob)
                               (rotate grob width starth endh)))
                          (else 0)))
                    (offset (if (or (= ang 0) (not straighten))
                                0
                                (let* ((adj-hgt (- starth endh))
                                       (def-ang (atan (/ adj-hgt width)))
                                       (and-rad (degrees->radians ang))
                                       (tot-ang (+ def-ang and-rad)))
                                  (- (* width (tan tot-ang)) adj-hgt)))))

               (set! mol (make-line-stencil thick x starth width (+ endh offset)))

               (set! mol
                     (ly:stencil-add
                      mol
                      (make-line-stencil thick x (- starth) width (- offset endh))))

               ;TODO: circle on the right end of the object should be placed with the offset. How?
               (if circled-tip
                   (let ((circle (make-circle-stencil rad thick #f)))
                     (if (not (interval-bound broken tip-dir))
                         (set! mol
                               (ly:stencil-combine-at-edge mol X tip-dir circle 0)))))

               ;if straight end-points are not needed, just rotate the stencil
               (if (and (not straighten) (not (= ang 0)))
                   (set! mol (ly:stencil-rotate mol ang CENTER CENTER)))

               (set! mol
                     (ly:stencil-translate-axis mol
                       (- (interval-bound x-points LEFT)
                         (ly:grob-relative-coordinate (interval-bound bounds LEFT) common X))
                       X))

               mol)))))))

#(define hairpin-follow-beam
   (lambda (grob)
     (let* ((lb (ly:spanner-bound grob LEFT))
            (rb (ly:spanner-bound grob RIGHT))
            (bound
             (find (lambda (b)
                     (grob::has-interface b 'note-column-interface))
               (list lb rb)))
            (beam
             (if bound
                 (ly:grob-object (ly:grob-object bound 'stem) 'beam)
                 (let* ((col (ly:item-get-column lb))
                        (elts (ly:grob-array->list
                               (ly:grob-object col 'bounded-by-me))))
                   (find (lambda (e) (grob::has-interface e 'beam-interface))
                     elts)))))
       (if (ly:grob? beam)
           (let* ((X-pos (ly:grob-property beam 'X-positions))
                  (Y-pos (ly:grob-property beam 'positions))
                  (ang (ly:angle (- (cdr X-pos) (car X-pos))
                         (- (cdr Y-pos) (car Y-pos)))))
             ang)
           0))))

% The following doesn't work because of the following error when Hairpin.rotate has a lambda that requires more than one variable.
%   226:29: Wrong number of arguments to #<procedure hairpin-upper-with-staff (grob width starth endh)>
#(define hairpin-upper-with-staff
   (lambda (grob width starth endh)
     (let* ((adj-hgt (- endh starth))
            (def-ang (atan (/ adj-hgt width)))
            (def-ang-degrees (* def-ang (/ 180 PI))))
       (- def-ang-degrees))))

music =
{
  c'1\<
  c'2\! c'2~\>
  c'2~ c'2\!
  c'2\> c'2\< c'1
  c''1\<
  c''4 a' c''\< a'
  c''4 a' c''\! a'\<
  c''4 a' c'' a'\!
  c''1~\<
  c''1~
  \break
  c''1\!
  c'1\!\<
  \break
  c'1
  \break
  c'2 c'2\!
  c''1\<
  c''4 a' c''\mf a'
  c''1\<
  c''4 a' c''\ffff a'
  c''4\< c''\! d''\> e''\!
  <<
    f''1
    { s4 s4\< s4\> s4\! }
  >>
  \once \override Hairpin.to-barline = ##f
  c''1\<
  c''1\!

  c'8\< e' g' b'\! d''\> b' g' e'\!
  << f''1 { s4 s\< s\> s\! } >>
  \override Hairpin.minimum-length = #5
  << f''1 { s4 s\< s\> s\! } >>
  \revert Hairpin.minimum-length
}

\markup \huge \bold "DEFAULT"


{
  \music
  \override Hairpin.circled-tip = ##t
  \music
}

\markup \huge \bold "ANGLE REWRITE"

{
  \override Hairpin.stencil = #hairpin::print-scheme
  \music
  \override Hairpin.rotate = 10
  \override Hairpin.straight-end = ##f
  \music
  \override Hairpin.rotate = -15
  \override Hairpin.straight-end = ##t
  \music
  %\override Hairpin.circled-tip = ##t %can't properly position circled-tip
  \override Hairpin.rotate = #hairpin-follow-beam
  \override Hairpin.straight-end = ##t
  \music

  \override Hairpin.rotate = #hairpin-upper-with-staff
  \override Hairpin.straight-end = ##t
  \music

}

\layout {
  ragged-right = ##t
}
_______________________________________________
lilypond-user mailing list
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user

Reply via email to