Am Sa., 10. Aug. 2024 um 19:11 Uhr schrieb Lukas-Fabian Moser <[email protected]>:

> Hi Kieren,
>
> Left to do: We probably also should modify the extents of the resulting 
> stencil to avoid collisions...
>
> If the thickness of the edge lines are expanded *inwards*, wouldn’t the 
> original extent still be accurate enough to use?
>
> Yes, good idea. But then we see that for thick lines, LilyPond's rounded
> line tips become a problem (actually, the problem wouldn't be just as
> serious with box-formed line tips):
>
> So we should probably replace the edge line by a box, more precisely: by a
> polygon with the correct slope at least on the connecting side. Sigh.
>
> Re-posting the source nevertheless, since in my first attempt I forgot
> that TupletBrackets can point upwards or downwards.
>
> \version "2.24.0"
>
> #(define (list-add! lst k x)
>    (list-set! lst k (+ (list-ref lst k) x)))
>
> {
>   \override TupletBracket.stencil =
>   #(grob-transformer
>     'stencil
>     (lambda (grob stil)
>       (let*
>        ((expr (ly:stencil-expr stil))
>         (xext (ly:stencil-extent stil X))
>         (yext (ly:stencil-extent stil Y))
>         (lines (cdaddr expr))
>         (right (first lines))
>         (left (second lines))
>         (thick (second right))
>         (dir (- (ly:grob-property grob 'direction)))
>         (new-thick 0.7)  ;; adjust at will
>         (offset (/ (- new-thick thick) 2)))
>
>        (list-set! right 1 new-thick)
>        (list-add! right 2 (- offset))         ; x1
>        (list-add! right 3 (* dir offset))     ; y1
>        (list-add! right 4 (- offset))         ; x2
>
>        (list-set! left 1 new-thick)
>        (list-add! left 2 offset)              ; x1
>        (list-add! left 3 (* dir offset))      ; y1
>        (list-add! left 4 offset)              ; x2
>
>        (ly:make-stencil expr xext yext))))
>
>   \tupletDown
>   \tuplet 3/2 { c'4 d' c''' }
>   \tupletUp
>   \tuplet 3/2 { c'4 d' e' }
> }
>
> And of course: The *proper* way to handle all of this would be to just
> swallow the pill and re-implement the a modified TupletBracket stencil in
> Scheme.
>
> Lukas
>

Here my take on it.
Some effort is done to identify the wings drawing lines: not going for the
position in the stencil-list, but for matching their height with the
relevant grob edge-height. Hopefully safer...

\version "2.24.3"

#(define (lists-map function ls)
  "Apply @var{function} to @var{ls} and all of it sublists.
First it recurses over the children, then the function is applied to
@var{ls}."
    (if (list? ls)
        (set! ls (map (lambda (y) (lists-map function y)) ls))
        ls)
    (function ls))

#(define (tuplet-bracket::line-parts grob stencil)
  "Examine @code{TupletBracket.stencil), accumulate lines used to draw
@code{TupletBracket}, divided into wings and horizontal lines."
  (if (ly:stencil-empty? stencil)
      '()
      (let* ((lines '())
             (edge-height (ly:grob-property grob 'edge-height))
             (stil-expr (ly:stencil-expr stencil)))

        ;; accumulate the bracket-drawing lines in `lines`
        (when (pair? stil-expr)
          (lists-map
            (lambda (l)
              (when (and (list? l)
                         (eq? (list-ref l 0) 'draw-line)
                         ;; Broken TupletBracket may have collapsed wings,
                         ;; don't catch them. Deal with them when this
procedure
                         ;; is called.
                         (not (zero?
                                (- (- (list-ref l 2) (list-ref l 4))
                                   (- (list-ref l 3) (list-ref l 5))))))
                (set! lines (cons l lines)))
              l)
            stil-expr))

        (call-with-values
          (lambda ()
            (partition
              (lambda (l)
                (let ((height (- (list-ref l 3) (list-ref l 5))))
                  ;; TODO looking at height is probably not safe enough.
                  ;; Avoid rounding issues
                  (or
                     (> 0.0000001
                        (abs (- (abs height) (abs (car edge-height)))))
                     (> 0.0000001
                        (abs (- (abs height) (abs (cdr edge-height))))))))
              lines))
          (lambda (x y)
            (list (cons 'wings x) (cons 'horizontals y)))))))

#(define (tuplet-bracket::wing-thickness wing-thickness)
  "Examine @code{TupletBracket.stencil), replace the wings by a polygon
mimicking enlarged thickness."
 (grob-transformer 'stencil
  (lambda (grob orig)
   (let* ((parts (tuplet-bracket::line-parts grob orig))
          (raw-wings (assoc-get 'wings parts)))
     (if (not (pair? raw-wings))
         orig
         (let* (
          (horizontals (assoc-get 'horizontals parts))
          (slopes
           (map
            (lambda (l)
              (/ (- (list-ref l 5) (list-ref l 3))
                 (- (list-ref l 4) (list-ref l 2))))
            horizontals))
          (wings
           (cond ((middle-broken-spanner? grob) '(#f #f))
                 ((first-broken-spanner? grob)
                   (append raw-wings (list #f)))
                 ((end-broken-spanner? grob)
                   (cons #f raw-wings))
                 (else raw-wings)))
          (grob-thick
           (ly:grob-property grob 'thickness))
          (staff-line-thick
           (ly:staff-symbol-line-thickness (ly:grob-object grob
'staff-symbol)))
          (thick (* grob-thick staff-line-thick))
          (edge-height (ly:grob-property grob 'edge-height))
          (shorten-pair (ly:grob-property grob 'shorten-pair '(0 . 0)))
          (dir (ly:grob-property grob 'direction)))

    (ly:make-stencil
     (lists-map
      (lambda (l)
       (cond
        ((equal? l (car wings))
          (let* ((start-x (list-ref l 2))
                 (start-y (+ (list-ref l 3) (* wing-thickness (car
slopes))))
                 (shorten-pair (car shorten-pair))
                 (edge-height (car edge-height)))
            `(polygon
              ,(list
                start-x (list-ref l 3)
                (+ start-x wing-thickness) start-y
                (+ start-x wing-thickness) (+ start-y (* -1 dir
edge-height))
                start-x (+ (list-ref l 3) (* dir -1 edge-height)))
              ;; take `thick` as blot-diameter to match rounded line ends
              ,thick
              #t)))
        ((equal? l (cadr wings))
          (let* ((end-x (list-ref l 2))
                 (start-x (- end-x wing-thickness))
                 (start-y (* start-x (cadr slopes)))
                 (end-y (list-ref l 5))
                 (edge-height (cdr edge-height)))
            `(polygon
               ,(list
                 end-x end-y
                 end-x (- end-y (* dir -1 edge-height))
                 start-x start-y
                 start-x (+ start-y (* dir -1 edge-height)))
               ,thick
               #t)))
        (else l)))
      (ly:stencil-expr orig))
     (ly:stencil-extent orig X)
     (ly:stencil-extent orig Y))))))))

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

\layout {
  \override TupletBracket.stencil = #(tuplet-bracket::wing-thickness 1)
}

{
  \tuplet 3/2 { b4 b b } \tuplet 3/2 { b'' b b } \tuplet 3/2 { b b b'' }
  \tupletDown
  \tuplet 3/2 { b4 b b } \tuplet 3/2 { b'' b b } \tuplet 3/2 { b b b'' }
}

{
  \tuplet 3/2 { b1 b''2 \break b4 b \break b''2 b }
}


Best,
  Harm

Reply via email to