lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Questions about HorizontalBracket


From: Francesco Napoleoni
Subject: Re: Questions about HorizontalBracket
Date: Sun, 13 Sep 2020 23:12:44 +0200

In data domenica 13 settembre 2020 19:22:13 CEST, Lukas-Fabian Moser ha 
scritto:
> Hi Francesco,
> 
> > I’m trying to use HorizontalBracket to annotate the intervals between
> > notes of a scale.
> > 
> > The example (perhaps not minimal, but almost working) attached shows
> > something very close to what I want to achieve.
> > 
> > However there are a few things that need to be fixed, or improved:
> > 1) the brackets remain outside the staff no matter how I fiddle with
> > staff-
> > padding and padding properties, while I would like them to stay closer to
> > the notes;
> > 2) I cannot find an (obvious and) automatic way to have the ends of a
> > bracket to align with the center of the note heads. I found a manual
> > workaround by setting the shorten-pair property, which is a far from
> > being an optimal solution;
> > 3) is there a way to create a V-shaped bracket? The hack I came up with is
> > ugly;
> > 4) to have brackets both above and below the notes I have used two voices,
> > one with the notes hidden. Is there a faster/less verbose way to obtain
> > the same result?
> > 
> > Besides these, it would be nice (but not essential) to have these bracket
> > to also follow the slope of an interval.
> 
> LilyPond's horizontal brackets are not very flexible, as far as I know.
> But it occurred to me that for everything you listed, the necessary
> mechanisms are in LilyPond as part of the mechanism typesetting slurs:
> 1), 2) is automatic for slurs, 3) is a matter of distorting a slur to a
> simple three-point line, and 4) is possible by virtue of the \=...
> construct.
> 
> Hence, how about:
> 
> \version "2.21.0"
> 
> % Some routines for calculating with 2D vectors (given as scheme pairs)
> #(define (vector-sum v w)
>     (cons (+ (car v) (car w))
>           (+ (cdr v) (cdr w))))
> 
> #(define (vector-factor factor v)
>     (cons (* (car v) factor)
>           (* (cdr v) factor)))
> 
> #(define (scalar-product v w)
>     (+ (* (car v) (car w))
>        (* (cdr v) (cdr w))))
> 
> #(define (midpoint p1 p2)
>     (vector-factor 1/2 (vector-sum p1 p2)))
> 
> #(define (normal p q)
>     ; yields a normal vector to the line from p to q.
>     ; the length of the normal vector will be proportional to
>     ; the distance [pq].
>     (cons (- (cdr p) (cdr q))
>           (- (car q) (car p))))
> 
> #(define (side v normal start)
>     ; A line through "start" with fixed normal vector "normal" cuts the
> plane
>     ; into two half-planes. This function returns
>     ; 0 if v lies on the line itself,
>     ; +1 if v lies in the half plane that the normal vector points to,
>     ; -1 otherwise.
>     (let ((dist (- (scalar-product normal v)
>                    (scalar-product normal start))))
>       (cond ((> dist 0) 1) ; is there no "sgn" function in guile?!
>             ((< dist 0) -1)
>             (else 0))
>       ))
> 
> % Shortcuts for using pairs inside a \markup \path ...
> #(define (moveto p) (list 'moveto (car p) (cdr p)))
> #(define (lineto p) (list 'lineto (car p) (cdr p)))
> 
> VShapeSlur =
> \tweak stencil
> #(lambda (grob)
>     (let* ((control-points (ly:grob-property grob 'control-points))
>            (start (first control-points))
>            (1st-directional-point (second control-points))
>            (2nd-directional-point (third control-points))
>            (stop (fourth control-points)))
>       (grob-interpret-markup grob #{
>         \markup {
>           \path #0.1 #(list (moveto start)
>                             (lineto (midpoint 1st-directional-point
> 2nd-directional-point))
>                             (lineto stop))
>         } #})))
> \etc
> 
> bracketSlur =
> \tweak stencil
> #(lambda (grob)
>     (let* ((control-points (ly:grob-property grob 'control-points))
>            (start (first control-points))
>            (1st-directional-point (second control-points))
>            (stop (fourth control-points))
>            (normal (normal start stop))
>            (scaled-normal
>             (vector-factor
>              (* 0.075 (side 1st-directional-point normal start))
>              normal)))
>       (grob-interpret-markup grob #{
>         \markup {
>           \path #0.1
>           #(list (moveto start)
>                  (lineto (vector-sum start scaled-normal))
>                  (lineto (vector-sum stop scaled-normal))
>                  (lineto stop))
>         } #})))
> \etc
> 
> \relative c' {
>    c1 \bracketSlur ( d) e \bracketSlur( f g a g f')
>    c,1 \VShapeSlur ( d) e \VShapeSlur( f g a g f')
> }
> 
> \relative c' {
>    c4 \VShapeSlur \=0_( \VShapeSlur \=1^( d e \bracketSlur \=2_( f\=1) e
> d\=2) c b\=0)
> }
> 
> Best
> Lukas

Thank you very much, Lukas! :-) Your answer goes far beyond my expectations: 
using slurs is a very good point. Actually I had thought about using them, but 
I had no clue on how to modify their shape. Besides that, Scheme language 
still looks a bit intimidating to me, so I had to resort to use 
HorizontalBracket.

Anyway, let’s see if I understand the big picture of your code:
1. you define some pure Scheme functions that deal about creating the basic 
“shapes” (actually the vectors that will be used by the “path” command);
2. then you define the commands that tweak the stencil of a Slur grob;
3. and, like a “tweak” command, you call it just before the parentheses, 
letting the magic happen, right?

By the way, the code does not compile with my old 2.19.83 version (included in 
my Linux installation), I had to download the latest version.

cheers
Francesco Napoleoni






reply via email to

[Prev in Thread] Current Thread [Next in Thread]