# Re: What to do wanting a 4th order Bézier?

```
Am 19.09.2016 um 00:32 schrieb David Kastrup:
> Urs Liska <u...@openlilylib.org> writes:
>
>> Am 18.09.2016 um 20:54 schrieb David Kastrup:
>>> Do you know how to split a bezier at a given ratio into equivalent
>>> beziers?  It's a comparatively simple operation and I think it's already
>>> somewhere in the C++ code though without access from Scheme.
>> No, but I should be able to figure it out (if noone sends a pointer
>> before I manage to do so).
> Well, METAFONT uses the notation
>
> a[z1, z2]
>
> for z1 + a*[z2-z1], mapping a range of 0..1 for a linearly between z1
> and z2.
>
> If we have points z1, z2, z3, z4 defining a Bezier, then the two split
> beziers are defined with the points
>
> z1, a[z1, z2], a[a[z1, z2], a[z2, z3]], a[a[a[z1, z2], a[z2, z3]],
>                                           a[a[z2, z3], a[z3, z4]]
>
> and
>
> a[a[a[z1, z2], a[z2, z3]], a[a[z2, z3], a[z3, z4]]],
> a[a[z2, z3], a[z3, z4]], a[z3, z4], z4
>
> Basically, calculation of a point a on an n-grade Bezier is done using a
> recursive formula to depth n, and keeping the intermediate results will
> give you the control points for the Bezier curves split at that point.
>```
```
I think before diving into that I share what I currently have, so we may
discuss which approach should actually be continued.

The attached solution does the following:

* Apply offsets for the start/end points and to the second and
second-to-last control-points, based on the original points of the
non-compound slur
* Add an inflection point, which is specified as a point between the
(actual) end points of the slur, given X and Y ratios (as a pair of
numbers between 0 and 1
* Determine the length and slope of the line going through the
inflection point.
o Currently this is done through specifying one point relative to
the inflection point and mirroring it symmetrically
o Instead I'd like to specify an angle and a length.
o It would be nice to have the angle relative to the slope of the
slur as a whole, but that may not be a good idea, as we have
actually two separate lines with different slopes
o Length should be given as a ratio, presumable relative to the
length of the line between the inflection point and the
respective end point.
o There should be one optional argument to enforce symmetrical
points here.

BTW I've spiced up the control points display a bit. I hope it's
self-explanatory.

I would like to integrate this with Janek's \shapeII functions
(https://github.com/openlilylib/snippets/tree/master/notation-snippets/shaping-bezier-curves)
as I think there'll be quite some code (and interface?) that can be shared.

Opinions?

Urs

```
```%%%%%%%%%%%%%%%%%%%%%%%%
\version "2.19.47"
\language "deutsch"
#(define (make-cross-stencil coord col)
"Draw a cross-stencil at coord."
(let ((thick 0.1)
(sz 0.2))
(stencil-with-color
(make-line-stencil
thick
(- (car coord) sz)
(- (cdr coord) sz)
(+ (car coord) sz)
(+ (cdr coord) sz))
(make-line-stencil
thick
(- (car coord) sz)
(+ (cdr coord) sz)
(+ (car coord) sz)
(- (cdr coord) sz)))

col)
))

#(define (connect-dots pt1 pt2 col)
(stencil-with-color
(make-line-stencil
0.05
(car pt1)
(cdr pt1)
(car pt2)
(cdr pt2)))
col))

#(define (mirror-point pt1 pt2)
"Mirror pt2 against pt1"
(cons
(- (* 2 (car pt1)) (car pt2))
(- (* 2 (cdr pt1)) (cdr pt2))))

(cons (+ (car pt1) (car pt2))
(+ (cdr pt1) (cdr pt2))))

#(define (divide pt1 pt2 ratio)
""
(let*
((x1 (car pt1))
(x2 (car pt2))
(y1 (cdr pt1))
(y2 (cdr pt2))
(xratio (car ratio))
(yratio (cdr ratio))
(newcenter    (cons
(+ (* (- 1 xratio) x1) (* xratio x2))
(+ (* (- 1 yratio) y1) (* yratio y2)))))
(display newcenter)
newcenter))

#(define (distance pt1 pt2)
(let
((square (lambda (x) (* x x))))
(sqrt
(+
(square (- (car pt1) (car pt2)))
(square (- (cdr pt1) (cdr pt2)))))))

compoundSlur =
#(define-event-function (options)(ly:context-mod?)
; TODO: Change inner-segment to
; - a ratio (to the length of the line between cp1 and cp4)
; - an angle (relative to the same line cp1--cp2)
(let
((proc
(lambda (grob)
(let*
((opts (map (lambda (o)
(cons (second o) (third o))) (ly:get-context-mods options)))
; TODO: factor out requiring/defaulting options
;; let offsets default to taking the control-points of the automatic slur
(offsets
(let ((prop (assq 'offsets opts)))
(if prop (cdr prop) '((0 . 0)(0 . 0)(0 . 0)(0 . 0)))))
;; let center-ratio default to 0.5/0.5
(center-ratio
(let ((prop (assq 'center-ratio opts)))
(if prop (cdr prop) '(.5 . .5))))
(inner-ratio
(let ((prop (assq 'center-ratio opts)))
(if prop (cdr prop) 0.5)))
(inner-segment (assq-ref opts 'inner-segment))

(ann?
(let ((prop (assq 'ann opts)))
(if prop (cdr prop) #f)))
;; automatic control points of the non-compound slur
(cps (ly:slur::calc-control-points grob))
;; add offsets to the four control points
(cp1 (add-points (first cps) (first offsets)))
(cp2 (add-points (second cps) (second offsets)))
(cp6 (add-points (third cps) (third offsets)))
(cp7 (add-points (fourth cps) (fourth offsets)))

;; calculate central connecting point and surrounding handles
(cp4 (divide cp1 cp7 center-ratio))
(inner-len (distance cp1 cp7))
(slope (/ (- (cdr cp7) (cdr cp1)) (- (car cp7) (car cp1))))

(cp3 (mirror-point cp4 cp5))

(cps1 (list cp1 cp2 cp3 cp4))
(cps2 (list cp4 cp5 cp6 cp7))
(first-spline-stil
(begin
(ly:grob-set-property! grob 'control-points cps1)
(ly:slur::print grob)))
(second-spline-stil
(begin
(ly:grob-set-property! grob 'control-points cps2)
(ly:slur::print grob)))
;; annotates all new control-points
(crosses
(if ann?
(apply
(append
;; display control points of the original, non-compound slur
(map
(lambda (c)
(make-cross-stencil c cyan))
cps)
;; display actual control points of the compound slur
(map
(lambda (c)
(make-cross-stencil c red))
(append cps1 cps2))
;; display connections between original and offset control points
(map
(lambda (c1 c2)
(connect-dots c1 c2 cyan))
cps
(list cp1 cp2 cp6 cp7))
;; display handles indicating the
(map
(lambda (c1 c2)
(connect-dots c1 c2 red))
(list cp1 cp3 cp4 cp7)
(list cp2 cp4 cp5 cp6))
))
empty-stencil))
)

(ly:message "angle: ~a" slope)
first-spline-stil
second-spline-stil
crosses
)))))
#{ -\tweak stencil \$proc ( #}))

upper = \relative {
\key d \major
\clef bass
s2 r8 d,16 g h d g h
d8 r s2.
s4 \voiceTwo h8.(-- c16-- h2--)
}
lower = \relative {
\key d \major
\clef bass
r2
<d' h g=>~-^
% Uncomment one out of the following lines to compare the
% default slur with the compound one
%(
%%{
-\compoundSlur \with {
offsets =       % offsets against the automatic control points
% of the original, non-compound slur
#'((0 . -1.5)   ; left starting point
(-2 . -1)   ; second control point
(-2 . 1)    ; second-to-last control point
(0 . 0))    % right end point

center-ratio =
#'(0.6 . 0.65)  % X/Y ratio of the inflection point
% calculated between the actual end points of the slur

% TODO: change to ratio and angle props (then create defaults)
inner-segment =
#'(4 . 6)       % X/Y of the (so far symmetric) line around the
% inflection point
ann = ##t 	    % Display control points
}
%}
<<
{
<d h g>4
% Uncomment one of the following lines to see the robustness of the slur
<c g e>4
%      <c g e>16 q q q
<h g>4 \voiceOne cis
\change Staff = upper
\clef treble \voiceOne d e fis2 )
\fermata
}
\new Voice {
\voiceTwo
s2. <g,= e>4 \oneVoice
<fis h,> <g e> <fis dis>2\fermata
}
>>
}
\score {
<<
\new PianoStaff <<
\new Staff = upper \upper
\new Staff = lower \lower
>>
>>
}
%%%%%%%%%%%%%%%%%%%%%%%
```

compound-slur-relative.preview.pdf
```_______________________________________________