One further iteration. Please let me know what you think of the interface: -\compoundSlur \with { % offsets against the automatic control points offsets = #'((0 . -1.5) ; left starting point (-2 . -1) ; second control point (2 . -5) ; second-to-last control point (0 . 0)) % right end point

## Advertising

inflection-ratio = #'(0.6 . 0.65) % X/Y ratio of the inflection point % calculated between the actual end points of the slur % defaults to the center % Slope of the line around the inflection point % defaults to 1/1 inflection-slope = 5/3 % length of the "handles" around the inflection % given as a ratio to the left or right baseline % (distance between inflection point and slur's end point). % If not given they default to the length of the respective handle % on the left or right side inflection-ratio-right = 0.5 inflection-ratio-left = 0.25 % Display control points ann = ##t } Of course one could use a list of unnamed arguments or an alist instead of the \with {} clause (I just happen to like them). I have spiced up the "annotation" even more compared to the last version (prints the original slur and its control points in light gray in the background), and I have the impression this approach to defining the inflection point is quite natural, but I'm open to discussion. One thing that is still missing from my personal wish list is a flag to make the center symmetric again. Urs Am 19.09.2016 um 11:00 schrieb Urs Liska: > > > > 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 > > > > _______________________________________________ > lilypond-user mailing list > lilypond-user@gnu.org > https://lists.gnu.org/mailman/listinfo/lilypond-user

%%%%%%%%%%%%%%%%%%%%%%%% \version "2.19.47" \language "deutsch" %%%%%%%%%%%%%%%%%%%%%%% % Display Configuration #(define col-bg (rgb-color .9 .9 .8)) #(define col-orig-slur cyan) #(define col-new-slur red) #(define conn-thickness 0.05) #(define cross-thickness 0.1) #(define cross-size 0.2) %%%%%%%%%%%%%%%%%%%%%%%%%%% % Geometry helper functions #(define (mirror-point pt1 pt2) "Mirror pt2 against pt1" (cons (- (* 2 (car pt1)) (car pt2)) (- (* 2 (cdr pt1)) (cdr pt2)))) #(define (add-points pt1 pt2) "Add two points" (cons (+ (car pt1) (car pt2)) (+ (cdr pt1) (cdr pt2)))) #(define (sloped-point slope dist) "Create a point with given slope and distance" (let ; TODO: Is this too confused, can it be done inline? ((factor (* dist (/ 1 (distance '(0 . 0) slope ))))) (cons (* (cdr slope) factor) (* (car slope) factor)))) #(define (inflection-point pt1 pt2 ratio) "Find a point between two points, giving the X and Y ratio independently" (let* ((xratio (car ratio)) (yratio (cdr ratio))) (cons (+ (* (- 1 xratio) (car pt1)) (* xratio (car pt2))) (+ (* (- 1 yratio) (cdr pt1)) (* yratio (cdr pt2)))))) #(define (distance pt1 pt2) "Caculate distance between two points" (let ((square (lambda (x) (* x x)))) (sqrt (+ (square (- (car pt1) (car pt2))) (square (- (cdr pt1) (cdr pt2))))))) #(define (line-slope pt1 pt2) "Determine the slope of a line connecting two points" (/ (- (cdr pt2) (cdr pt1)) (- (car pt2) (car pt1)))) %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Control point visualization #(define (make-cross-stencil coord col) "Draw a cross-stencil at coord." (stencil-with-color (ly:stencil-add (make-line-stencil cross-thickness (- (car coord) cross-size) (- (cdr coord) cross-size) (+ (car coord) cross-size) (+ (cdr coord) cross-size)) (make-line-stencil cross-thickness (- (car coord) cross-size) (+ (cdr coord) cross-size) (+ (car coord) cross-size) (- (cdr coord) cross-size))) col)) #(define (connect-dots pt1 pt2 col) "Draw a thin line connecting two points" (stencil-with-color (ly:stencil-add (make-line-stencil conn-thickness (car pt1) (cdr pt1) (car pt2) (cdr pt2))) col)) #(define (default-option opts name value) "Retrieve an option value or a default" (let ((prop (assq name opts))) (if prop (cdr prop) value))) 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))) ;; Retrieve options and set defaults (offsets (default-option opts 'offsets '((0 . 0)(0 . 0)(0 . 0)(0 . 0)))) (inflection-ratio (default-option opts 'inflection-ratio '(.5 . .5))) (inflection-slope (default-option opts 'inflection-slope '(1 . 1))) (ann? (default-option opts 'ann #f)) (inflection-ratio-left (default-option opts 'inflection-ratio-left #f)) (inflection-ratio-right (default-option opts 'inflection-ratio-right #f)) ; TODO: (inner-segment (assq-ref opts 'inner-segment)) ;; 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 inflection point and surrounding control points (cp4 (inflection-point cp1 cp7 inflection-ratio)) ;; left hand side length of the inflection ;; (if given it is the ratio to the left "baseline" ;; otherwise it is the same length as the leftmost control point distance) (cp3 (add-points cp4 (sloped-point inflection-slope (if inflection-ratio-left (* -1 inflection-ratio-left (distance cp1 cp4)) (* -1 (distance cp1 cp2)))))) ;; right hand side length of the inflection (cp5 (add-points cp4 (sloped-point inflection-slope (if inflection-ratio-right (* inflection-ratio-right (distance cp4 cp7)) (distance cp7 cp6))))) (first-spline-stil (begin (ly:grob-set-property! grob 'control-points (list cp1 cp2 cp3 cp4)) (ly:slur::print grob))) (second-spline-stil (begin (ly:grob-set-property! grob 'control-points (list cp4 cp5 cp6 cp7)) (ly:slur::print grob))) ;; display original slur and its control points (original-slur (if ann? (apply ly:stencil-add ;; display control points of the original, non-compound slur (append ;; display original slur (list (stencil-with-color (begin (ly:grob-set-property! grob 'control-points cps) (ly:grob-set-property! grob 'layer -1) (ly:slur::print grob)) col-bg)) (map (lambda (c) (make-cross-stencil c col-orig-slur)) cps) )) empty-stencil)) ;; display new control-points and connections (crosses (if ann? (apply ly:stencil-add (append ;; display actual control points of the compound slur (map (lambda (c) (make-cross-stencil c col-new-slur)) (list cp1 cp2 cp3 cp4 cp5 cp6 cp7)) ;; display connections between original and offset control points (map (lambda (c1 c2) (connect-dots c1 c2 col-orig-slur)) cps (list cp1 cp2 cp6 cp7)) ;; display obsolete handles of the original slur (map (lambda (c1 c2) (connect-dots c1 c2 col-bg)) (list (first cps) (fourth cps)) (list (second cps) (third cps))) ;; display handles indicating the (map (lambda (c1 c2) (connect-dots c1 c2 col-new-slur)) (list cp1 cp3 cp4 cp7) (list cp2 cp4 cp5 cp6)) )) empty-stencil)) ) (ly:stencil-add original-slur first-spline-stil second-spline-stil crosses ))))) #{ -\tweak stencil $proc ( #})) upper = \relative { \once \override Score.MetronomeMark.Y-offset = 8 \once \override Score.MetronomeMark.color = #white \tempo "X" \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 . -5) ; second-to-last control point (0 . 0)) % right end point inflection-slope = 5/3 inflection-ratio-right = 0.5 inflection-ratio-left = 0.25 inflection-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**

*Description:* Adobe PDF document

_______________________________________________ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user