Am Sa., 10. Aug. 2024 um 19:11 Uhr schrieb Lukas-Fabian Moser
<lfm@gmx.de>:
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