Re: Questions about HorizontalBracket

2020-09-16 Thread Francesco Napoleoni
In data martedì 15 settembre 2020 12:58:19 CEST, Lukas-Fabian Moser ha 
scritto:
> Hi Francesco,
> 
> > Thank you very much, Lukas! :-) Your answer goes far beyond my
> > expectations: using slurs is a very good point.
> > [...]
> > 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?
> 
> Yes, that basically is the picture. I created a second version that not
> only should have a clearer structure (because now the creation of the
> shapes is factored out into two custom-made markup functions) but is
> also heavily commented.
> 
> I have a plan to create some examples of "LilyPond programming with
> annotations" (for the dual purpose of enlightening myself and helping
> others on their path), so feel free to comment and ask questions.

That would be great. Indeed for me it’s not as hard to learn the Guile 
language as to understand the internal structures of Lilypond and their 
relation with the language. So, a collection of commented examples could be 
very helpful.


> > By the way, the code does not compile with my old 2.19.83 version
> [...]
> My bad: I hadn't realized that the invaluable \=... mechanism [...] wasn't
> contained in 2.19.83. Sorry.

No problem: as a matter of fact it’s definitely time for me to upgrade my 
software to a more recent version. ;-)

cheers
Francesco Napoleoni






Re: Questions about HorizontalBracket

2020-09-15 Thread Lukas-Fabian Moser

Hi Francesco,


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?


Yes, that basically is the picture. I created a second version that not 
only should have a clearer structure (because now the creation of the 
shapes is factored out into two custom-made markup functions) but is 
also heavily commented.


I have a plan to create some examples of "LilyPond programming with 
annotations" (for the dual purpose of enlightening myself and helping 
others on their path), so feel free to comment and ask questions.



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.


My bad: I hadn't realized that the invaluable \=... mechanism for 
simultaneous slurs (added by David K. in 2015 
https://sourceforge.net/p/testlilyissues/issues/4626/) wasn't contained 
in 2.19.83. Sorry.


Best
Lukas

\version "2.21.0"

% -- Scheme functions for dealing with 2D vectors -
% A vector is represented as a pair v = (x . y), hence
% x is (car v) and y is (cdr v).

% Some routines for calculating with 2D vectors (given as scheme pairs)
#(define (vector-add v w) ; Calculate v+w
   (cons (+ (car v) (car w))
 (+ (cdr v) (cdr w
% Remember that (cons a b) by definition constructs the pair (a . b).

#(define (vector-substract v w) ; Calculate v-w
   (cons (- (car v) (car w))
 (- (cdr v) (cdr w

#(define (vector-stretch factor v) ; Calculate factor * v.
   ; (The mathematician in me wanted to write lambda * v, but lambda
   ; is a sacred reserved word in scheme :-).)
   (cons (* (car v) factor)
 (* (cdr v) factor)))

#(define (scalar-product v w) ; Euclidean scalar product of vectors
   (+ (* (car v) (car w))
  (* (cdr v) (cdr w

#(define (midpoint v w) ; calculate (v+w)/2
   (vector-stretch 1/2 (vector-add v w)))

#(define (rotate-90-ccw v)
   ; rotate vector 90° counter-clockwise
   ; (mathematically positive direction)
   (cons (- (cdr v)) (car v)))

#(define (normal p q)
   ; constructs a normal vector to the line from p to q.
   ; the length of the normal vector will be proportional to
   ; the distance [pq].
   (rotate-90-ccw (vector-substract q p)))

#(define (on-which-halfplane 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))
 ))

% -- Markup functions replacing slur shapes -
% The following markup functions expect a list of control points
% like they are used for slurs:
% (start 1st-directional-point 2nd-directional-point stop)

% First, we define shortcuts for easy construction of \path's.
% [ Background: We want to construct a markup using \path. As you can
%   see in http://lilypond.org/doc/v2.20/Documentation/notation/graphic,
%   \path expects a list of commands of the form
% '((moveto 0 0)
%   (lineto -1 1)
%   (lineto 1 1)
%   (lineto 1 -1)
%   (curveto -5 -5 -5 5 -1 0)
%   (closepath)),
%   i.e. each element is itself a list, starting with a command symbol like
%   'lineto followed by numeric parameters.
%   But for us, points/vectors are _pairs_ of numbers.
%   So, we define a "moveto" command does the necessary conversion: It 
combines

%   - the symbol "moveto",
%   - the x-coordinate of point p (i.e. (car p))
%   - the y coordinate of point p (i.e. (cdr p))
%   into a list. ]
#(define (moveto p) (list 'moveto (car p) (cdr p)))
#(define (lineto p) (list 'lineto (car p) (cdr p)))

% Markup functions are defined by #(define-markup-command ...), see
% 
http://lilypond.org/doc/v2.20/Documentation/extending/new-markup-command-definition

%
% [ Note that the syntax for defining new markup commands differs from
%   the syntax for defining new music functions
% 
(http://lilypond.org/doc/v2.20/Documentation/extending/music-function-definitionsThe 
syntax is different from the way you define new music functions):
%   #(define-markup-command (name-of-new-command 

Re: Questions about HorizontalBracket

2020-09-15 Thread Lukas-Fabian Moser

New version with some typos removed:



\version "2.21.0"

% -- Scheme functions for dealing with 2D vectors -
% A vector is represented as a pair v = (x . y), hence
% x is (car v) and y is (cdr v).

% Some routines for calculating with 2D vectors (given as scheme pairs)
#(define (vector-add v w) ; Calculate v+w
   (cons (+ (car v) (car w))
 (+ (cdr v) (cdr w
% Remember that (cons a b) by definition constructs the pair (a . b).

#(define (vector-substract v w) ; Calculate v-w
   (cons (- (car v) (car w))
 (- (cdr v) (cdr w

#(define (vector-stretch factor v) ; Calculate factor * v.
   ; (The mathematician in me wanted to write lambda * v, but lambda
   ; is a sacred reserved word in scheme :-).)
   (cons (* (car v) factor)
 (* (cdr v) factor)))

#(define (scalar-product v w) ; Euclidean scalar product of vectors
   (+ (* (car v) (car w))
  (* (cdr v) (cdr w

#(define (midpoint v w) ; calculate (v+w)/2
   (vector-stretch 1/2 (vector-add v w)))

#(define (rotate-90-ccw v)
   ; rotate vector 90° counter-clockwise
   ; (mathematically positive direction)
   (cons (- (cdr v)) (car v)))

#(define (normal p q)
   ; constructs a normal vector to the line from p to q.
   ; the length of the normal vector will be proportional to
   ; the distance [pq].
   (rotate-90-ccw (vector-substract q p)))

#(define (on-which-halfplane 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))
 ))

% -- Markup functions replacing slur shapes -
% The following markup functions expect a list of control points
% like they are used for slurs:
% (start 1st-directional-point 2nd-directional-point stop)

% First, we define shortcuts for easy construction of \path's.
% [ Background: We want to construct a markup using \path. As you can
%   see in http://lilypond.org/doc/v2.20/Documentation/notation/graphic,
%   \path expects a list of commands of the form
% '((moveto 0 0)
%   (lineto -1 1)
%   (lineto 1 1)
%   (lineto 1 -1)
%   (curveto -5 -5 -5 5 -1 0)
%   (closepath)),
%   i.e. each element is itself a list, starting with a command symbol like
%   'lineto followed by numeric parameters.
%   But for us, points/vectors are _pairs_ of numbers.
%   So, we define a "moveto" command does the necessary conversion: It 
combines

%   - the symbol "moveto",
%   - the x-coordinate of point p (i.e. (car p))
%   - the y coordinate of point p (i.e. (cdr p))
%   into a list. ]
#(define (moveto p) (list 'moveto (car p) (cdr p)))
#(define (lineto p) (list 'lineto (car p) (cdr p)))

% Markup functions are defined by #(define-markup-command ...), see
% 
http://lilypond.org/doc/v2.20/Documentation/extending/new-markup-command-definition

%
% [ Note that the syntax for defining new markup commands differs from
%   the syntax for defining new music functions:
% 
(http://lilypond.org/doc/v2.20/Documentation/extending/music-function-definitions)

%
%   #(define-markup-command (name-of-new-command layout props 
arguments...) (type-predictes...) ...)

%   vs.
%   name-of-new-music-function = #(define-music-function (arguments...) 
(type-predicates...) ...)

%
%   One of the reasons is that #(define-markup-command) defines not only
%   \name-of-new-command but also make-(name-of-new-command)-markup
%   to be used in scheme; see below. ]

#(define-markup-command
  (slurReplacementVShape layout props control-points)
  (number-pair-list?) ; The only actual parameter (control-points) 
should be a list of (we expect: four) number pairs

  (let* ((start (first control-points))
 (1st-directional-point (second control-points))
 (2nd-directional-point (third control-points))
 (stop (fourth control-points)))
    (interpret-markup layout props #{
  \markup {
    \path #0.1 #(list (moveto start)
  ; from start ...
  (lineto (midpoint 1st-directional-point 
2nd-directional-point))
  ; ... draw line to the midpoint of the two 
directional control points  ...

  ; ... and then to stop.
  (lineto stop))
  } #})))

#(define-markup-command
  (slurReplacementBracket layout props control-points)
  (number-pair-list?)
  (let* ((start (first control-points))
 (stop (fourth control-points))
 (1st-directional-point (second control-points))
 ; We have to find out the direction in which the bracket should
 ; protrude. The idea here is to use the first 

Re: Questions about HorizontalBracket

2020-09-13 Thread Francesco Napoleoni
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, 

Re: Questions about HorizontalBracket

2020-09-13 Thread Lukas-Fabian Moser

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



Questions about HorizontalBracket

2020-09-13 Thread Francesco Napoleoni
Hi everyone

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.

The summary of my needs can be found in the attached image.

Can anyone shed some light, please?

Thanks
Francesco Napoleoni
\version "2.19.83"

semitone = {
  % How can I obtain a V-shaped bracket?
  \once \override Voice.HorizontalBracket.bracket-flare = #'(3 . 3)
}

tone = {
  \once \override Voice.HorizontalBracket.bracket-flare = #'(0 . 0)
  \once \override Voice.HorizontalBracket.edge-height = #'(0.5 . 0.5)
}

\layout {
  \context {
\Staff
\remove "Time_signature_engraver"
\remove "Collision_engraver"
  }
  \context {
\Voice
\consists "Horizontal_bracket_engraver"

% Is there any better alternative to get the edges of the bracket
% aligned to the center of the notes?
\override HorizontalBracket.shorten-pair = #'(0.9 . 1)
  }
}

\score {
  \new Staff \relative c' <<
\cadenzaOn

\new Voice {
  \voiceOne
  \tone
  c1\startGroup d\stopGroup
  \semitone
  e\startGroup f\stopGroup
  \tone
  g\startGroup a\stopGroup
  \semitone
  b\startGroup c\stopGroup
}

\new Voice \with {
  \override HorizontalBracket.direction = #UP
} {
  \voiceTwo
  \hideNotes
  c1
  \tone
  d\startGroup e\stopGroup
  \tone
  f\startGroup g\stopGroup
  \tone
  a\startGroup b\stopGroup
  c
}
  >>
}