Define new articulation with markup or path (instead of glyph)

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

Define new articulation with markup or path (instead of glyph)

Urs Liska-3

Hi,

I need to define new items to attach to notes that center-align to the note heads. These should be either regular texts or drawn paths. I know a few approaches but haven't found a working solution for me, so I'd be happy to get some hints (or solutions).

As I want *some* of the objects to be used as a drop-in replacement for existing commands like \upbow the following solution doesn't work:

    upbow =
    #(define-music-function ()()
       #{
         \tweak self-alignment-X #CENTER
         -\markup "hin."
       #})

because

    c4 \upbow

will fail with the complaint about an unexpected post-event (I have to write -\upbow)

The second limitation to this is that it doesn't align to the notehead's center but to the stem. Therefore I thought creating either an articulation or a dynamic text.

Creating a new articulation (or overwriting the definition of an existing one) seems tempting using something like

    #(append! default-script-alist
       (list
        `("scriptDownbow"
           . ((script-stencil . (feta . ("dfermata" . "ufermata")))
              ; any other properties
              (toward-stem-shift-in-column . 0.0)
              (padding . 1)
              (avoid-slur . around)
              (direction . ,UP)
              ))
        ))

    % create postfix commands to use the articulations
    downbow = #(make-articulation "scriptDownbow")

This successfully makes \downbow use the fermata instead of the regular glyph. However, it seems there's no way to make that use a markup or a path instead of an Emmentaler glyph (if this old information (https://www.mail-archive.com/lilypond-user@.../msg64645.html) still holds true).

Creating a dynamic script is easy:

    upbow =
    #(make-dynamic-script
      (markup
       (#:normal-text "hin.")))

However, I have two problems with this approach:

How can I make sure the dynamics stay on one vertical line? Attaching them directly to the note gives the result as in the attached "horizontal-shift.png" while dynamics printed in a Dynamics context don't seem to avoid collisions, and I don't know how to avoid that.

So, is there any reasonable way to create something (function, articulation, dynamics) with the following characteristics:

  • can be written like articulations/dynamics (i.e. with or without postfix operator)
  • can be forced to a common vertical baseline with other elements
  • pushes notecolumns to obtain the necessary space (like \textLengthOn does for markup)

?

Any advice would be appreciated!
Urs


_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user

horizontal-shift.png (11K) Download Attachment
dynamics-clash.png (14K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Lukas-Fabian Moser
Hi Urs,

>
>     upbow =
>     #(define-music-function ()()
>        #{
>          \tweak self-alignment-X #CENTER
>          -\markup "hin."
>        #})
>
> because
>
>     c4 \upbow
>
> will fail with the complaint about an unexpected post-event (I have to
> write -\upbow)
>

If you use define-event-function instead of define-music-function, you
may write c4 \upbow.

Best
Lukas

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Urs Liska-3


Am 30.09.2018 um 12:08 schrieb Lukas-Fabian Moser:

> Hi Urs,
>>
>>     upbow =
>>     #(define-music-function ()()
>>        #{
>>          \tweak self-alignment-X #CENTER
>>          -\markup "hin."
>>        #})
>>
>> because
>>
>>     c4 \upbow
>>
>> will fail with the complaint about an unexpected post-event (I have
>> to write -\upbow)
>>
>
> If you use define-event-function instead of define-music-function, you
> may write c4 \upbow.

Thank you, this helps.
Since this is a no-argument function it seems acceptable to handle the
horizontal alignment by replacing the self-alignment-X tweak with a
manually set X-offset tweak.

Urs

>
> Best
> Lukas


_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Thomas Morley-2
In reply to this post by Urs Liska-3
Hi Urs,

sorry for the late reply.
Right now I've a cold (not working in my regular job), so I've more
time to look into lilypond-tasks.
While waiting for a guile-complie to finish...

Am So., 30. Sep. 2018 um 12:01 Uhr schrieb Urs Liska <[hidden email]>:

> Creating a new articulation (or overwriting the definition of an existing one) seems tempting using something like
>
>     #(append! default-script-alist
>        (list
>         `("scriptDownbow"
>            . ((script-stencil . (feta . ("dfermata" . "ufermata")))
>               ; any other properties
>               (toward-stem-shift-in-column . 0.0)
>               (padding . 1)
>               (avoid-slur . around)
>               (direction . ,UP)
>               ))
>         ))
>
>     % create postfix commands to use the articulations
>     downbow = #(make-articulation "scriptDownbow")
>
> This successfully makes \downbow use the fermata instead of the regular glyph. However, it seems there's no way to make that use a markup or a path instead of an Emmentaler glyph (if this old information (https://www.mail-archive.com/lilypond-user@.../msg64645.html) still holds true).

Still true.
As long as you try to fill "script-stencil" you are limited to the
script-glyphs Emmentaler provides.
But there is no need to go for script-stencil, you may let it unset
and define only stencil, perhaps with different result for up/down.
See below.

[...]

> So, is there any reasonable way to create something (function, articulation, dynamics) with the following characteristics:
>
> can be written like articulations/dynamics (i.e. with or without postfix operator)
> can be forced to a common vertical baseline with other elements
> pushes notecolumns to obtain the necessary space (like \textLengthOn does for markup)
>
> ?
>
> Any advice would be appreciated!
> Urs

In the code below two new articulations are defined: path and polygon.
The polygon-definition is after a idea by Torsten, I even extended it
a bit. Probably too complicated for a simple
proof-of-concept-example...

Nevertheless here all the code:

\version "2.19.82"

%% Not sure if needed, though, better be paranoid and work on a copy of
%% default-script-alist to avoid possible bleed-over.
#(define my-script-alist default-script-alist)

#(define my-polygon-stil
  ;; After an idea by Torsten
  ;; different stencils are returned relying on 'direction
  (lambda (grob)
    (let* ((th 0.1)
           (dir (ly:grob-property grob 'direction))
           (nmbr
             (if (positive? dir)
                 6
                 3))
           ;; Value @code{6} returns a hexagon, try others.
           (alpha-step (/ (* 2 PI) nmbr))
           (alpha-start (/ alpha-step 2))
           (radius 0.7)
           (polypoints-list
             (let loop ((alpha alpha-start))
               (if (> alpha (* 2 PI))
                   '()
                   (cons (* (abs radius) (sin alpha))
                         (cons (- 0 (* (abs radius) (cos alpha)))
                               (loop (+ alpha alpha-step)))))))
           (polypoints-pairs
             (let lp ((ppts polypoints-list))
               (if (or (null? ppts) (odd? (length polypoints-list)))
                   '()
                   (cons (cons (list-ref ppts 0) (list-ref ppts 1))
                         (lp (drop ppts 2))))))
           (x-ext
             (cons
               (reduce min +inf.0 (map car polypoints-pairs))
               (reduce max -inf.0 (map car polypoints-pairs))))
           (y-ext
             (cons
               (reduce min +inf.0 (map cdr polypoints-pairs))
               (reduce max -inf.0 (map cdr polypoints-pairs)))))

    (ly:make-stencil
      `(polygon ',polypoints-list  ,th #f)
      x-ext
      y-ext))))

#(define my-path-stil
  (lambda (grob)
    (let* ((dir (ly:grob-property grob 'direction)))
    (grob-interpret-markup grob
      #{
            \markup
              \path
              #0.17
              #`((moveto 0 0)
                (lineto ,(if (> dir 0) 0 0.75) 0.75))
      #}))))


#(define my-polygon-list
  `("polygon"
    . (
       (avoid-slur . inside)
       (padding . 0.50)
       (stencil . ,my-polygon-stil)
       (side-relative-direction . ,DOWN))))

#(define my-path-list
  `("path"
    . (
       (avoid-slur . inside)
       (padding . 0.50)
       (stencil . ,my-path-stil)
       (side-relative-direction . ,DOWN))))

%% A macro setting the lists from above in the copy of `default-script-alist´
%% For now, every new script has to be inserted in a single run.
%% TODO
%% Probably better to do simpler list processing with append, cons etc
#(define-macro (set-my-script-alist! ls-1 ls-2)
"Creates a new key-value-pair, taken from ls-2, in ls-1"
 `(set! ,ls-1
    (if (and (pair? ,ls-2) (pair? (cadr ,ls-2)))
        (assoc-set! ,ls-1 (car ,ls-2) (cdr ,ls-2))
        (begin
          (ly:warning (_"Unsuitable list\n\t~a \n\tdetected, ignoring. ") ,ls-2)
          ,ls-1))))

#(set-my-script-alist! my-script-alist my-polygon-list)
#(set-my-script-alist! my-script-alist my-path-list)


%% To use the new scripts call them in \layout
\layout {
  \context {
    \Score
    scriptDefinitions = #my-script-alist
  }
}

polygon = #(make-articulation "polygon")
path = #(make-articulation "path")

%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% EXAMPLES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% simple

{ c'4\staccato c'\path c'\polygon }

%% fancy

#(define (make-script x)
  (make-music 'ArticulationEvent
              'articulation-type x))

#(define (add-script m x)
 (cond ((eqv? (ly:music-property m 'name) 'EventChord)
        (set! (ly:music-property m 'elements)
              (append  (ly:music-property m 'elements)
                       (list (make-script x)))))
       ((eqv? (ly:music-property m 'name) 'NoteEvent)
          (set! (ly:music-property m 'articulations)
             (append  (ly:music-property m 'articulations)
                      (list (make-script x)))))
       (else #f))
 m)

addArt =
#(define-music-function (parser location type music) (string? ly:music?)
  (music-map (lambda (m) (add-script m type)) music))

mus = {
  c'8( d') e'( f') g'( a') b'( c'')
  d''( e'') f''( g'') a''( a'') r4
}

staffMus = {
  \voiceOne
  \mus
  \voiceTwo
  \mus
}

\score {
  <<
    \new Staff \addArt "staccato" \staffMus
    \new Staff \addArt "polygon" \staffMus
    \new Staff \addArt "path" \staffMus
  >>
  \layout {
    \context {
      \Voice
       \override Script.avoid-slur = #'inside
       \override Script.toward-stem-shift = 1.0
       \override Script.toward-stem-shift-in-column = 0.0
    }
  }
}

HTH,
  Harm

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Urs Liska-3
Hi Harm,

sorry, I forgot to reply to that until now.


Am 12.10.2018 um 14:35 schrieb Thomas Morley:

> Hi Urs,
>
> sorry for the late reply.
> Right now I've a cold (not working in my regular job), so I've more
> time to look into lilypond-tasks.
> While waiting for a guile-complie to finish...
>
> Am So., 30. Sep. 2018 um 12:01 Uhr schrieb Urs Liska <[hidden email]>:
>
>> Creating a new articulation (or overwriting the definition of an existing one) seems tempting using something like
>>
>>      #(append! default-script-alist
>>         (list
>>          `("scriptDownbow"
>>             . ((script-stencil . (feta . ("dfermata" . "ufermata")))
>>                ; any other properties
>>                (toward-stem-shift-in-column . 0.0)
>>                (padding . 1)
>>                (avoid-slur . around)
>>                (direction . ,UP)
>>                ))
>>          ))
>>
>>      % create postfix commands to use the articulations
>>      downbow = #(make-articulation "scriptDownbow")
>>
>> This successfully makes \downbow use the fermata instead of the regular glyph. However, it seems there's no way to make that use a markup or a path instead of an Emmentaler glyph (if this old information (https://www.mail-archive.com/lilypond-user@.../msg64645.html) still holds true).
> Still true.
> As long as you try to fill "script-stencil" you are limited to the
> script-glyphs Emmentaler provides.
> But there is no need to go for script-stencil, you may let it unset
> and define only stencil, perhaps with different result for up/down.
> See below.

That is good to know. I think this would make for a very useful tutorial
post, explaining things a bit more in-depth so others (like me) may
become somewhat more familiar with the stuff.

>
> [...]
>> So, is there any reasonable way to create something (function, articulation, dynamics) with the following characteristics:
>>
>> can be written like articulations/dynamics (i.e. with or without postfix operator)
>> can be forced to a common vertical baseline with other elements
>> pushes notecolumns to obtain the necessary space (like \textLengthOn does for markup)
>>
>> ?
>>
>> Any advice would be appreciated!
>> Urs
> In the code below two new articulations are defined: path and polygon.
> The polygon-definition is after a idea by Torsten, I even extended it
> a bit. Probably too complicated for a simple
> proof-of-concept-example...

Probably right, but nevertheless I managed to boil it down to do (only)
what I currently need.

>
> Nevertheless here all the code:
>
> \version "2.19.82"
>
> %% Not sure if needed, though, better be paranoid and work on a copy of
> %% default-script-alist to avoid possible bleed-over.
> #(define my-script-alist default-script-alist)

Except that this doesn't *create* a copy but only a reference, isn't it?
So your changes to my-script-alist also affect default-script-alist.

>
>
> HTH,
>    Harm

Indeed, thank you very much. Here's my solution, I created the "strich"
articulation:

#(define strich-stencil
    (lambda (grob)
      (grob-interpret-markup grob
        #{
          \markup
          \path
          #0.19
          #`((moveto 0 0)
             (lineto 0 0.75))
        #})))

#(define strich-list
    `("strich" .
       ((avoid-slur . inside)
        (padding . 0.5)
        (stencil . ,strich-stencil)
        (side-relative-direction . ,DOWN))))

%% A macro setting the lists from above in the copy of `default-script-alist´
%% For now, every new script has to be inserted in a single run.
%% TODO
%% Probably better to do simpler list processing with append, cons etc
#(define-macro (set-my-script-alist! ls-1 ls-2)
    "Creates a new key-value-pair, taken from ls-2, in ls-1"
    `(set! ,ls-1
           (if (and (pair? ,ls-2) (pair? (cadr ,ls-2)))
               (assoc-set! ,ls-1 (car ,ls-2) (cdr ,ls-2))
               (begin
                (ly:warning (_"Unsuitable list\n\t~a \n\tdetected, ignoring. ") ,ls-2)
                ,ls-1))))

#(set-my-script-alist! default-script-alist strich-list)

\layout {
   \context {
     \Score
     scriptDefinitions = #default-script-alist
   }
}

strich = #(make-articulation "strich")

Best
Urs

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Thomas Morley-2
Hi Urs,

Am Sa., 13. Okt. 2018 um 00:00 Uhr schrieb Urs Liska <[hidden email]>:

> > %% Not sure if needed, though, better be paranoid and work on a copy of
> > %% default-script-alist to avoid possible bleed-over.
> > #(define my-script-alist default-script-alist)
>
> Except that this doesn't *create* a copy but only a reference, isn't it?
> So your changes to my-script-alist also affect default-script-alist.

Well, if I add

#(pretty-print
  (lset-difference
    equal?
    my-script-alist
    default-script-alist))

at the end of my file, I get:

(("path"
  (avoid-slur . inside)
  (padding . 0.5)
  (stencil . #<procedure my-path-stil (grob)>)
  (side-relative-direction . -1))
 ("polygon"
  (avoid-slur . inside)
  (padding . 0.5)
  (stencil . #<procedure my-polygon-stil (grob)>)
  (side-relative-direction . -1)))

If default script-alist would be changed as well, then the result should be '().

Or am I thinking wrongly?

Cheers,
  Harm

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Urs Liska-3
Hi Harm,


Am 13.10.2018 um 01:18 schrieb Thomas Morley:

> Hi Urs,
>
> Am Sa., 13. Okt. 2018 um 00:00 Uhr schrieb Urs Liska <[hidden email]>:
>
>>> %% Not sure if needed, though, better be paranoid and work on a copy of
>>> %% default-script-alist to avoid possible bleed-over.
>>> #(define my-script-alist default-script-alist)
>> Except that this doesn't *create* a copy but only a reference, isn't it?
>> So your changes to my-script-alist also affect default-script-alist.
> Well, if I add
>
> #(pretty-print
>    (lset-difference
>      equal?
>      my-script-alist
>      default-script-alist))
>
> at the end of my file, I get:
>
> (("path"
>    (avoid-slur . inside)
>    (padding . 0.5)
>    (stencil . #<procedure my-path-stil (grob)>)
>    (side-relative-direction . -1))
>   ("polygon"
>    (avoid-slur . inside)
>    (padding . 0.5)
>    (stencil . #<procedure my-polygon-stil (grob)>)
>    (side-relative-direction . -1)))
>
> If default script-alist would be changed as well, then the result should be '().
>
> Or am I thinking wrongly?

Ah, no, that's correct. But it's not because of your initial "copy" with
define. See

\version "2.19.82"

#(define list-a '((one . 1)(two . 2)))
#(define list-clone list-a)
#(define new-list '())
#(set! list-clone (assoc-set! list-a 'three 3))
#(set! new-list (assoc-set! list-a 'three 3))
#(define new-list-b (assoc-set! list-a 'three 3))
#(display list-a)#(newline)
#(display list-clone)#(newline)
#(display new-list)#(newline)
#(display new-list-b)

You'll notice that list-clone, new-list, and new-list-b are all the same
in the end. If I'm not mistaken it's because assoc-set! is the point
where the copy is actually made. So you could either 'define' the new
list somewhere or probably initialize it to an empty list before
performing the assoc-set!
This also indicates that using assoc-set is potentially inefficient when
performed numerous times. It would seem better to first check if the key
is already present and if necessary cons the new element to the
beginning. But I may be mistaken because I don't know how assoc-set!
*really* works internally, maybe it's more optimized than I'd think.

Best
Urs

>
> Cheers,
>    Harm


_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Aaron Hill
On 2018-10-13 12:01 am, Urs Liska wrote:

> Hi Harm,
>
>
> Am 13.10.2018 um 01:18 schrieb Thomas Morley:
>> Hi Urs,
>>
>> Am Sa., 13. Okt. 2018 um 00:00 Uhr schrieb Urs Liska
>> <[hidden email]>:
>>
>>>> %% Not sure if needed, though, better be paranoid and work on a copy
>>>> of
>>>> %% default-script-alist to avoid possible bleed-over.
>>>> #(define my-script-alist default-script-alist)
>>> Except that this doesn't *create* a copy but only a reference, isn't
>>> it?
>>> So your changes to my-script-alist also affect default-script-alist.
>> Well, if I add
>>
>> #(pretty-print
>>    (lset-difference
>>      equal?
>>      my-script-alist
>>      default-script-alist))
>>
>> at the end of my file, I get:
>>
>> (("path"
>>    (avoid-slur . inside)
>>    (padding . 0.5)
>>    (stencil . #<procedure my-path-stil (grob)>)
>>    (side-relative-direction . -1))
>>   ("polygon"
>>    (avoid-slur . inside)
>>    (padding . 0.5)
>>    (stencil . #<procedure my-polygon-stil (grob)>)
>>    (side-relative-direction . -1)))
>>
>> If default script-alist would be changed as well, then the result
>> should be '().
>>
>> Or am I thinking wrongly?
>
> Ah, no, that's correct. But it's not because of your initial "copy"
> with define. See
>
> \version "2.19.82"
>
> #(define list-a '((one . 1)(two . 2)))
> #(define list-clone list-a)
> #(define new-list '())
> #(set! list-clone (assoc-set! list-a 'three 3))
> #(set! new-list (assoc-set! list-a 'three 3))
> #(define new-list-b (assoc-set! list-a 'three 3))
> #(display list-a)#(newline)
> #(display list-clone)#(newline)
> #(display new-list)#(newline)
> #(display new-list-b)
>
> You'll notice that list-clone, new-list, and new-list-b are all the
> same in the end.

Unless Guile is performing some fanciness behind the scenes, those lists
are *not* the same in the end.  They contain the same values (equal? ->
#t), but referentially they are unique (eq? -> #f).  Each is a unique
copy because assoc-set! returned a new list rather than modifying
list-a.

> If I'm not mistaken it's because assoc-set! is the
> point where the copy is actually made. So you could either 'define'
> the new list somewhere or probably initialize it to an empty list
> before performing the assoc-set!
> This also indicates that using assoc-set is potentially inefficient
> when performed numerous times. It would seem better to first check if
> the key is already present and if necessary cons the new element to
> the beginning. But I may be mistaken because I don't know how
> assoc-set! *really* works internally, maybe it's more optimized than
> I'd think.

According to the docs [1], assoc-set! (and family) may modify the
original alist.  So whether a copy is made or not depends on an
implementation detail.  Near as I can tell, the original alist is
modified in-place when the key is found within.  But when the key is
new, the result of using acons to append the new key/value to the head
of the list results in a copy being returned.

[1]:
https://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/Adding-or-Setting-Alist-Entries.html#Adding-or-Setting-Alist-Entries

So the pattern (set! a (assoc-set! a k v)) is necessary to be absolutely
sure that "a" is modified.  Otherwise, you'll need to say (assoc-set!
(list-copy a) k v) to be absolutely sure that "a" is *not* modified.


-- Aaron Hill

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Aaron Hill
On 2018-10-13 12:29 am, Aaron Hill wrote:
> According to the docs [1], assoc-set! (and family) may modify the
> original alist.  So whether a copy is made or not depends on an
> implementation detail.  Near as I can tell, the original alist is
> modified in-place when the key is found within.  But when the key is
> new, the result of using acons to append the new key/value to the head
> of the list results in a copy being returned.
>
> [1]:
> https://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/Adding-or-Setting-Alist-Entries.html#Adding-or-Setting-Alist-Entries

Nope, I'm wrong about copying.  acons (and therefore assoc-set!) does
not copy (shallow or deep).  Consider the following:

guile> (define a '((one . 1) (two . 2)))
guile> (define b (assoc-set! a 'three 3))
guile> a
((one . 1) (two . 2))
guile> b
((three . 3) (one . 1) (two . 2))
guile> (define c (assoc-set! a 'three 3))
guile> c
((three . 3) (one . 1) (two . 2))
guile> (eq? b c)
#f
guile> (equal? b c)
#t
guile> (eq? a (cdr b))
#t
guile> (eq? (cdr b) (cdr c))
#t
guile> (set! a (assoc-set! a 'two 2.2))
guile> a
((one . 1) (two . 2.2))
guile> b
((three . 3) (one . 1) (two . 2.2))
guile> c
((three . 3) (one . 1) (two . 2.2))

While "b" and "c" are unique as far as the initial node in their
respective lists (because acons returns a new list), they share the
remainder with the same nodes within the original list.  So,
modification to the list that "a" references will propagate to "b" and
"c".

Upon reflection, this all makes sense.  So, one should probably be
explicit about copying and use list-copy (shallow) or copy-tree (deep),
as needed.

-- Aaron Hill

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Urs Liska-3
Hi Aaron,

althought this has long left the original thread's purpose I find this
extremely interesting. Would you be interested in adding some content to
https://scheme-book.ursliska.de, maybe somewhere below
https://scheme-book.ursliska.de/scheme/lists/? There is always need for
explanations that go to substantially more length than the concise Guile
manual.

Best
Urs


Am 13.10.2018 um 10:03 schrieb Aaron Hill:

> On 2018-10-13 12:29 am, Aaron Hill wrote:
>> According to the docs [1], assoc-set! (and family) may modify the
>> original alist.  So whether a copy is made or not depends on an
>> implementation detail.  Near as I can tell, the original alist is
>> modified in-place when the key is found within.  But when the key is
>> new, the result of using acons to append the new key/value to the head
>> of the list results in a copy being returned.
>>
>> [1]:
>> https://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/Adding-or-Setting-Alist-Entries.html#Adding-or-Setting-Alist-Entries 
>>
>
> Nope, I'm wrong about copying.  acons (and therefore assoc-set!) does
> not copy (shallow or deep).  Consider the following:
>
> guile> (define a '((one . 1) (two . 2)))
> guile> (define b (assoc-set! a 'three 3))
> guile> a
> ((one . 1) (two . 2))
> guile> b
> ((three . 3) (one . 1) (two . 2))
> guile> (define c (assoc-set! a 'three 3))
> guile> c
> ((three . 3) (one . 1) (two . 2))
> guile> (eq? b c)
> #f
> guile> (equal? b c)
> #t
> guile> (eq? a (cdr b))
> #t
> guile> (eq? (cdr b) (cdr c))
> #t
> guile> (set! a (assoc-set! a 'two 2.2))
> guile> a
> ((one . 1) (two . 2.2))
> guile> b
> ((three . 3) (one . 1) (two . 2.2))
> guile> c
> ((three . 3) (one . 1) (two . 2.2))
>
> While "b" and "c" are unique as far as the initial node in their
> respective lists (because acons returns a new list), they share the
> remainder with the same nodes within the original list.  So,
> modification to the list that "a" references will propagate to "b" and
> "c".
>
> Upon reflection, this all makes sense.  So, one should probably be
> explicit about copying and use list-copy (shallow) or copy-tree
> (deep), as needed.
>
> -- Aaron Hill
>
> _______________________________________________
> lilypond-user mailing list
> [hidden email]
> https://lists.gnu.org/mailman/listinfo/lilypond-user


_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user
Reply | Threaded
Open this post in threaded view
|

Re: Define new articulation with markup or path (instead of glyph)

Trevor Bača-2
Hi Urs,

I went through this a couple of months ago, and it *seems* possible to use markups (and paths) as legitimately new articulations (maybe -- and happily -- in contradiction to the assumption in your original post, if I'm reading correctly).

Here's the pattern as a MWE:

%%% BEGIN NOVEL ARTICULATION USING MARKUP %%%

baca-damp-markup =
    \markup
    \scale #'(0.75 . 0.75)
    \combine
    \bold
    \override #'(font-name . "Times") "O"
    \path #0.15
    #'(
        (moveto -.4 .7)
        (rlineto 2.4 0)
        (closepath)
        (moveto .8 -.5)
        (rlineto 0 2.4)
        )

#(append! default-script-alist
   (list
    `("bacadamp"
       . (
           (stencil . ,ly:text-interface::print)
           (text . ,baca-damp-markup)
           (avoid-slur . around)
           (padding . 0.20)
           (script-priority . 125)
           (side-relative-direction . ,DOWN)
           (skyline-horizontal-padding . 0.20)
           ;;(toward-stem-shift . 0.5)
           ))))

baca-damp = #(make-articulation "bacadamp")

\layout {
    \context {
        \Score
        scriptDefinitions = #default-script-alist
    }
}

%%% END %%%


You can then write ...

\new Staff { c'4 \baca-damp }

... and get ...

novel-articulation.png

... as output.

To note:

1. Apologies for not yet crediting the various posts in the archives that made this possible; it was a weekend or two of reading through various breadcrumbs on the list that made the pattern possible.

2. The baca- prefix is a namespacing convention since I'm always mixing with modules in Python. Replace as necessary if the snippet works for you.

3. Wrt to your three initial criteria, the example above demonstrates #1 ("no postfix operator"). Because the symbol created is a legit script, overrides like "\override Script.staff-padding = 5" do work. So maybe this helps meet your #2 ("common vertical baseline"). I'm not certain about your #3 ("pushes notecolumns to obtain the necessary space"). But some testing seems to show that *if you explicitly specify the font of markup* then Lily can find a stencil (and move note columns).

%%% BEGIN WIDE EXAMPLE %%%

estr-markup =
    \markup
    \override #'(font-name . "Times")
    "ESTREMAMENTE"

#(append! default-script-alist
   (list
    `("articestr"
       . (
           (stencil . ,ly:text-interface::print)
           (text . ,estr-markup)
           (avoid-slur . around)
           (padding . 0.20)
           (script-priority . 125)
           (side-relative-direction . ,DOWN)
           (skyline-horizontal-padding . 0.20)
           ;;(toward-stem-shift . 0.5)
           ))))

estr = #(make-articulation "articestr")

\layout {
    \context {
        \Score
        scriptDefinitions = #default-script-alist
    }
}

%%% END %%%


Then ...

\new Staff {

    c'4
    d'4
    \estr
    e'4
    f'4

}

... gives ...

wide-articulation.png

... as output.

Note that if you use "bald" markup (without a font override) Lily finds no stencil and produces no output for the articulation.

Trevor.
 


On Sat, Oct 13, 2018 at 3:07 AM Urs Liska <[hidden email]> wrote:
Hi Aaron,

althought this has long left the original thread's purpose I find this
extremely interesting. Would you be interested in adding some content to
https://scheme-book.ursliska.de, maybe somewhere below
https://scheme-book.ursliska.de/scheme/lists/? There is always need for
explanations that go to substantially more length than the concise Guile
manual.

Best
Urs


Am 13.10.2018 um 10:03 schrieb Aaron Hill:
> On 2018-10-13 12:29 am, Aaron Hill wrote:
>> According to the docs [1], assoc-set! (and family) may modify the
>> original alist.  So whether a copy is made or not depends on an
>> implementation detail.  Near as I can tell, the original alist is
>> modified in-place when the key is found within.  But when the key is
>> new, the result of using acons to append the new key/value to the head
>> of the list results in a copy being returned.
>>
>> [1]:
>> https://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/Adding-or-Setting-Alist-Entries.html#Adding-or-Setting-Alist-Entries
>>
>
> Nope, I'm wrong about copying.  acons (and therefore assoc-set!) does
> not copy (shallow or deep).  Consider the following:
>
> guile> (define a '((one . 1) (two . 2)))
> guile> (define b (assoc-set! a 'three 3))
> guile> a
> ((one . 1) (two . 2))
> guile> b
> ((three . 3) (one . 1) (two . 2))
> guile> (define c (assoc-set! a 'three 3))
> guile> c
> ((three . 3) (one . 1) (two . 2))
> guile> (eq? b c)
> #f
> guile> (equal? b c)
> #t
> guile> (eq? a (cdr b))
> #t
> guile> (eq? (cdr b) (cdr c))
> #t
> guile> (set! a (assoc-set! a 'two 2.2))
> guile> a
> ((one . 1) (two . 2.2))
> guile> b
> ((three . 3) (one . 1) (two . 2.2))
> guile> c
> ((three . 3) (one . 1) (two . 2.2))
>
> While "b" and "c" are unique as far as the initial node in their
> respective lists (because acons returns a new list), they share the
> remainder with the same nodes within the original list.  So,
> modification to the list that "a" references will propagate to "b" and
> "c".
>
> Upon reflection, this all makes sense.  So, one should probably be
> explicit about copying and use list-copy (shallow) or copy-tree
> (deep), as needed.
>
> -- Aaron Hill
>
> _______________________________________________
> lilypond-user mailing list
> [hidden email]
> https://lists.gnu.org/mailman/listinfo/lilypond-user


_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user


--

_______________________________________________
lilypond-user mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user