[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