Jump to content

Shear markup

From LilyPond wiki

This superseeds snippet 1166 and snippet 1167.

\version "2.24.0"

%% Thabks to Arnold from the geman forum
%% https://lilypondforum.de/index.php/topic,1216.msg6404.html#msg6404

#(define-markup-command (pop-override layout props arg)
  (markup?)
  "pop the last override in the markup chain"
  (let ((popped-props (if (> (length props) 1) (cdr props) props)))
   (interpret-markup layout popped-props arg)))

#(define-markup-command (slant layout props phi arg)
  (number? markup?)
  #:category other
  #:properties ((par #t)
                (tilt 0.0)
                (finalrotation 0.0)
                (extrascaling '(1 . 1)))
  "
A geometric transformation which may be described in two ways:
 a) Shear Mapping: Cut your image in small stripes parallel to your
    base axis (e.g. X axis), then move them parallel to your base axis,
    the distance is proportional to the distance from the base axis.
 b) Skew Coordinates: See your input image as a geometric list by the
    cartesian coordiate system relative to your base axes (e.g. X axis
    and perpendicular the Y axis) - name these axes U resp. V. Now the
    V axis will be rotated to manipulate your image. Your U-V coordiante
    system is no longer cartesian, because it's no longer pependicular.
 Tranformation matrix for an angle @var{phi}, default tilt, type a
 resp. property par = ##t:
    xx = 1.0    xy = tan(w)
    yx = 0.0    yy = 1.0
 Tranformation matrix for an angle @var{phi}, default tilt, type b
 resp. property par = ##f:
    xx = 1.0    xy = sin(w)
    yx = 0.0    yy = cos(w)
 Relation from type b to type a: the original Y axis (V axis) is
 scaled by factor 1/cos(w)

 Properties for this markup:
   @var{par} is #t for Type a (default), #f for Type b
   @var{tilt} specifies the invariant axis, 0 for X axis (default),
   90 for Y axis, other values possible
   @var{finalrotation} specifies an additional rotation appended to this
   shear / skew mapping
   @var{extrascaling} will apply an extra scaling to the U and V axis
   
 Arguments for this markup:
   @var{phi} is the shear angle. (allmost) ±90° will force geometric
   problems.
   And finally the markup to be transformed.
   
Technically there is no SCHEME function available to apply a
ly:transform? object to a stencil. Therefore roation and scale
statements are used to fullfill the task. 
Once the U and V axis are properly scaled, the stencil is rotated so
the U axis directs 45° up to the right and the V axis directs 45° up to
the left. A properly choosen scale factor pair will rotate both axes
together (or away from each other) by a specified angle without scaling
these axes. A final back rotation completes the sequence.
   "
  (let* ((half-phi (* 0.5 phi))
         (phi-dir (ly:directed phi))
         (cos-phi (car phi-dir))
         (half-dir (ly:directed half-phi))
         (sin-part (cdr half-dir))
         (cos-part (car half-dir))
         (px (- cos-part sin-part))
         (py (+ cos-part sin-part))
         (stil (interpret-markup layout props arg))
         (need-first-rot (or par
                             (not (= (car extrascaling) 1))
                             (not (= (cdr extrascaling) 1))))
         (first-scale (if par
                       (cons (car extrascaling) (/ (cdr extrascaling) cos-phi))
                       extrascaling)))
   ; for debugging:
   ; (for-each display (list "\n\\slant " phi " (par=" par " ori=" tilt " fin=" finalrotation " scl=" extrascaling ")\n"
   ;  "  cos-phi=" (ly:number->string cos-phi)
   ;  " sin-part=" (ly:number->string sin-part)
   ;  " cos-part=" (ly:number->string cos-part)
   ;  " first-scale=" first-scale
   ;  "\n"))
   (if (< (abs cos-phi) 0.01) (ly:program-error
     "slant markup with angle too close to 90° may cause runtime error!"))
   (if (< (abs px) 0.00001)
    (set! px (if (negative? px) -0.00001 0.00001)))
   (if (< (abs py) 0.00001)
    (set! py (if (negative? py) -0.00001 0.00001)))
   (let* ((v-pos-stil
           (if need-first-rot
            (ly:stencil-rotate-absolute
             (ly:stencil-scale
              (if (= tilt 0.0)
               stil
               (ly:stencil-rotate-absolute
                stil
                (- tilt) 0.0 0.0))
              (car first-scale) (cdr first-scale))
             45.0 0.0 0.0)
            (ly:stencil-rotate-absolute stil (- 45.0 tilt) 0.0 0.0)))
          (final-stil
           (ly:stencil-rotate-absolute
            (ly:stencil-scale v-pos-stil px py)
            (+ -45.0 (- half-phi) tilt finalrotation) 0.0 0.0))
          (final-expr (ly:stencil-expr final-stil))
          (final-X-ext (stencil-true-extent final-stil X))
          (final-Y-ext (stencil-true-extent final-stil Y)))
    (ly:make-stencil final-expr final-X-ext final-Y-ext))))

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

#(define di-wl 7.18)
#(define di-wr 41.41)
#(define bezier-round 0.551857)
#(define sz 16)

#(define (di-map size z x y)
  (let ((dir-l (ly:directed di-wl size))
        (dir-r (ly:directed di-wr size)))
   (cons (- (* (car dir-l) x) (* (car dir-r) 0.5 z))
         (- (* y size) (* (cdr dir-l) x) (* (cdr dir-r) 0.5 z)))))
         
DiRoofL = #(define-scheme-function (m) (markup?)
 (let* ((h (sqrt 0.75))
        (PP (di-map sz -0.5 0 h))
        (lgs (/ (sqrt (+ (* (car PP) (car PP)) (* (cdr PP) (cdr PP)))) sz))
        (w (ly:angle (car PP) (cdr PP))))
  (make-override-markup `((finalrotation . ,(- di-wl))
                          (extrascaling . (1 . ,lgs))
                          (par . #f))
   (make-slant-markup (- 90 w di-wl) m))))

DiRoofR = #(define-scheme-function (m) (markup?)
 (let* ((h (sqrt 0.75))
        (PP (di-map sz 0 -0.5 h))
        (lgs (/ (sqrt (+ (* (car PP) (car PP)) (* (cdr PP) (cdr PP)))) sz))
        (w (ly:angle (car PP) (cdr PP))))
  (make-override-markup `((finalrotation . ,di-wr)
                          (extrascaling . (0.5 . ,lgs))
                          (par . #f))
   (make-slant-markup (- 90 w (- di-wr)) m))))
   
DiPlaneL = #(define-scheme-function (m) (markup?)
 (make-override-markup `((finalrotation . ,(- di-wl))
                         (par . #f))
  (make-slant-markup (- di-wl) m)))

DiPlaneL¹ = #(define-scheme-function (m) (markup?)
 (make-override-markup `((finalrotation . 90)
                         (par . #f))
  (make-slant-markup di-wl m)))

DiPlaneR = #(define-scheme-function (m) (markup?)
 (make-override-markup `((finalrotation . ,di-wr)
                         (extrascaling . (0.5 . 1))
                         (par . #f))
  (make-slant-markup di-wr m)))
  
DiPlaneR¹ = #(define-scheme-function (m) (markup?)
 (make-override-markup `((finalrotation . 90)
                         (extrascaling . (1 . 0.5))
                         (par . #f))
  (make-slant-markup (- di-wr) m)))
  
DiPlaneT = #(define-scheme-function (m) (markup?)
 (make-override-markup `((finalrotation . ,(- di-wl))
                         (extrascaling . (1 . 0.5))
                         (par . #f))
  (make-slant-markup (- 90.0 di-wr di-wl) m)))

DiPlaneT¹ = #(define-scheme-function (m) (markup?)
 (make-override-markup `((finalrotation . ,di-wr)
                         (extrascaling . (0.5 . 1))
                         (par . #f))
  (make-slant-markup (+ di-wr di-wl -90.0) m)))

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

#(define roof-path `((moveto 0 0)
                     (lineto ,sz 0)
                     (lineto ,(* 0.5 sz) ,sz)
                     (closepath)))
#(define large-bow-path `((moveto 0 0)
                          (lineto ,sz 0)
                          (curveto ,sz ,(* sz bezier-round)
                                   ,(* sz (- 2 bezier-round)) ,sz
                                   ,(* sz 2) ,sz)
                          (curveto ,(* sz (+ 2 bezier-round)) ,sz
                                   ,(* sz 3) ,(* sz bezier-round)
                                   ,(* sz 3) 0)
                          (lineto ,(* sz 4) 0)
                          (lineto ,(* sz 4) ,(* sz 1.5))
                          (lineto 0 ,(* sz 1.5))
                          (closepath)))
#(define small-bow-path `((moveto 0 0)
                          (lineto ,sz 0)
                          (curveto ,sz ,(* sz bezier-round 0.5)
                                   ,(* sz 0.5 (- 3 bezier-round)) ,(* sz 0.5)
                                   ,(* sz 1.5) ,(* sz 0.5))
                          (curveto ,(* sz 0.5 (+ 3 bezier-round)) ,(* sz 0.5)
                                   ,(* sz 2) ,(* sz 0.5 bezier-round)
                                   ,(* sz 2) 0)
                          (lineto ,(* sz 3) 0)
                          (lineto ,(* sz 3) ,sz)
                          (lineto 0 ,sz)
                          (closepath)))

RoofPlane = #(define-scheme-function (r g b) (number? number? number?)
 (make-with-color-markup (list r g b)
  (make-override-markup '(filled . #t)
   (make-path-markup 0 roof-path))))
   
BowPlaneSmall = #(define-scheme-function (r g b) (number? number? number?)
 (make-with-color-markup (list r g b)
  (make-override-markup '(filled . #t)
   (make-path-markup 0 small-bow-path))))
   
BowPlaneLarge = #(define-scheme-function (r g b) (number? number? number?)
 (make-with-color-markup (list r g b)
  (make-override-markup '(filled . #t)
   (make-path-markup 0 large-bow-path))))

RectPlane = #(define-scheme-function (r g b x y) (number? number? number? number? number?)
 (make-with-color-markup (list r g b)
  (make-override-markup '(filled . #t)
   (make-path-markup 0 `((moveto 0 0)
                         (lineto ,(* sz x) 0)
                         (lineto ,(* sz x) ,(* sz y))
                         (lineto 0 ,(* sz y))
                         (closepath))))))
                         

RoofPlaneR = \markup \RoofPlane #1.0 #0.4 #0.4
RoofPlaneL = \markup \RoofPlane #0.9 #0.2 #0.2

BowPlaneLaRaw = \markup \BowPlaneLarge #0.2 #0.7 #0.2
BowPlaneLd = \markup \BowPlaneLarge #0.1 #0.5 #0.1
BowFeederL = \markup \RectPlane     #0.1 #0.5 #0.1 #1 #1
BowSideL   = \markup \RectPlane     #0.5 #0.9 #0.5 #1 #1.5
BowTopLa   = \markup \RectPlane     #0.2 #0.6 #0.2 #4 #1

BowPlaneLbRaw = \markup \BowPlaneSmall #0.7 #0.7 #0.6
BowFeederSm = \markup \RectPlane    #0.5 #0.5 #0.4 #1 #0.8
BowTopSm    = \markup \RectPlane    #0.6 #0.6 #0.5 #3 #1

RoofPlaneRV¹ = \markup \overlay {
  \RoofPlaneR
  \translate #(cons (* 0.5 sz) (* 0.4 sz))
   \scale #(cons (* 0.1 sz) (* 0.1 sz))
    \center-align \musicglyph #"clefs.F"
}

RoofPlaneLB¹ = \markup \overlay {
  \RoofPlaneL
  \translate #(cons (* 0.5 sz) (* 0.3 sz))
   \scale #(cons (* 0.1 sz) (* 0.1 sz))
    \center-align \musicglyph #"clefs.G"
}

RoofPlaneRV² = \markup \overlay {
  \RoofPlaneR
  \translate #(cons (* 0.5 sz) (* 0.1 sz))
   \scale #(cons (* 0.17 sz) (* 0.17 sz))
    \center-align \musicglyph #"pedal.Ped"
}

RoofPlaneLB² = \markup \overlay {
  \RoofPlaneL
  \translate #(cons (* 0.5 sz) (* 0.3 sz))
   \scale #(cons (* 0.1 sz) (* 0.1 sz))
    \center-align \musicglyph #"clefs.C"
}

RoofPlaneRV³ = \markup \overlay {
  \RoofPlaneR
  \translate #(cons (* 0.5 sz) (* 0.35 sz))
   \scale #(cons (* 0.22 sz) (* 0.22 sz))
    \center-align \musicglyph #"scripts.coda"
}

RoofPlaneLB³ = \markup \overlay {
  \RoofPlaneL
  \translate #(cons (* 0.5 sz) (* 0.35 sz))
   \scale #(cons (* 0.15 sz) (* 0.15 sz))
    \center-align \musicglyph #"scripts.segno"
}

LongPlane¹¹ = \markup \overlay {
  \RectPlane #0.1 #0.1 #1.0 #4 #1
  \translate #(cons (* sz 2) (* sz 0.6))
   \scale #(cons (* sz 0.15) (* sz 0.15))
    \bold \column {
      \center-align "deutschsprachiges"
      \center-align "Lilypondforum"
    }
}

LongPlane¹² = \markup \overlay {
  \RectPlane #0.3 #0.6 #1.0 #4 #1
  \translate #(cons (* sz 2) (* sz 0.4))
   \scale #(cons (* sz 0.15) (* sz 0.15))
    \center-align "https://lilypondforum.de"
}

LongPlane²¹ = \markup \overlay {
  \RectPlane #1.0 #0.5 #0.1 #2.5 #1
  \translate #(cons (* sz 1.25) (* sz 0.35))
   \scale #(cons (* sz 0.2) (* sz 0.2))
    \bold \center-align "Lilypond"
}
  
LongPlane²² = \markup \overlay {
  \RectPlane #1.0 #0.7 #0.5 #2.5 #1
  \translate #(cons (* sz 1.25) (* sz 0.4))
   \scale #(cons (* sz 0.12) (* sz 0.15))
    \center-align "www.lilypond.org"
}
  
LongPlane³¹ = \markup \overlay {
  \RectPlane #0.4 #0.2 #0.0 #2.5 #1
  \translate #(cons (* sz 1.25) (* sz 0.55))
   \scale #(cons (* sz 0.15) (* sz 0.15))
    \with-color #yellow
    \italic \override #'(baseline-skip . 2.5) \column {
      \center-align "ein Notenbild,"
      \center-align "mit dem man"
    }
}
  
LongPlane³² = \markup \overlay {
  \RectPlane #0.6 #0.3 #0.1 #2.5 #1
  \translate #(cons (* sz 1.25) (* sz 0.55))
   \scale #(cons (* sz 0.15) (* sz 0.15))
    \with-color #'(1.0 1.0 0.5)
    \italic \override #'(baseline-skip . 2.5) \column {
      \center-align "gerne vom"
      \center-align "Blatt spielt."
    }
}

BowPlaneLa = \markup \overlay {
  \BowPlaneLaRaw
  \translate #(cons (* sz 0.2) (* sz 1.2))
  %  \scale #(cons (* sz 0.15) (* sz 0.15))
    \score {
      { c'4 g' f' e'8 d' b'4. a'16 g' f'2
        e''16( d'') c''-. b'-. \acciaccatura e'8 g' a'16 b'
        \repeat tremolo 4 { c''16 a' }
        b'8 g'4 f'8( ~ f'8 d'8 c'4)_\fermata \bar "|." }
      \layout {
        ragged-right = ##f
        line-width = #(* 6.3 sz)
        indent = 0
        \context {
          \Score
          \override NonMusicalPaperColumn.line-break-permission = ##f
          \override SpacingSpanner.common-shortest-duration = #(ly:make-moment 1/1)
        }
      }
    }
  \translate #(cons (* 0.5 sz) (* 0.5 sz))
   \scale #(cons (* 0.22 sz) (* 0.22 sz))
    \center-align \sharp
  \translate #(cons (* 3.5 sz) (* 0.3 sz))
   \scale #(cons (* 0.22 sz) (* 0.22 sz))
    \center-align \flat
  
}

BowPlaneLb = \markup \overlay {
  \BowPlaneLbRaw
  \translate #(cons (* sz 0.2) (* sz 0.7))
  %  \scale #(cons (* sz 0.15) (* sz 0.15))
    \score {
      { \clef alto
        c'4. f8 g4 b d'\downbow f'2 e'8( d'
        c'8 b a g f) e4.\trill
        c1\fermata \bar "|." }
      \layout {
        ragged-right = ##f
        line-width = #(* 4.7 sz)
        indent = 0
        \context {
          \Score
          \override NonMusicalPaperColumn.line-break-permission = ##f
          \override SpacingSpanner.common-shortest-duration = #(ly:make-moment 1/1)
        }
      }
    }
  \translate #(cons (* 0.5 sz) (* 0.25 sz))
   \scale #(cons (* 0.12 sz) (* 0.12 sz))
    \center-align \natural
  \translate #(cons (* 2.5 sz) (* 0.15 sz))
   \scale #(cons (* 0.12 sz) (* 0.12 sz))
    \center-align \doubleflat
  
}

% Check, no projection
%{ 
\markup {
 \RoofPlaneRV¹ \RoofPlaneLB¹ \RoofPlaneRV² \RoofPlaneLB² \RoofPlaneRV³ \RoofPlaneLB³
}
\markup {
  \BowPlaneLa
  \BowPlaneLb
}

\markup \column {
  \line { \LongPlane¹¹ \LongPlane²¹ }
  \line { \LongPlane¹² \LongPlane²² }
  \line { \LongPlane³¹ \LongPlane³² }
}
%}

% Simple Dimetric 3D image
\markup \left-align \overlay {
  % Pilars
  \translate #(di-map sz  0 1 0) \DiPlaneL¹ \LongPlane¹¹
  \translate #(di-map sz -1 1 0) \DiPlaneR¹ \LongPlane¹²

  \translate #(di-map sz -3 4 0) \DiPlaneL¹ \LongPlane³¹
  \translate #(di-map sz -4 4 0) \DiPlaneR¹ \LongPlane³²

  \translate #(di-map sz  0 4 0) \DiPlaneL¹ \LongPlane²¹
  \translate #(di-map sz -1 4 0) \DiPlaneR¹ \LongPlane²²

  % Bow brick on the right  
  \translate #(di-map sz -3 3 2.5) \DiPlaneL \BowFeederL
  \translate #(di-map sz  0 4 2.5) \DiPlaneR \BowPlaneLa
  \translate #(di-map sz  0 3 2.5) \DiPlaneL \RectPlane #0 #0.4 #0 #1 #1.5
  \translate #(di-map sz 0 3 4) \DiPlaneT \RectPlane #0.2 #0.4 #0.1 #1 #4
  
  % Small bow brick above
  \translate #(di-map sz -3 3 4) \DiPlaneL \BowFeederSm
  \translate #(di-map sz -1 4 4) \DiPlaneR \BowPlaneLb
  \translate #(di-map sz -1 4 5) \DiPlaneT¹ \BowTopSm
  
  % Bow brick to the left
  \translate #(di-map sz -1 0 4) \DiPlaneL \BowPlaneLd
  \translate #(di-map sz  0 1 4) \DiPlaneR \BowFeederL
  \translate #(di-map sz  0 0 4) \DiPlaneL \BowPlaneLa
  \translate #(di-map sz  0 4 4) \DiPlaneR \BowSideL
  \translate #(di-map sz  0 0 5.5) \DiPlaneT \BowTopLa
  
  % Roof
  \translate #(di-map sz  0 0 5.5) \DiRoofL \RoofPlaneLB¹
  \translate #(di-map sz  0 1 5.5) \DiRoofR \RoofPlaneRV¹
  \translate #(di-map sz -3 3 5.0) \DiRoofL \RoofPlaneLB³
  \translate #(di-map sz -3 4 5.0) \DiRoofR \RoofPlaneRV³
  \translate #(di-map sz  0 3 5.5) \DiRoofL \RoofPlaneLB²
  \translate #(di-map sz  0 4 5.5) \DiRoofR \RoofPlaneRV²

  % Coordinate aid
  %{
  \translate #(di-map sz 0 0 0) \left-align ". 0 0 0"
  \translate #(di-map sz 1 0 0) \left-align ". 1 0 0"
  \translate #(di-map sz 2 0 0) \left-align ". 2 0 0"
  \translate #(di-map sz 3 0 0) \left-align ". 3 0 0"
  \translate #(di-map sz 0 1 0) \left-align ". 0 1 0"
  \translate #(di-map sz 0 2 0) \left-align ". 0 2 0"
  \translate #(di-map sz 0 3 0) \left-align ". 0 3 0"
  \translate #(di-map sz 0 4 0) \left-align ". 0 4 0"
  \translate #(di-map sz -4 4 0) \left-align ". -4 4 0"
  \translate #(di-map sz 0 0 1) \left-align ". 0 0 1"
  % \translate #(di-map sz 0 0 1.5) \left-align ". 0 0 1.5"
  % \translate #(di-map sz 0 1 1.5) \left-align ". 0 1 1.5"
  % \translate #(di-map sz -1 1 1.5) \left-align ". -1 1 1.5"
  % \translate #(di-map sz -0.5 0.5 2.5) \left-align ". -0.5 0.5 2.5"
  %}
}