Skip to content

Instantly share code, notes, and snippets.

@ingoogni
Last active March 11, 2025 12:07
Show Gist options
  • Save ingoogni/490d2d50710bab4fe5e689714e9ef287 to your computer and use it in GitHub Desktop.
Save ingoogni/490d2d50710bab4fe5e689714e9ef287 to your computer and use it in GitHub Desktop.
Spline and curve interpolation for many variants. Based upon the works of Steve H. Noskowicz. Parallel excecution using Malebolgia.
import sequtils, math
## Spline and curve interpolation for many variants.
## Based upon the works of Steve H. Noskowicz.
## http://web.archive.org/web/20151002232205/http://home.comcast.net/~k9dci/site/?/page/Piecewise_Polynomial_Interpolation/
## Get the book PDF and the appendix.
type
CurveSegmentError* = object of Defect
CurveOrderError* = object of Defect
CurveParameterError* = object of Defect
Curve*[T] = object
segmentDim*: int # number of control points per segment
stepSize*: int # number of control points to get to the next
# segment.
segments*: int # number of segments in curve, number of control
# points in a multi segment curve is
# `segmentDim + (n * stepSize)`
matrix*: seq[seq[float]] # base matrix
degree*: int # degree of the curve
cp*: seq[T] # sequence of control points
curmult*: seq[seq[T]] # control points multiplied by weights
origin*: T # ... to get the type from ...
ICurve*[T] = object
stepSize*: int
segmentDim*: int
matrix*: seq[seq[float]]
degree*: int
cp*: iterator (): T
origin*: T
#if three dimensions (vmath) is not enough
func `+`[T:float](a, b: openArray[T]): seq[T] =
result = newSeq[T](a.len)
for i,j in a:
result[i] = j + b[i]
func `*`[T:float](a, b: seq[T]):seq[T]=
result = newSeq[T](a.len)
for i,j in a:
result[i] = j * b[i]
func nSegments(length: int, stepSize: int, segmentDim: int): int {.inline.} =
let lsd = length - segmentDim
if lsd < 0:
raise newException(CurveSegmentError, "Too few points in curve array")
if (lsd mod stepSize) == 0:
result = 1 + (lsd div stepSize)
else:
raise newException(CurveSegmentError, "Incorrect number of points in curve array")
func precalcCurmult(curve: var Curve) =
curve.curmult = newSeq[seq[curve.origin.type]](curve.segments)
for segment in 0 ..< curve.segments:
let current_pos = segment * curve.stepSize
curve.curmult[segment] = newSeq[curve.origin.type](len(curve.matrix))
# Calculate each coefficient
for i in 0 ..< len(curve.matrix):
curve.curmult[segment][i] = default(curve.origin.type)
# Accumulate the weighted sum
for j in 0 ..< len(curve.matrix[i]):
let jSegment = j + current_pos
curve.curmult[segment][i] += curve.cp[jSegment] * curve.matrix[i][j]
func divideMatrix(matrix: seq[seq[float]], divisor: float): seq[seq[float]] {.inline.} =
matrix.mapIt(it.mapIt(it / divisor))
func initCurve*[T](
cp: seq[T] or iterator (): T, # control points
segmentDim: int,
stepSize: int,
degree: int,
matrix: seq[seq[float]],
): Curve[T] | ICurve[T] =
when cp is iterator (): T:
var curve = ICurve[T]()
else:
var curve = Curve[T]()
curve.cp = cp
curve.segmentDim = segmentDim
curve.stepSize = stepSize
curve.degree = degree
curve.matrix = matrix
when curve is Curve[T]:
curve.segments = nSegments(len(curve.cp), curve.stepSize, curve.segmentDim)
precalcCurmult(curve)
return curve
func linear*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Linear "curve".
## Two control points define a segment.
## The curve passes through all control points.
## Number of control points in a multi segment curve is `2 + (n * 1)`
initCurve(
cp = cp,
segmentDim = 2,
stepSize = 1,
degree = 1,
matrix = @[
@[-1.0, 1.0],
@[1.0]
]
)
func simple3*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Simple Cubic curve. Linear segments
## Two control points define a segment.
## The curve passes through all control points.
## Number of control points in a multi segment curve is `2 + (n * 1)`
initCurve(
cp = cp,
segmentDim = 2,
stepSize = 1,
degree = 3,
matrix = @[
@[2.0, -2.0],
@[-3.0, 3.0],
@[0.0, 0.0],
@[1.0]
]
)
func simple3ESC*[T](cp: seq[T] or iterator (): T, esc: float): Curve[T] | ICurve[T] =
## Simple Cubic curve with Endpoint Slope Control. Linear segments
## Two control points define a segment.
## The curve passes through all control points.
## Number of control points in a multi segment curve is `2 + (n * 1)`
## `esc`: controls the velocity, spacing of points. It goes to the value of
## `esc` at the control points.
initCurve(
cp = cp,
segmentDim = 2,
stepSize = 1,
degree = 3,
matrix = @[
@[2.0 - (2.0 * esc), (2.0 * esc) - 2.0],
@[(3.0 * esc) - 3.0, 3.0 - (3.0 * esc)],
@[-esc, esc],
@[1.0],
],
)
func simple3MSC*[T](cp: seq[T] or iterator (): T, msc: float): Curve[T] | ICurve[T] =
## Simple Cubic curve with Midpoint Slope Control
## Two control points define a segment.
## The curve passes through all control points.
## Number of control points in a multi segment curve is `2 + (n * 1)`
## `msc`: controls the velocity, spacing of points. It goes towards the value
## of `msc` at the mid point between the control points.
initCurve(
cp = cp,
segmentDim = 2,
stepSize = 1,
degree = 3,
matrix = @[
@[(4.0 * msc) - 4.0, 4.0 - (4.0 * msc)],
@[6.0 - (6.0 * msc), (6.0 * msc) - 6.0],
@[(2.0 * msc) - 3.0, 3.0 - (2.0 * msc)],
@[1.0],
],
)
func simple3IESC*[T](cp: seq[T] or iterator (): T, esc0: float, esc1: float): Curve[T] | ICurve[T] =
## Simple Cubic with Independent Endpoint Slope Control
## Two control points define a segment.
## The curve passes through all control points.
## Number of control points in a multi segment curve is `2 + (n * 1)`
## `esc0`, `esc1`: control the velocity, spacing of points. It goes to the
## value of `esc` at the control point.
initCurve(
cp = cp,
segmentDim = 2,
stepSize = 1,
degree = 3,
matrix = @[
@[2.0 - esc0 - esc1, esc0 + esc1 - 2.0],
@[(2.0 * esc0) + esc1 - 3.0, 3.0 - (2.0 * esc0) - esc1],
@[-esc0, esc0],
@[1.0],
],
)
func bezier2*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Quadratic Bezier curve
## Three control points define a segment.
## The curve passes through the first and last control point of a segment.
## The curve is "attracted" to the middel control point.
## Number of control points in a multi segment curve is `3 + (n * 2)`
initCurve(
cp = cp,
segmentDim = 3,
stepSize = 2,
degree = 2,
matrix = @[
@[1.0, -2.0, 1.0],
@[-2.0, 2.0],
@[1.0]
]
)
func bezier2EVC*[T](cp: seq[T] or iterator (): T, evc: float): Curve[T] | ICurve[T] =
## Quadratic Bezier curve with Endpoint Velocity Control
## Three control points define a segment.
## The curve passes through the first and last control point of a segment.
## The curve is "attracted" to the middel control point.
## Number of control points in a multi segment curve is `3 + (n * 2)`
## `evc`: Changes the length of the end tangent vectors. This pushes the
## centre of the curve towards the middle control point. At `evc = 0` it
## is a quadratic Bezier curve. At `evc = -2` it is a simple cubic curve
## and at `evc = 2` the curve goes through the middle control point.
initCurve(
cp = cp,
segmentDim = 3,
stepSize = 2,
degree = 2,
matrix = @[
@[-evc, 0.0, evc],
@[1.0 + (2.0 * evc), -2.0 - evc, 1.0 - evc],
@[-2.0 - evc, 2.0 + evc],
@[1.0],
]
)
func bezier3*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Cubic Bezier curve
## Four control points define a segment.
## The curve passes through the first and last control point of a segment.
## The curve is "attracted" to the middel control points.
## Number of control points in a multi segment curve is `4 + (n * 3)`
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 3,
degree = 3,
matrix = @[
@[-1.0, 3.0, -3.0, 1.0],
@[3.0, -6.0, 3.0],
@[-3.0, 3.0],
@[1.0]
]
)
func bezier3EVC*[T](cp: seq[T] or iterator (): T, evc: float): Curve[T] | ICurve[T] =
## Cubic Bezier Curve with Endpoint Velocity Control
## Four control points define a segment.
## The curve passes through the first and last control point of a segment.
## The curve is "attracted" to the middel control points.
## Number of control points in a multi segment curve is `4 + (n * 3)`
## `evc`: Changes the length of the end tangent vectors. This pushes
## the curve towards the middle control point.
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 3,
degree = 3,
matrix = @[
@[-1.0 - evc, 3.0 + evc, -3.0 - evc, 1.0 + evc],
@[3.0 + (2 * evc), -6.0 - (2 * evc), 3.0 + evc, -evc],
@[-3.0 - evc, 3.0 + evc],
@[1.0]
]
)
func bspline2*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Quadratic b-spline aka parabolic spline
## Three control points define a segment.
## The curve does not pass through any control point. It starts half way P0P1
## and ends half way P1P2.
## The curve is "attracted" to the middel control points.
## Number of control points in a multi segment curve is `3 + (n * 1)`
initCurve(
cp = cp,
segmentDim = 3,
stepSize = 1,
degree = 2,
matrix = divideMatrix(
@[
@[1.0, -2.0, 1.0],
@[-2.0, 2.0],
@[1.0, 1.0]
],
2.0
)
)
func bspline3*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Cubic b-spline
## Four control points define a segment.
## The curve does not pass through any control point. It goes from near
## P1 to near P2
## The curve is "attracted" to the middel control points.
## Number of control points in a multi segment curve is `4 + (n * 1)`
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 1,
degree = 3,
matrix = divideMatrix(
@[
@[-1.0, 3.0, -3.0, 1.0],
@[3.0, -6.0, 3.0],
@[-3.0, 0.0, 3.0],
@[1.0, 4.0, 1.0]
],
6.0
),
)
func cardinal*[T](cp: seq[T] or iterator (): T, tension: float): Curve[T] | ICurve[T] =
## Cardinal spline
## Four control points define a segment.
## Number of control points in a multi segment curve is `4 + (n * 1)`
let t = (1.0 - tension) / 2.0
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 1,
degree = 3,
matrix = @[
@[-t, 2.0 - t, t - 2.0, t],
@[2.0 * t, t - 3.0, 3.0 - (2.0 * t), -t],
@[-t, 0.0, t, 0.0],
@[0.0, 1.0, 0.0, 0.0],
],
)
func golden_curve*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Golden curve, quadratic three point
## Three control points define a segment.
## The curve passes through all control point.
## At `t = 0.5` it passes through the middle control point.
## Number of control points in a multi segment curve is `3 + (n * 2)`
initCurve(
cp = cp,
segmentDim = 3,
stepSize = 2,
degree = 2,
matrix = @[
@[2.0, -4.0, 2.0],
@[-3.0, 4.0, -1.0],
@[1.0]
]
)
func four_point3_curve*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Cubic Four Point curve
## (identical to a quadratic Lagrange with `t1 = 1/3` and `t2 = 2/3`)
## Four control points define a segment.
## The curve passes through all control point.
## Number of control points in a multi segment curve is `4 + (n * 3)`
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 3,
degree = 3,
matrix = divideMatrix(
@[
@[-9.0, 27.0, -27.0, 9.0],
@[18.0, -45.0, 36.0, -9.0],
@[-11.0, 18.0, -9.0, 2.0],
@[2.0],
],
2.0,
),
)
func hermiteC*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Hermite curve in conventional form
## Four control points define a segment.
## The curve passes through the first and second control point. The
## third and fourth control points are tangent vectors.
## Number of control points in a multi segment curve is `4 + (n * 3)`
## Not realy suitable for piece wise interpolation.
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 3,
degree = 3,
matrix = @[
@[2.0, -2.0, 1.0, 1.0],
@[-3.0, 3.0, -2.0, -1.0],
@[0.0, 0.0, 1.0],
@[1.0]
],
)
func hermite*[T](cp: seq[T] or iterator (): T, tension: float, bias: float): Curve[T] | ICurve[T] =
## Hermite curve (in Bezier form)
## Four control points define a segment.
## The curve passes through the first and last control point. The two
## middle control points are tangent vectors.
## Number of control points in a multi segment curve is `4 + (n * 1)`
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 1,
degree = 3,
matrix = @[
@[2.0, -3.0, 0.0, 1.0],
@[-2.0, 3.0, 0.0, 0.0],
@[tension + bias, tension - bias, -tension - (2.0 * bias), bias - tension],
@[bias - tension, tension - (2.0 * bias), bias, 0.0],
],
)
func catmullrom*[T](cp: seq[T] or iterator (): T): Curve[T] | ICurve[T] =
## Catmull Rom spline
## Four control points define a segment.
## The curve passes through the middle two control point.
## Number of control points in a multi segment curve is `4 + (n * 1)`
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 1,
degree = 3,
matrix = divideMatrix(
@[
@[-1.0, 3.0, -3.0, 1.0],
@[2.0, -5.0, 4.0, -1.0],
@[-1.0, 0.0, 1.0],
@[0.0, 2.0]
],
2.0,
),
)
func lagrange2*[T](cp: seq[T] or iterator (): T, t2: float): Curve[T] | ICurve[T] =
## Quadratic Lagrange curve
## Tree control points define a segment.
## The curve passes through all three control points.
## Number of control points in a multi segment curve is `3 + (n * 2)`
## `t2`: defines at what value for t the curve goes through P2
let
n1 = 1.0 / t2
n2 = 1.0 / (t2 * (t2 - 1.0))
n3 = 1.0 / (1.0 - t2)
initCurve(
cp = cp,
segmentDim = 3,
stepSize = 2,
degree = 2,
matrix = @[
@[n1, n2, n3],
@[-n1 - 1.0, -n2, -n3 * t2],
@[1.0]
]
)
# lagrange3 does not seem proper
#func lagrange3*[T](cp: seq[T] or iterator (): T, t1: float, t2: float): Curve[T] | ICurve[T] =
# ## Cubic Lagrange curve
# ## Four control points define a segement.
# ## Number of control points in a multi segment curve is `4 + (n * 3)`
# ## The value of `t1` determines at what value of t the curve passes through `P1`.
# ## The value of `t2` determines at what value of t the curve passes through `P2`.
# ## When `t1 = 1/3` and `t2 = 2/3`, this is the cubic four point.
#
# if t1 <= 0.0 or t2 >= 1.0 or t1 > t2:
# raise newException(CurveParameterError, "Not fulfilled: 0 < t1 < t2 < 1")
#
# let
# n0 = 1.0 / (t1 * t2)
# n1 = 1.0 / (t1 * (t1 - t2) * (t1 - 1))
# n2 = 1.0 / (t2 * (t2 - t1) * (t2 - 1))
# n3 = 1.0 / ((1 - t1) * (1 - t2))
#
# initCurve(
# cp = cp,
# segmentDim = 4,
# stepSize = 3,
# degree = 3,
# matrix = @[
# @[-n0, n1, n2, n3],
# @[n0 * (t1 + t2 + 1.0), -n1 * (t2 + 1.0), -n2 * (t1 + 1.0), -n3 * (t1 + t2)],
# @[-n0 * (t1 + t2 + t1 * t2), n1 * t2, n2 * t1, n3 * t1 * t2 ],
# @[ 1.0, 0.0, 0.0, 0.0]
# ]
# )
func beta_spline*[T](cp: seq[T] or iterator (): T, tension: float, bias: float): Curve[T] | ICurve[T] =
## Beta spline, Cubic B-spline with Tension, Bias
## Four control points define a segment.
## The curve passes through none of the control points. It goes from near P1 to near P2.
## Number of control points in a multi segment curve is `4 + (n * 1)`
## `T`: from 0 to infinity, attracts to control point P1. From 0 to -6 pushes away from P1.
## `B`: from 0 to 1, pushes towards P2, from 1 to infinity pushes towards P1
## `B = 1` & `T = 0` --> `B-spline`
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 1,
degree = 3,
matrix = divideMatrix(
@[
@[
-2.0 * pow(bias, 3.0),
2.0 * (tension + pow(bias, 3.0) + pow(bias, 2.0) + bias),
-2.0 * (tension + pow(bias, 2.0) + bias + 1.0),
2.0,
],
@[
6.0 * pow(bias, 3.0),
-3.0 * (tension + (2.0 * pow(bias, 3.0) + (2.0 * pow(bias, 2.0)))),
3.0 * (tension + (2.0 * pow(bias, 2.0))),
],
@[-6.0 * pow(bias, 3.0), 6.0 * (pow(bias, 3.0) - bias), 6.0 * bias],
@[2.0 * pow(bias, 3.0), tension + (4.0 * (pow(bias, 2.0) + bias)), 2.0],
],
(tension + (2.0 * pow(bias, 3.0)) + (4.0 * pow(bias, 2.0)) + (4.0 * bias) + 2.0),
),
)
func tcb_spline*[T](
cp: seq[T] or iterator (): T, tension: float, cont: float, bias: float
): Curve[T] | ICurve[T] =
## Kochanek Bartels aka TCB-spline. Tension, Continuity, Bias.
## Four control points define a segment.
## The curve passes trough P1 and P2.
## Number of control points in a multi segment curve is 4 + (n * 1)
## Tension `T = +1` --> Tight, `T = -1` --> Round
## Bias `B = 1 --> Post Shoot`, `B = -1 --> Pre shoot`
## Continuity `C = +1 --> Inverted corners`, `C = -1 --> Box corners`
## `T = B = C --> Catmull Rom`
## `T = 1 --> simple cubic`
## `T = B = 0` & `C = -1 linear interpolation`
let
a = (1.0 - tension) * (1.0 + cont) * (1.0 + bias)
b = (1.0 - tension) * (1.0 - cont) * (1.0 - bias)
c = (1.0 - tension) * (1.0 - cont) * (1.0 + bias)
d = (1.0 - tension) * (1.0 + cont) * (1.0 - bias)
initCurve(
cp = cp,
segmentDim = 4,
stepSize = 1,
degree = 3,
matrix = divideMatrix(
@[
@[-a, 4.0 + a - b - c, -4.0 + b + c - d, d],
@[2.0 * a, -6.0 - (2.0 * a) + (2.0 * b) + c, 6.0 - (2.0 * b) - c + d, -d],
@[-a, a - b, b],
@[0.0, 2.0],
],
2.0,
),
)
func palmer*[T](cp: seq[T] or iterator (): T, tension: float, bias: float): Curve[T] | ICurve[T] =
## Palmer with Tension, Bias
## Three control points define a segment.
## The curve passes trough P1 and P3 and passes by P2.
## Number of control points in a multi segment curve is `3 + (n * 2)`
let
n0 = 1.0 / bias
n1 = 1.0 / (bias * (1.0 - bias))
n2 = 1.0 / (1.0 - bias)
initCurve(
cp = cp,
segmentDim = 3,
stepSize = 2,
degree = 3,
matrix = @[
@[-2.0 * tension, 0.0, 2.0 * tension],
@[n0 + (3.0 * tension), -n1, n2 - (3.0 * tension)],
@[-1.0 - n0 - tension, n1, tension - (n2 / n0)],
@[1.0],
],
)
#[
t goes from 0 - 1. From t figure out in what segment of the curve we are.
from the segment number and t figure out what the equivalent segment time tseg is.
segments are interpolated from 0-1, using tseg.
finaly run Horner
]#
func getPoint*[T](curve: Curve[T], t: float): T {.inline.}=
## Gets a single point from the curve, with t ∈ [0,1].
var
segment = default(int)
tseg = default(float)
if t < 1.0:
segment = int(floor(t * float(curve.segments)))
tseg = (t * float(curve.segments)) - float(segment)
else:
segment = curve.segments - 1
tseg = 1.0
var F = curve.curmult[segment][0]
for i in 1 ..< len(curve.curmult[segment]):
F = (F * tseg) + curve.curmult[segment][i]
return F
func getCurve*[T](curve: Curve[T], resolution: int): seq[T] =
## Gets all points from the curve, with t ∈ [0, 1].
result = newSeq[T](resolution)
for i in 0 ..< resolution:
let t = i / (resolution - 1)
result[i] = getPoint(curve, t)
func iterCurve*[T](curve: Curve[T], resolution: int): iterator (): T =
return iterator (): T =
for i in 0 ..< resolution:
let t = i / (resolution - 1)
yield getPoint(curve, t)
# A more unified approach to forward differencing
func setupForwardDifferences[T](curmult: var seq[T], delta: seq[float], degree: int): seq[T] =
result = newSeq[T](degree + 1)
# First element is the starting point
result[0] = pop(curmult)
# Calculate the forward differences based on the curve degree
for d in 1..degree:
var diff = default(T)
# Calculate coefficients for each degree
for i in 0..<(len(curmult) - (d - 1)):
# Determine the multiplier based on the position and degree
var multiplier = 1.0
if d == 1:
# First derivative - use delta[i]
multiplier = delta[degree - 1]
elif d == 2:
# Second derivative - different coefficients based on position
multiplier = if i == 0: delta[degree - 2] * 6.0
elif i == 1: delta[degree - 2] * 2.0
else: delta[degree - 2]
elif d == 3:
# Third derivative - constant acceleration change
multiplier = delta[degree - 3] * 6.0
diff += curmult[i] * multiplier
result[d] = diff
proc iterCurve*[T](curve: ICurve[T], steps: int): iterator (): T =
let fsteps = steps.float
# Create delta values based on steps
let delta = @[1 / (fsteps * fsteps * fsteps), 1 / (fsteps * fsteps), 1 / fsteps]
# Set up seq for segment, call curve.cp() iterator segmentDim times
var segment = newSeqWith(curve.segmentDim, curve.cp())
return iterator (): T =
while true:
# Multiply the matrix coefficients by the control points
var curmult = newSeq[curve.origin](len(curve.matrix))
for i in 0 ..< len(curve.matrix):
for j in 0 ..< len(curve.matrix[i]):
curmult[i] = curmult[i] + (segment[j] * curve.matrix[i][j])
# Set up vectors for forward differences using the consolidated function
var iterCurves = setupForwardDifferences(curmult, delta, curve.degree)
# Step interpolate through one segment
var i = 0
while i < steps:
yield iterCurves[0]
# Update using forward differences
for d in 0..<(curve.degree):
iterCurves[d] += iterCurves[d + 1]
inc(i)
# Get the points for the next segment
segment.delete(0..(curve.stepsize - 1))
while segment.len != curve.segmentDim:
if finished(curve.cp): break
segment.add(curve.cp())
# For the last segment, also yield its last point
if finished(curve.cp):
yield iterCurves[0]
break
when is_main_module:
import vmath
import pixie
func vals(data: seq): auto =
return iterator (): auto =
for i in data:
yield i
let
image = newImage(1000, 1000)
ctx = newContext(image)
v0: seq[Vec2] = @[
vec2(150, 900),
vec2(150, 150),
vec2(500, 150),
vec2(500, 500),
vec2(500, 850),
vec2(850, 850),
vec2(850, 150)
]
resolution = 100
steps = 50
# control points
image.fill(rgba(255, 255, 255, 255))
ctx.fillStyle = rgba(255, 0, 0, 255)
for i in v0:
ctx.fillCircle(circle(i, 8))
# loop over seq of control points, get point by point
ctx.fillStyle = rgba(0, 255, 0, 125)
for i in 0 ..< resolution:
let t = i / (resolution - 1)
ctx.fillCircle(
circle(
getPoint[Vec2](bezier3[Vec2](v0), t),
9
)
)
# loop over all points of curve
ctx.strokeStyle = rgba(125, 0, 125, 125)
ctx.lineWidth = 3
let ps = getCurve[Vec2](bezier3[Vec2](v0), resolution)
for p in ps:
ctx.strokeCircle(
circle(p, 12)
)
# iterate using seq of control points as input to curve
ctx.fillStyle = rgba(255, 0, 0, 125)
let val = iterCurve[Vec2](bezier3[Vec2](v0), resolution)
for p in val():
ctx.fillCircle(
circle(p, 6)
)
# iterate using seq of control points as input to curve
ctx.fillStyle = rgba(255,255, 255, 125)
let ip = iterCurve[Vec2](bezier3[Vec2](v0), resolution)
while true:
let val = ip()
if finished(ip):
break
ctx.fillCircle(
circle(val, 3)
)
# iterate using iterator of control points as input to curve
let pp = iterCurve[Vec2](bezier3[Vec2](vals(v0)), steps)
ctx.fillStyle = rgba(255, 0, 0, 255)
while true:
if finished(pp):
break
ctx.fillCircle(
circle(pp(), 2)
)
image.writeFile("curve_img.png")
#nim c -d:ThreadPoolSize=8 -d:useMalloc -d:FixedChanSize=16 parcurve.nim
import std/[monotimes]
import curve
import pixie
import malebolgia
let
image = newImage(1000, 1000)
ctx = newContext(image)
v0 = @[
vec2(100, 500),
vec2(100, 100),
vec2(500, 100),
vec2(500, 500),
vec2(500, 900),
vec2(900, 900),
vec2(900, 500),
]
resolution = 100000
let tlin = bezier3(v0)
var m = createMaster()
var p = newSeq[Vec2](resolution)
func pcurve(curve:Curve, resolution: int, threadId: int, p: ptr): auto=
let numPer = resolution div ThreadPoolSize
let frm = numPer * threadId
let to = if threadId == ThreadPoolSize:
resolution - 1
else:
numPer * (threadId + 1)
for idx in frm ..< to:
let t = idx / (resolution - 1)
p[][idx] = getPoint(curve, t)
let t0 = getMonoTime()
m.awaitAll:
for i in 0 ..< ThreadPoolSize:
m.spawn pcurve(tlin, resolution, i, addr p)
let t1 = getMonoTime()
echo t1 - t0
image.fill(rgba(255, 255, 255, 255))
ctx.fillStyle = rgba(0, 0, 0, 255)
for i in p:
ctx.fillCircle(circle(i, 0.5))
image.writeFile("pcurve_img.png")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment