Hi,

> On Thu, Sep 3, 2015 at 2:42 PM, Caio Giovaneti de Barros <
> [email protected]> wrote:
>
>> Hello David!
>>
>> On 02-09-2015 10:19, David Nalesnik wrote:
>>
>>>
>>> Try this out:
>>>
>>> http://lists.gnu.org/archive/html/lilypond-user/2013-05/msg00405.html
>>>
>>>
>> This looks ok, but I'm not sure it'll work form me. I need to tweak the
>> Spanner a bit to centralize the line height vertically in relation to the
>> left and right bounds
>
>
[...]


>
>
>> and also use custom symbols for each bound.
>
>
>
I worked with Harm's function (link above) and produced the attached.  It
uses the same technique of \fill-line (or rather, \justify-line) to space
the text items, but draws the connecting lines differently.  Instead of
using \whiteout to break the line, it draws each line individually.

The function will accept a list of markups or strings.  "" acts as a
spacer, forcing text elements farther apart and others correspondingly
closer  (exactly as multiple dashes do in Harm's function).

The syntax for inputting a list of markups is unwieldy.   Is there a nicer
way to handle this sort of input?

+Important*:  This needs the to-be-released 2.19.27 (or current master) to
be fully functional.  You'd just need to uncomment the lines indicated in
justify-line-helper to get fully overridable lines: dashed, dotted, zigzag,
customization of dashes, etc.  At the moment you'll see solid lines.
 (Though there's a pretty unsatisfactory workaround for dashed lines
included.)

Hopefully another step forward!

David

Image attached in further post.
%%%%
\version "2.19.25"

%% Only needed for versions before 2.19.27.
#(define (make-dashed-line-stencil width startx starty endx endy on off phase)
   (let ((xext (cons (min startx endx) (max startx endx)))
         (yext (cons (min starty endy) (max starty endy))))
     (ly:make-stencil
      (list 'dashed-line
        ; thick on off dx dy phase
        width on off (- endx startx) (- endy starty) phase)
      ; Since the line has rounded edges, we have to / can safely add half the
      ; width to all coordinates!
      (interval-widen xext (/ width 2))
      (interval-widen yext (/ width 2)))))

%% The following is taken from scm/define-markup-commands.scm.  It is
%% needed to emulate \justify-line, so that texts may be spread
%% evenly along TextSpanner.
#(define (get-fill-space
          word-count line-width word-space text-widths constant-space?)
   "Calculate the necessary paddings between adjacent texts in a
single justified line.  The lengths of all texts are stored in
@var{text-widths}.  When @var{constant-space?} is @code{#t}, the
formula for the padding between texts is:
padding = (line-width - total-text-width)/(word-count - 1)
When @var{constant-space?} is @code{#f}, the formula for the
padding between interior texts a and b is:
padding = line-width/(word-count - 1) - (length(a) + length(b))/2
In this case, the first and last padding have to be calculated
specially using the whole length of the first or last text.
All paddings are checked to be at least word-space, to ensure that
no texts collide.
Return a list of paddings."
   (cond
    ((null? text-widths) '())
    (constant-space?
     (make-list
      (1- word-count)
      ;; Ensure that space between words cannot be
      ;; less than word-space.
      (max
       word-space
       (/ (- line-width (apply + text-widths))
         (1- word-count)))))

    ;; special case first padding
    ((= (length text-widths) word-count)
     (cons
      (- (- (/ line-width (1- word-count)) (car text-widths))
        (/ (cadr text-widths) 2))
      (get-fill-space
       word-count line-width word-space (cdr text-widths)
       constant-space?)))
    ;; special case last padding
    ((= (length text-widths) 2)
     (list (- (/ line-width (1- word-count))
             (+ (/ (car text-widths) 2) (cadr text-widths)))
       0))
    (else
     (let ((default-padding
            (- (/ line-width (1- word-count))
              (/ (+ (car text-widths) (cadr text-widths)) 2))))
       (cons
        (if (> word-space default-padding)
            word-space
            default-padding)
        (get-fill-space
         word-count line-width word-space (cdr text-widths)
         constant-space?))))))

%% Adapted from scm/define-markup-commands.scm.
#(define (justify-line-helper
          grob args word-space line-width constant-space? padding)
   "Return a stencil which spreads @var{args} along a line of width
@var{line-width}, with spaces filled by a line. If @var{constant-space?}
is set to @code{#t}, the space between words is constant.  If @code{#f}, the
distance between words varies according to their relative lengths."
   (let* ((orig-stencils
           (map (lambda (a) (grob-interpret-markup grob a)) args))
          (stencils
           (map (lambda (stc)
                  (if (ly:stencil-empty? stc X)
                      point-stencil
                      stc))
             orig-stencils))
          (text-widths
           (map (lambda (stc)
                  (interval-length (ly:stencil-extent stc X)))
             stencils))
          (text-width (apply + text-widths))
          (word-count (length stencils))
          (fill-space
           (cond
            ((= word-count 1)
             (list
              (/ (- line-width text-width) 2)
              (/ (- line-width text-width) 2)))
            ((= word-count 2)
             (list
              (- line-width text-width)))
            (else
             (get-fill-space
              word-count line-width word-space text-widths
              constant-space?))))
          (line-contents (if (= word-count 1)
                             (list
                              point-stencil
                              (car stencils)
                              point-stencil)
                             stencils)))
     
     (if (null? (remove ly:stencil-empty? orig-stencils))
         empty-stencil
         (begin
          (set! line-contents
                (let loop ((contents line-contents) ; stencils to distribute
                            (space fill-space) ; areas to fill with lines
                            (X-coord 0.0) ; where to place segments of spanner
                            (accum 0.0) ; for drawing lines though spacers
                            (left-text (car line-contents)) ; how segment starts
                            (stil empty-stencil)) ; the stencil to return
                  ; Since segments are built text + line below, we need a
                  ; special case for line endings, which consist of a stencil
                  ; and no continuation.
                  (if (null? (cdr contents))
                      ; no need to draw break marker
                      (if (eq? (car contents) point-stencil)
                          stil
                          (ly:stencil-add stil 
                            (ly:stencil-translate-axis
                             (car contents) X-coord X)))
                      (let ((current (car contents))
                            (next (cadr contents)))
                        (cond
                         ; don't draw individual lines for successive ""
                         ; rather keep a tally so that a single line will
                         ; be drawn once a visible stencil (or the end
                         ; of the line) is encountered
                         ((and (not (eq? next (last contents)))
                               (eq? next point-stencil))
                          (loop
                           (cdr contents)
                           (cdr space)
                           X-coord
                           (+ accum (car space))
                           left-text ; keep track of beginning of segment
                           stil))
                         (else
                          (let* ((start-offset
                                  (if (eq? left-text point-stencil)
                                      0.0 padding))
                                 (end-offset
                                  (if (eq? next point-stencil)
                                      0.0 (- padding)))
                                 (line (make-line-stencil
                                        0.1
                                        start-offset 0.0
                                        (+ (car space) accum end-offset) 0.0))
                                 ; inferior dashed line!
                                 ;(line (make-dashed-line-stencil
                                 ; 0.1 ; thickness
                                 ;  start-offset 0.0
                                 ; (+ (car space) accum end-offset) 0.0
                                 ;  1 ; on
                                 ;  1 ; off
                                 ;  0 ; phase
                                 ;  ))
                                 ; Uncomment the following definition of line
                                 ; for use with versions 2.19.27 and higher.
                                 ; This allows different line styles and
                                 ; modifications through overrides of
                                 ; TextSpanner grob.
                                 ; (line (ly:line-interface::line
                                 ;        grob
                                 ;        start-offset 0.0
                                 ;       (+ (car space) accum end-offset) 0.0))
                                 (text-width
                                  (interval-length
                                   (ly:stencil-extent left-text X)))
                                 (text-and-line
                                  (ly:stencil-add left-text
                                    (ly:stencil-translate-axis
                                     line text-width X))))
                            (loop
                             (cdr contents)
                             (cdr space)
                             (+ X-coord text-width (car space) accum)
                             0.0
                             next
                             (ly:stencil-add
                              stil
                              (ly:stencil-translate-axis
                               text-and-line X-coord X))))))))))))
     
     (if (> word-count 1)
         ;; shift s.t. stencils align on the left edge, even if
         ;; first stencil had negative X-extent (e.g. center-column)
         ;; (if word-count = 1, X-extents are already normalized in
         ;; the definition of line-contents)
         (set! line-contents
               (ly:stencil-translate-axis
                line-contents
                (- (car (ly:stencil-extent (car stencils) X)))
                X)))
     line-contents))

%% Based on addTextSpannerText, by Thomas Morley.  See
%% http://www.mail-archive.com/lilypond-user%40gnu.org/msg81685.html
addT =
#(define-music-function (text) (list?)
   #{
     % The following two overrides are needed to give the text spanner
     % stencil the correct length.  Temporary solution.  We shouldn't
     % need to calculate a default stencil, then replace it.
     \once \override TextSpanner.bound-details.right.text = #(last text)
     \once \override TextSpanner.bound-details.right-broken.text = ##f
     
     \once \override TextSpanner.stencil =
     #(lambda (grob)
        (let* ((stil (ly:line-spanner::print grob))
               ;; have we been split?
               (orig (ly:grob-original grob))
               ;; if yes, get the split pieces (our siblings)
               (siblings (if (ly:grob? orig)
                             (ly:spanner-broken-into orig)
                             '())))
          (if (> (length siblings) 2)
              (begin
               (ly:warning "More than two linebreaks are not supported.")
               stil)
              (let* (;; split the input-string into parts
                      (text-lst text)
                      ;; how many parts did we get?
                      (txt-lst-length (length text-lst))
                      ;; get a divisor to split `text-lst' into parts 
                      ;; to be printed before and after the line break.
                      ;; TODO: find better method
                      (partial (if (null? siblings)
                                   1
                                   (floor (/ txt-lst-length
                                            (length siblings)))))
                      ;; get the parts
                      ;; adding an empty string at end of first line,
                      ;; and start of second line
                      (head (append (list-head text-lst partial)
                              (list (make-null-markup))))
                      (tail (cons (make-null-markup)
                              (list-tail text-lst partial)))
                      (stil-ext-X (ly:stencil-extent stil X))
                      (line-width (interval-length stil-ext-X))
                      (text-to-use
                       (cond
                        ((null? siblings) text-lst)
                        ((eq? grob (car siblings)) head)
                        ((eq? grob (cadr siblings)) tail)))
                      (padding (ly:grob-property grob 'padding 0.0)))
          
                (ly:stencil-translate-axis
                 (justify-line-helper
                  grob text-to-use 0.1 line-width #t padding)
                 (car stil-ext-X) X)))))
   #})

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\relative c'' {
  \addT #(list "ral" "len" "tan" "do")
  c,1\startTextSpan
  d'2 d\stopTextSpan
  
  \override TextSpanner.padding = 0.5
  \addT #(list
          #{ \markup \raise #-0.5 "ral" #} 
          #{ \markup \raise #-0.5 "len" #}
          #{ \markup \raise #-0.5 "tan" #}
          #{ \markup \raise #-0.5 "do" #}
          )
  c,1\startTextSpan
  \break
  d'2 d\stopTextSpan
  
  \addT #'("ral" "len" "" "" "" "tan" "" "" "" "" "do")
  \break
  c,1\startTextSpan
  d'2 d\stopTextSpan
  
  \override TextSpanner.bound-details.left.padding = -0.5
  \override TextSpanner.bound-details.right.padding = 1
  \addT #(list
          #{ \markup \left-align \draw-circle #1 #0.2 ##t #}
          #{ \markup \left-align \with-color #grey \draw-circle #1 #0.2 ##t #}
          #{ \markup \left-align \draw-circle #1 #0.2 ##f #} )
  
  \revert TextSpanner.padding
  \override TextSpanner.bound-details.right-broken.padding = 0.0
  c1~\startTextSpan
  c1\stopTextSpan
  
  \addT #(list
          #{ \markup \left-align \draw-circle #1 #0.2 ##t #}
          #{ \markup \left-align \with-color #grey \draw-circle #1 #0.2 ##t #}
          #{ \markup \left-align \draw-circle #1 #0.2 ##f #} )
  c1\startTextSpan
  \break
  d'2 d\stopTextSpan
  \break
}

\layout {
  ragged-right = ##f
}
_______________________________________________
lilypond-user mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/lilypond-user

Reply via email to