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