2023-04-05

(sph spline-path)

composable interpolated paths through points.

part of sph-lib

library description

spline-path-new creates path objects and spline-path gets values from it.

path objects contain a configuration object that can be modified to create a new modified path.

paths can also contain other paths as segments.

# usage

~~~

(define path-all

  (spline-path-new*

    (move (2 10))

    (line (10 20) (20 50))

    (bezier (30 10) (40 40) (50 10))

    (catmull-rom (60 10) (70 40) (80 10) (90 50))

    (custom-simple (100 0)

      (unquote

        (l (time points)

          (apply (l (start end) (list time (+ (second start) (sin (* time (/ (* 2 pi) 10))))))

            points))))

    (custom (110 0)

      (unquote

        (l (segments next points)

          (apply

            (l (start end)

              (list

                (vector start end (l (t) (list t (+ (second start) (/ (+ (first start) t) 2)))))))

            points))))

    (path (unquote (spline-path-new* (line (10 10)))) (unquote (spline-path-new* (line (10 10)))))))

~~~

spline-path gets points on the path at offset:

~~~

(spline-path 0 path-all)

(spline-path 1 path-all)

(spline-path 55 path-all)

~~~

module name

(sph spline-path)

exported bindings

variable: sph-spline-path-description
procedure: spline-path time path ->
number path -> (time number ...):point
get value at time for a path created by spline-path-new.
returns a zero vector for gaps and before or after the path
procedure: spline-path->procedure a ->
spline-path -> {number:t -> (t number ...)}
procedure: spline-path->procedure-fast a ->
spline-path -> {number:t -> (t number ...)}
uses spline-path-fast
procedure: spline-path-append a b ... ->
spline-path ... -> spline-path
add one or more spline-paths as a path segment to the end of path a
procedure: spline-path-combine f a b ... ->
return a new path that is a combination of the given paths.
each point of path is combined by f for each dimension.
example that sums three paths: (spline-path-combine + path1 path2 path3)
procedure: spline-path-config a ->
procedure: spline-path-config->generic-config a ->
((type arguments ...) ...) -> ((type points other) ...)
convert a spline-path-new config to the internally used more generic config where points
and arguments are at predictable places
procedure: spline-path-config-distance->t config ->
generic-config -> generic-config
convert the first point values which are point-relative distances to path-relative offsets
procedure: spline-path-config-t->distance config ->
generic-config -> generic-config
convert the first point values which are path-relative offets to point-relative distances
procedure: spline-path-constant a ... ->
create a path that always returns the same values. the first time dimension is still updated.
equivalent to (sp-path-new* (constant (0 a ...)))
procedure: spline-path-constant? a ->
spline-path -> boolean
true if path uses a single constant segment and the path never changes
procedure: spline-path-end a ->
procedure: spline-path-fast time path ->
bypasses all mappers and only works if time is on path.
about 10% faster
procedure: spline-path-infinite? a ->
spline-path -> boolean
true if the path has infinite length
procedure: spline-path-map-config a f ->
path procedure:{generic-config mapper-config procedure:return -> spline-path} -> spline-path
return :: generic-config mapper-config -> spline-path
create a new path with parameters mapped by f.
example: (spline-path-map-config path (lambda (config mapper c) (c config mapper)))
procedure: spline-path-modify a #:deep #:randomise #:repeat #:reverse #:scale #:shift #:stretch #:mapper-add #:mapper-remove ->
spline-path [keys ...] -> spline-path
return a new path with specified modifications.
# keys
randomise: boolean/random-state
repeat: boolean/number (experimental)
reverse: boolean
scale: false/number:factor
shift: false/number:amount
stretch: false/number:to-end
deep: with reverse or randomise: apply to paths as segments as well
mapper-add: ((symbol:id false/procedure:input false/procedure:output custom-info ...) ...)
mapper-remove: id/(id ...)
# example
(spline-path-modify path #:reverse #t #:randomise (random-state-from-platform)
#:scale 0.2 #:stretch (* 2 (spline-path-end path)))
procedure: spline-path-new config [mapper] ->
((symbol:interpolator-name any:parameter ...) ...) [(list ...)] -> path
the returned object is to be passed to spline-path to get points on a path between
given points interpolated by selected functions. similar to the path element of svg vector graphics.
points in the given segment configuration are relative to the start of the path.
point: (number:dimension ...)
# dimensions
* the number of dimensions must be equal between all segments
* all interpolators support an unlimited number of dimensions except for arc, which is 2d only
# segments
* the given segments describe the endpoints as in \"line to\" or \"move to\"
* at least one segment must be given
# segment types
## syntax
* (move point)
* (line point point ...)
* (bezier point ...)
* (catmull-rom point point ...)
* (catmull-rom-tension tension point point ...)
* (arc (x y):point radius-x [radius-y rotation large-arc sweep])
* (custom-simple point:end/(point ...) f custom-arguments ...)
* (custom point/(point ...) f custom-arguments ...)
* (path spline-path ...)
## move
* move can be given as the first element to start the path at this point
* move can also be used to create gaps
## constant
* describes a flat line to t(infinity)
* infinitely repeats the values of all dimensions except the first time dimension
* the optional point argument is a move
* not useful inbetween segments as the next segment is never reached
## path
* segments can be paths
* this can be used to append multiple paths
## custom
* calls a procedure that should return segments
* additional config arguments are passed on
* segment: #(point:start point:end procedure:{t -> point})
* f :: preceeding-segments following-config points custom-argument ... -> (segment ...)
* f :: (vector ...) (list ...) (point ...) any ... -> (vector ...)
# mapping
* an arbitrary number of procedures can be installed that are called either for the input to spline-path or the output
* this allows for interesting transformations like dynamic time stretches, value scaling or path combination
* the mapper support allows paths to be sampled using spline-path
* the mapper argument is a list with mapper config elements
* mapper config elements are (symbol:key false/procedure:{t -> t}:input false/procedure:{point -> point}:output any:custom-info ...)
* key is a custom symbol except \"repeat\", which is used internally.
* input and output mapper procedures are optional
* the collection of all active mappers will be accessible in the path object with spline-path-mapper-config.
* the active mapper list will be created from the mapper config
# other
* the catmull-rom interpolation is always centripetal
* custom-simple takes a procedure (t points any:custom ... -> point)
* for \"arc\" see how arcs are created with svg
# example
(spline-path-new* (move (20 0)) (line (20 0.25) (10 0.4)))
syntax: spline-path-new* segment ...
procedure: spline-path-new-generic config [mapper] ->
list [list] -> spline-path
new spline path object from generic-config.
spline-path always gives a point with the time value to output mapper that was originally passed to spline-path,
so that input mapper can change it and the output will have the users requested time.
having mappers in a separate config makes internally used mappers easier to replace and subsequently added user mappers manageable
procedure: spline-path-null a ->
procedure: spline-path-start a ->
procedure: spline-path? a ->
any -> boolean