(html (head (title "sph-lib (sph spline-path)") (link (@ (rel "stylesheet") (type "text/css") (href "/css/sph.css"))) (meta (@ (name "viewport") (content "width=device-width,initial-scale=1"))) #f) (body () (div (@ (class top)) (nav (a (@ (href "/")) "start") ", " (a (@ (href "/feed.xml")) "feed")) "") (div (@ (class "middle")) (div (@ (class mtime) (title "last modification time of the current page")) "2023-04-05") (section (h1 "(sph spline-path)") (div (p "composable interpolated paths through points.") (p "part of " (a (@ (href "../../sph-lib.html")) "sph-lib")) ((section (@ (class "library-description")) (h1 "library description") (div (p "spline-path-new creates path objects and spline-path gets values from it.") (p "path objects contain a configuration object that can be modified to create a new modified path.") (p "paths can also contain other paths as segments.") (p "# usage") (p "~~~") (p "(define path-all") (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(spline-path-new*")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(move (2 10))")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(line (10 20) (20 50))")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(bezier (30 10) (40 40) (50 10))")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(catmull-rom (60 10) (70 40) (80 10) (90 50))")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(custom-simple (100 0)")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(unquote")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(l (time points)")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(apply (l (start end) (list time (+ (second start) (sin (* time (/ (* 2 pi) 10))))))")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "points))))")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(custom (110 0)")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(unquote")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(l (segments next points)")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(apply")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(l (start end)")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(list")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(vector start end (l (t) (list t (+ (second start) (/ (+ (first start) t) 2)))))))")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "points))))")) (p (((*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp") (*ENTITY* "nbsp")) "(path (unquote (spline-path-new* (line (10 10)))) (unquote (spline-path-new* (line (10 10)))))))")) (p "~~~") (p "spline-path gets points on the path at offset:") (p "~~~") (p "(spline-path 0 path-all)") (p "(spline-path 1 path-all)") (p "(spline-path 55 path-all)") (p "~~~"))) (section (@ (class "library-name")) (h1 "module name") (p "(sph spline-path)")) (section (@ (class "library-exports")) (h1 "exported bindings") (div (div (@ (class "doc-bindings")) (div (@ (id "doc-b-" "sph-spline-path-description") (class "doc-b")) (div (span (@ (class "type")) "variable") ": " (span (@ (class "name")) "sph-spline-path-description")) "" "") (div (@ (id "doc-b-" "spline-path") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path") " " (span (@ (class "first-sig")) "time path ->")) (div (@ (class "rest-sig")) (div "number path -> (time number ...):point")) (div (@ (class "description")) (("get value at time for a path created by spline-path-new." (br) "returns a zero vector for gaps and before or after the path")))) (div (@ (id "doc-b-" "spline-path->procedure") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path->procedure") " " (span (@ (class "first-sig")) "a ->")) (div (@ (class "rest-sig")) (div "spline-path -> {number:t -> (t number ...)}")) "") (div (@ (id "doc-b-" "spline-path->procedure-fast") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path->procedure-fast") " " (span (@ (class "first-sig")) "a ->")) (div (@ (class "rest-sig")) (div "spline-path -> {number:t -> (t number ...)}")) (div (@ (class "description")) (("uses spline-path-fast")))) (div (@ (id "doc-b-" "spline-path-append") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-append") " " (span (@ (class "first-sig")) "a b ... ->")) (div (@ (class "rest-sig")) (div "spline-path ... -> spline-path")) (div (@ (class "description")) (("add one or more spline-paths as a path segment to the end of path a")))) (div (@ (id "doc-b-" "spline-path-combine") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-combine") " " (span (@ (class "first-sig")) "f a b ... ->")) "" (div (@ (class "description")) (("return a new path that is a combination of the given paths." (br) "each point of path is combined by f for each dimension." (br) "example that sums three paths: (spline-path-combine + path1 path2 path3)")))) (div (@ (id "doc-b-" "spline-path-config") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-config") " " (span (@ (class "first-sig")) "a ->")) "" "") (div (@ (id "doc-b-" "spline-path-config->generic-config") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-config->generic-config") " " (span (@ (class "first-sig")) "a ->")) (div (@ (class "rest-sig")) (div "((type arguments ...) ...) -> ((type points other) ...)")) (div (@ (class "description")) (("convert a spline-path-new config to the internally used more generic config where points" (br) "and arguments are at predictable places")))) (div (@ (id "doc-b-" "spline-path-config-distance->t") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-config-distance->t") " " (span (@ (class "first-sig")) "config ->")) (div (@ (class "rest-sig")) (div "generic-config -> generic-config")) (div (@ (class "description")) (("convert the first point values which are point-relative distances to path-relative offsets")))) (div (@ (id "doc-b-" "spline-path-config-t->distance") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-config-t->distance") " " (span (@ (class "first-sig")) "config ->")) (div (@ (class "rest-sig")) (div "generic-config -> generic-config")) (div (@ (class "description")) (("convert the first point values which are path-relative offets to point-relative distances")))) (div (@ (id "doc-b-" "spline-path-constant") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-constant") " " (span (@ (class "first-sig")) "a ... ->")) "" (div (@ (class "description")) (("create a path that always returns the same values. the first time dimension is still updated." (br) "equivalent to (sp-path-new* (constant (0 a ...)))")))) (div (@ (id "doc-b-" "spline-path-constant?") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-constant?") " " (span (@ (class "first-sig")) "a ->")) (div (@ (class "rest-sig")) (div "spline-path -> boolean")) (div (@ (class "description")) (("true if path uses a single constant segment and the path never changes")))) (div (@ (id "doc-b-" "spline-path-end") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-end") " " (span (@ (class "first-sig")) "a ->")) "" "") (div (@ (id "doc-b-" "spline-path-fast") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-fast") " " (span (@ (class "first-sig")) "time path ->")) "" (div (@ (class "description")) (("bypasses all mappers and only works if time is on path." (br) "about 10% faster")))) (div (@ (id "doc-b-" "spline-path-infinite?") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-infinite?") " " (span (@ (class "first-sig")) "a ->")) (div (@ (class "rest-sig")) (div "spline-path -> boolean")) (div (@ (class "description")) (("true if the path has infinite length")))) (div (@ (id "doc-b-" "spline-path-map-config") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-map-config") " " (span (@ (class "first-sig")) "a f ->")) (div (@ (class "rest-sig")) (div "path procedure:{generic-config mapper-config procedure:return -> spline-path} -> spline-path")) (div (@ (class "description")) (("return :: generic-config mapper-config -> spline-path" (br) "create a new path with parameters mapped by f." (br) "example: (spline-path-map-config path (lambda (config mapper c) (c config mapper)))")))) (div (@ (id "doc-b-" "spline-path-modify") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-modify") " " (span (@ (class "first-sig")) "a #:deep #:randomise #:repeat #:reverse #:scale #:shift #:stretch #:mapper-add #:mapper-remove ->")) (div (@ (class "rest-sig")) (div "spline-path [keys ...] -> spline-path")) (div (@ (class "description")) (("return a new path with specified modifications." (br) "# keys" (br) "randomise: boolean/random-state" (br) "repeat: boolean/number (experimental)" (br) "reverse: boolean" (br) "scale: false/number:factor" (br) "shift: false/number:amount" (br) "stretch: false/number:to-end" (br) "deep: with reverse or randomise: apply to paths as segments as well" (br) "mapper-add: ((symbol:id false/procedure:input false/procedure:output custom-info ...) ...)" (br) "mapper-remove: id/(id ...)" (br) "# example" (br) "(spline-path-modify path #:reverse #t #:randomise (random-state-from-platform)" (br) " #:scale 0.2 #:stretch (* 2 (spline-path-end path)))")))) (div (@ (id "doc-b-" "spline-path-new") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-new") " " (span (@ (class "first-sig")) "config [mapper] ->")) (div (@ (class "rest-sig")) (div "((symbol:interpolator-name any:parameter ...) ...) [(list ...)] -> path")) (div (@ (class "description")) (("the returned object is to be passed to spline-path to get points on a path between" (br) "given points interpolated by selected functions. similar to the path element of svg vector graphics." (br) "points in the given segment configuration are relative to the start of the path." (br) "point: (number:dimension ...)" (br) "# dimensions" (br) "* the number of dimensions must be equal between all segments" (br) "* all interpolators support an unlimited number of dimensions except for arc, which is 2d only" (br) "# segments" (br) "* the given segments describe the endpoints as in \\\"line to\\\" or \\\"move to\\\"" (br) "* at least one segment must be given" (br) "# segment types" (br) "## syntax" (br) "* (move point)" (br) "* (line point point ...)" (br) "* (bezier point ...)" (br) "* (catmull-rom point point ...)" (br) "* (catmull-rom-tension tension point point ...)" (br) "* (arc (x y):point radius-x [radius-y rotation large-arc sweep])" (br) "* (custom-simple point:end/(point ...) f custom-arguments ...)" (br) "* (custom point/(point ...) f custom-arguments ...)" (br) "* (path spline-path ...)" (br) "## move" (br) "* move can be given as the first element to start the path at this point" (br) "* move can also be used to create gaps" (br) "## constant" (br) "* describes a flat line to t(infinity)" (br) "* infinitely repeats the values of all dimensions except the first time dimension" (br) "* the optional point argument is a move" (br) "* not useful inbetween segments as the next segment is never reached" (br) "## path" (br) "* segments can be paths" (br) "* this can be used to append multiple paths" (br) "## custom" (br) "* calls a procedure that should return segments" (br) "* additional config arguments are passed on" (br) "* segment: #(point:start point:end procedure:{t -> point})" (br) "* f :: preceeding-segments following-config points custom-argument ... -> (segment ...)" (br) "* f :: (vector ...) (list ...) (point ...) any ... -> (vector ...)" (br) "# mapping" (br) "* an arbitrary number of procedures can be installed that are called either for the input to spline-path or the output" (br) "* this allows for interesting transformations like dynamic time stretches, value scaling or path combination" (br) "* the mapper support allows paths to be sampled using spline-path" (br) "* the mapper argument is a list with mapper config elements" (br) "* mapper config elements are (symbol:key false/procedure:{t -> t}:input false/procedure:{point -> point}:output any:custom-info ...)" (br) "* key is a custom symbol except \\\"repeat\\\", which is used internally." (br) "* input and output mapper procedures are optional" (br) "* the collection of all active mappers will be accessible in the path object with spline-path-mapper-config." (br) "* the active mapper list will be created from the mapper config" (br) "# other" (br) "* the catmull-rom interpolation is always centripetal" (br) "* custom-simple takes a procedure (t points any:custom ... -> point)" (br) "* for \\\"arc\\\" see how arcs are created with svg" (br) "# example" (br) "(spline-path-new* (move (20 0)) (line (20 0.25) (10 0.4)))")))) (div (@ (id "doc-b-" "spline-path-new*") (class "doc-b")) (div (span (@ (class "type")) "syntax") ": " (span (@ (class "name")) "spline-path-new*") " " (span (@ (class "first-sig")) "segment ...")) "" "") (div (@ (id "doc-b-" "spline-path-new-generic") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-new-generic") " " (span (@ (class "first-sig")) "config [mapper] ->")) (div (@ (class "rest-sig")) (div "list [list] -> spline-path")) (div (@ (class "description")) (("new spline path object from generic-config." (br) "spline-path always gives a point with the time value to output mapper that was originally passed to spline-path," (br) "so that input mapper can change it and the output will have the users requested time." (br) "having mappers in a separate config makes internally used mappers easier to replace and subsequently added user mappers manageable")))) (div (@ (id "doc-b-" "spline-path-null") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-null") " " (span (@ (class "first-sig")) "a ->")) "" "") (div (@ (id "doc-b-" "spline-path-start") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path-start") " " (span (@ (class "first-sig")) "a ->")) "" "") (div (@ (id "doc-b-" "spline-path?") (class "doc-b")) (div (span (@ (class "type")) "procedure") ": " (span (@ (class "name")) "spline-path?") " " (span (@ (class "first-sig")) "a ->")) (div (@ (class "rest-sig")) (div "any -> boolean")) "")))))))) ()))