Bezier curve library
Based off the work found here:
- https://pomax.github.io/bezierinfo/
- https://pomax.github.io/bezierjs/
- https://github.com/Pomax/bezierjs
- https://github.com/oysteinmyrmo/bezier
Types
Bezier[N] = object points: array[N + 1, Vec2]
- A bezier curve of order N
DynBezier = object points: seq[Vec2]
- Bezier curve where the order isn't known at compile time
LUT[T] {.byref.} = object table: seq[tuple[t: float, point: Vec2, distanceFrom0: float]] curve: T
- A lookup table of precalculated points within a curve
Procs
proc `[]`(curve: DynBezier; point: Natural): Vec2 {....raises: [], tags: [].}
- Returns a control point within this curve
proc `[]`[N](curve: Bezier[N]; point: range[0 .. N]): Vec2
- Returns a control point within this curve
proc align(curve: Bezier | DynBezier; p1, p2: Vec2): auto
- Rotates this bezier curve so it aligns with the given line
proc approxLen(curve: Bezier | DynBezier; steps: Positive): float
- Calculates the approximate length of a curve. This is a faster algorithm than calling length directly
proc approxLen[T](lut: LUT[T]): float
- Uses a LUT to determine the approximate length of a curve. This is a bit innacurate, but faster than calling length
proc boundingBox(curve: Bezier | DynBezier): tuple[minX, minY, maxX, maxY: float]
- Returns the bounding box for a curve
proc compute(curve: DynBezier; t: float): Vec2 {....raises: [], tags: [].}
- Computes the position of a point along the curve, where t is a value between 0.0 and 1.0.
proc compute[N](curve: Bezier[N]; t: float): Vec2
- Computes the position of a point along the curve, where t is a value between 0.0 and 1.0.
proc derivative(curve: DynBezier): DynBezier {....raises: [], tags: [].}
- Computes the derivative of a bezier curve. The result of this is a new bezier curve with an order of N - 1
proc derivative[N](curve: Bezier[N]): auto
- Computes the derivative of a bezier curve. The result of this is a new bezier curve with an order of N - 1
proc findMaxY(curve: Bezier | DynBezier; x: float): Option[Vec2]
- Finds the maximum y on a curve for a given x.
proc findMinY(curve: Bezier | DynBezier; x: float): Option[Vec2]
- Finds the maximum y on a curve for a given x.
proc length(curve: Bezier | DynBezier): float
- Calculates the length of a curve. This can be expensive, so if you need a faster version consider using approxLen instead.
proc lut[T: Bezier | DynBezier](curve: T; steps: range[2 .. high(int)]): LUT[T]
- Creates a lookup table of indexes into this curve, where steps is the number of points to sample along the curve.
proc newBezier[N](points: varargs[Vec2]): Bezier[N]
- Creates a new Bezier curve where the curve order is known at build time. For example, passing in N = 3 is a cubic curve.
proc newDynBezier(points: varargs[Vec2]): DynBezier {....raises: [], tags: [].}
- Creates a new Bezier curve where the curve order is only known at runtime
proc normal(curve: Bezier | DynBezier; t: float): Vec2
- Returns the tangent vector at a given location, where t is avalue between 0.0 and 1.0
proc order(curve: DynBezier): Natural {....raises: [], tags: [].}
- The order of the curve is the number of points used to define the curve, starting at 0. N = 1 is linear (2 points), N = 2 is quadratic (3 points), N = 3 is cubic (4 points)
proc order[N](curve: Bezier[N]): Natural
- The order of the curve is the number of points used to define the curve, starting at 0. N = 1 is linear (2 points), N = 2 is quadratic (3 points), N = 3 is cubic (4 points)
proc project[T](lut: LUT[T]; point: Vec2): float
- Finds the location on a curve closest to the given point. Returns a value between 0.0 and 1.0 that can be fed into the compute function
proc split(curve: DynBezier; t: float): (DynBezier, DynBezier) {....raises: [], tags: [].}
- Splits the curve at the given location, where t is avalue between 0.0 and 1.0
proc split[N](curve: Bezier[N]; t: float): (Bezier[N], Bezier[N])
- Splits the curve at the given location, where t is avalue between 0.0 and 1.0
proc tangent(curve: Bezier | DynBezier; t: float): Vec2
- Returns the tangent vector at a given location, where t is a value between 0.0 and 1.0
proc tightBoundingBox(curve: Bezier | DynBezier): array[4, Vec2]
- Returns the corners of a bounding box that is tightly aligned to a curve
proc xs(curve: DynBezier): seq[float] {....raises: [], tags: [].}
- Returns all x values from the points in this curve
proc xs[N](curve: Bezier[N]): array[N + 1, float]
- Returns all x values from the points in this curve
Iterators
iterator extrema(curve: DynBezier): float {....raises: [], tags: [].}
- Calculates all the extrema on a curve, expressed as a location between 0.0 and 1.0. You can feed these values into the compute method to get their coordinates
iterator extrema[N](curve: Bezier[N]): float
- Calculates all the extrema on a curve, expressed as a location between 0.0 and 1.0. You can feed these values into the compute method to get their coordinates
iterator findY(curve: Bezier | DynBezier; x: float): Vec2
- Produces the Y values for a given X. This can produce multiple values because a bezier curve may have multiple intersections with the same x value
iterator intersects(curve: Bezier | DynBezier; p1, p2: Vec2): Vec2
- Yields the points where a curve intersects a line
iterator intervals[T](lut: LUT[T]; steps: Positive): Vec2
- Produces points along the curve that are more geometrically evenly spaced. They aren't guaranteed to be exactly evenly spaced, but they will be better than using segment. If you need more accuracy, you can increase the sample size of the LUT. The argument steps is the number of intervals to produce. So this iterator will yield steps + 1 number of points.
iterator pairs(curve: DynBezier | Bezier): (int, Vec2)
- Produces all the points in this curve as well as their index