Skip to content

Commit

Permalink
[orx-shapes] Add generated and verified documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
edwinRNDR committed Jan 18, 2025
1 parent 1752685 commit 2236da0
Show file tree
Hide file tree
Showing 23 changed files with 335 additions and 25 deletions.
9 changes: 9 additions & 0 deletions orx-shapes/src/commonMain/kotlin/alphashape/AlphaShape.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ fun List<Vector2>.alphaShape(): Shape {
return AlphaShape(this).createShape()
}

/**
* Calculates the circumradius of a triangle formed by three 2D points.
* The circumradius is the radius of the circumcircle passing through all three points of the triangle.
*
* @param p1 The first vertex of the triangle.
* @param p2 The second vertex of the triangle.
* @param p3 The third vertex of the triangle.
* @return The circumradius of the triangle as a Double.
*/
private fun circumradius(p1: Vector2, p2: Vector2, p3: Vector2): Double {
val a = (p2 - p1).length
val b = (p3 - p2).length
Expand Down
11 changes: 11 additions & 0 deletions orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ import org.openrndr.shape.Segment2D
import org.openrndr.shape.ShapeContour
import kotlin.random.Random

/**
* Represents a base class for a Bezier patch, a surface defined by control points with optional color assignments.
*
* The Bezier patch is constructed from a 4x4 grid of control points and optionally a 4x4 grid of color values.
*
* @param C The type of the color, which must implement the interfaces [AlgebraicColor] and [ConvertibleToColorRGBa].
* @property points A 4x4 grid of control points representing the Bezier patch.
* @property colors An optional 4x4 grid of colors associated with the corresponding control points.
*
* @throws IllegalArgumentException if the `points` matrix is not 4x4, or if `colors` is not empty and not 4x4.
*/
open class BezierPatchBase<C>(
val points: List<List<Vector2>>,
val colors: List<List<C>> = emptyList()
Expand Down
16 changes: 16 additions & 0 deletions orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch3D.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ import org.openrndr.shape.Path3D
import org.openrndr.shape.Segment3D
import kotlin.random.Random

/**
* Represents a 3D Bezier patch defined by a 4x4 grid of control points and optional color data.
* This class provides utilities for manipulation, transformation, and evaluation of the patch.
*
* The control points and colors must be organized as a 4x4 grid. The patch supports operations
* including transformation, sub-patching, path extraction, and random point generation.
*
* @param C The type of color data associated with the patch. It must implement both
* `AlgebraicColor` and `ConvertibleToColorRGBa`.
* @property points A 4x4 grid of control points that define the shape of the Bezier patch.
* @property colors A 4x4 grid of color data corresponding to the control points. This parameter
* is optional and defaults to an empty list.
*
* @throws IllegalArgumentException if `points` or `colors`, if provided, do not conform
* to the required 4x4 structure.
*/
open class BezierPatch3DBase<C>(
val points: List<List<Vector3>>,
val colors: List<List<C>> = emptyList()
Expand Down
20 changes: 17 additions & 3 deletions orx-shapes/src/commonMain/kotlin/blend/ContourBlend.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import org.openrndr.extra.shapes.rectify.rectified
import org.openrndr.shape.ShapeContour

/**
* ContourBlend holds two rectified contours with an equal amount of segments
* A utility class for blending between two rectified contours.
*
* The `ContourBlend` class facilitates blending operations between two
* `RectifiedContour` instances, assuming that they have a compatible structure
* with an equal number of segments.
*
* @constructor Creates a `ContourBlend` instance with two provided contours.
* @param a The first `RectifiedContour` to blend.
* @param b The second `RectifiedContour` to blend.
*/
class ContourBlend(val a: RectifiedContour, val b: RectifiedContour) {
fun mix(blendFunction: (Double) -> Double): ShapeContour {
Expand All @@ -17,11 +25,17 @@ class ContourBlend(val a: RectifiedContour, val b: RectifiedContour) {
}
}


/**
* Create a [ContourBlend] for contours [a] and [b]
* Creates a ContourBlend instance for blending between two ShapeContour instances.
*
* Finding the pose that minimizes the error between [a] and [b] is not part of this function's work.
* This function rectifies the provided contours, splits them into segments suitable for blending,
* and verifies that both resulting rectified contours have an equal number of segments.
*
* @param a the first ShapeContour to blend
* @param b the second ShapeContour to blend
* @return a ContourBlend instance representing the blended contours
* @throws IllegalArgumentException if the preprocessing for contours fails to produce an equal number of segments
*/
fun ContourBlend(a: ShapeContour, b: ShapeContour): ContourBlend {
val ra = a.rectified()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ fun RectifiedContour.splitForBlend(other: RectifiedContour): RectifiedContour {
return ShapeContour.fromContours(splitAt(rts), originalPath.closed && other.originalPath.closed).rectified()
}

/**
* Blends two rectified contours by applying a blend function to their segments.
*
* This method takes two `RectifiedContour` instances and a blend function to produce
* a `ShapeContour` that smoothly interpolates between the two contours.
* The blending is performed by applying the blend function at specific parameter values
* for each segment of the contours.
*
* @param other the other `RectifiedContour` to blend with
* @param blendFunction a function determining the blend factor for the segments as a function of rectified parameter space
* @return a `ShapeContour` that is the result of blending this contour with the other contour
* using the provided blend function
*/
fun RectifiedContour.mix(other: RectifiedContour, blendFunction: (Double) -> Double): ShapeContour {
val n = this.originalPath.segments.size.toDouble()
val segs = (this.originalPath.segments zip other.originalPath.segments).mapIndexed { index, it ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ fun RectifiedPath3D.splitForBlend(other: RectifiedPath3D): RectifiedPath3D {
return Path3D.fromPaths(splitAt(rts), originalPath.closed && other.originalPath.closed).rectified()
}

/**
* Produces a blended 3D path by mixing two rectified paths using a custom blending function.
*
* This function combines segments from the current `RectifiedPath3D` instance with segments
* from another specified `RectifiedPath3D` instance. Each segment is blended based on a
* provided blending function that accepts a normalized parameter and returns a blend weight.
* The resultant path retains characteristics such as closedness if both input paths are closed.
*
* @param other the other `RectifiedPath3D` to be blended with the current path
* @param blendFunction a function that provides blending weights based on a normalized parameter (t-value)
* @return a `Path3D` representing the blended result of the two input paths
*/
fun RectifiedPath3D.mix(other: RectifiedPath3D, blendFunction: (Double) -> Double): Path3D {
val n = this.originalPath.segments.size.toDouble()
val segs = (this.originalPath.segments zip other.originalPath.segments).mapIndexed { index, it ->
Expand Down
34 changes: 22 additions & 12 deletions orx-shapes/src/commonMain/kotlin/blend/SegmentExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import org.openrndr.shape.Segment3D


/**
* Cubic segment mix
* @param other the segment to mix with
* @param f0 the mix factor for the start point
* @param f1 the mix factor for the first control point
* @param f2 the mix factor for the second control point
* @param f3 the mix factor for the end point
* Blends the properties of two `Segment2D` instances based on the provided weights for each control point and the corner property.
*
* The resulting `Segment2D` is computed by interpolating between the corresponding properties
* of `this` segment and the `other` segment, with specific weights for start, control points, end, and corner.
*
* @param other the `Segment2D` to blend with the current segment
* @param f0 the blend factor for the start points
* @param f1 the blend factor for the first control points
* @param f2 the blend factor for the second control points
* @param f3 the blend factor for the end points
* @return a new `Segment2D` that is the blended result
*/
fun Segment2D.mix(other: Segment2D, f0: Double, f1: Double, f2: Double, f3: Double): Segment2D {
val ac = this.cubic
Expand All @@ -30,12 +35,17 @@ fun Segment2D.mix(other: Segment2D, f0: Double, f1: Double, f2: Double, f3: Doub
}

/**
* Cubic segment mix
* @param other the segment to mix with
* @param f0 the mix factor for the start point
* @param f1 the mix factor for the first control point
* @param f2 the mix factor for the second control point
* @param f3 the mix factor for the end point
* Creates a new `Segment3D` by blending the coordinates of two input segments using specified weights.
*
* This function performs a weighted blending operation between the start, control, and end points
* of two cubic `Segment3D` instances, resulting in a new blended segment.
*
* @param other the other `Segment3D` to blend with
* @param f0 the blending weight for the starting point of the segment
* @param f1 the blending weight for the first control point of the segment
* @param f2 the blending weight for the second control point of the segment
* @param f3 the blending weight for the ending point of the segment
* @return a new `Segment3D` that represents the blended result
*/
fun Segment3D.mix(other: Segment3D, f0: Double, f1: Double, f2: Double, f3: Double): Segment3D {
val ac = this.cubic
Expand Down
9 changes: 9 additions & 0 deletions orx-shapes/src/commonMain/kotlin/frames/Frames.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ fun List<Vector3>.frames(up0: Vector3): List<Matrix44> {
return frames(this, up0 = up0)
}

/**
* Calculates a list of frame transformation matrices using parallel transport along a series of positions.
*
* @param positions a list of 3D positions defining the path.
* @param directions an optional list of direction vectors at each position for guiding forward orientation;
* if empty, directions are estimated from the positions.
* @param up0 the initial up vector, must not have zero or NaN length.
* @return a list of 4x4 frame matrices corresponding to the input positions.
*/
fun frames(positions: List<Vector3>, directions: List<Vector3> = emptyList(), up0: Vector3): List<Matrix44> {

require(up0.squaredLength > 0.0) {
Expand Down
17 changes: 17 additions & 0 deletions orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,30 @@ import org.openrndr.math.Matrix44
import org.openrndr.math.Vector3
import org.openrndr.shape.Path3D

/**
* Generates a list of frame transformation matrices along a 3D path using parallel transport.
*
* @param ascendingTs a list of increasing parameter values that define positions along the path.
* @param up0 the initial up vector, used to determine the orientation of frames; must not have zero or NaN length.
* @param analyticalDirections a flag indicating whether to use analytically calculated directions along the path.
* @return a list of 4x4 transformation matrices representing the frames at the specified path positions.
*/
fun Path3D.frames(ascendingTs: List<Double>, up0: Vector3, analyticalDirections: Boolean) : List<Matrix44> {
val positions = ascendingTs.map { this.position(it) }
val directions = if (analyticalDirections) ascendingTs.map { this.direction(it) } else emptyList()

return frames(positions, directions, up0)
}

/**
* Computes a list of frame transformation matrices along a 3D rectified path using parallel transport.
*
* @param ascendingTs a list of increasing parameter values that define positions along the path.
* @param up0 the initial up vector, which determines the initial orientation of the frames.
* @param analyticalDirections whether to calculate direction vectors analytically;
* if false, this will use an empty list as directions.
* @return a list of 4x4 transformation matrices representing frames at the specified positions on the path.
*/
fun RectifiedPath3D.frames(ascendingTs: List<Double>, up0: Vector3, analyticalDirections: Boolean = true) : List<Matrix44> {
val positions = ascendingTs.map { this.position(it) }
val directions = if (analyticalDirections) ascendingTs.map { this.direction(it) } else emptyList()
Expand Down
19 changes: 19 additions & 0 deletions orx-shapes/src/commonMain/kotlin/hobbycurve/HobbyCurve.kt
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,18 @@ private fun thomas(a: DoubleArray, b: DoubleArray, c: DoubleArray, d: DoubleArra
return x
}

/**
* Uses the Sherman-Morrison formula to solve a system of linear equations with a modified
* tridiagonal matrix, based on the Thomas algorithm.
*
* @param a The lower diagonal coefficients of the tridiagonal matrix.
* @param b The main diagonal coefficients of the tridiagonal matrix.
* @param c The upper diagonal coefficients of the tridiagonal matrix.
* @param d The right-hand side vector of the system of equations.
* @param s The value for the last row modification in the matrix.
* @param t The value for the first row modification in the matrix.
* @return A solution vector of the system of equations considering the specified matrix modifications.
*/
private fun sherman(
a: DoubleArray,
b: DoubleArray,
Expand All @@ -270,6 +282,13 @@ private fun sherman(
}
}

/**
* Calculates a parameter used in Hobby's algorithm for constructing smooth curves.
*
* @param a The first angle in radians, representing the direction of the tangent vector at the start of the segment.
* @param b The second angle in radians, representing the direction of the tangent vector at the end of the segment.
* @return A computed value used to adjust the control points for the curve segment.
*/
private fun rho(a: Double, b: Double): Double {
val sa = sin(a)
val sb = sin(b)
Expand Down
24 changes: 23 additions & 1 deletion orx-shapes/src/commonMain/kotlin/primitives/Arc.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,36 @@ import org.openrndr.shape.ShapeContour
import org.openrndr.shape.contour

/**
* A circular arc
* Represents an arc defined by a center point, a radius, and a range of angles.
*
* This class provides methods to compute the position of a point along the arc
* and to create a shape contour representation of the arc.
*
* @property center The center point of the arc.
* @property radius The radius of the arc.
* @property angle0 The starting angle of the arc, in degrees.
* @property angle1 The ending angle of the arc, in degrees.
*/
class Arc(val center: Vector2, val radius: Double, val angle0: Double, val angle1: Double) : LinearType<Arc> {
/**
* Calculates the position of a point along the arc at a specified parameter `t`.
* The parameter `t` interpolates between the starting and ending angles of the arc.
*
* @param t A parameter ranging from 0.0 to 1.0, where 0.0 corresponds to the starting point of the arc
* and 1.0 corresponds to the ending point of the arc.
* @return The position of the point on the arc as a [Vector2].
*/
fun position(t: Double): Vector2 {
val angle = mix(angle0, angle1, t.clamp(0.0, 1.0))
return Polar(angle, radius).cartesian + center
}

/**
* A computed property that provides a [ShapeContour] representation of the arc.
* The contour is constructed by moving to the position at the start of the arc (t=0.0),
* and then creating a circular arc from that point through an intermediate position
* (t=0.5) before ending at the final position (t=1.0).
*/
val contour: ShapeContour
get() {
return contour {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ import kotlin.math.max
import kotlin.math.min

/**
* Find intersection of [this] and [other]
* @return a rectangle shaped intersection or [Rectangle.EMPTY] when the intersection is empty.
* Computes the intersection of the current box with another box.
* If the two boxes intersect, the resulting box represents the overlapping region.
* If the two boxes do not intersect, an empty box is returned.
*
* @param other The box to intersect with the current box.
* @return A new box representing the overlapping region between the current box and the specified box,
* or an empty box if there is no intersection.
*/
fun Box.intersection(other: Box) : Box = if (this.intersects(other)) {
val tn = this.normalized
Expand Down
11 changes: 11 additions & 0 deletions orx-shapes/src/commonMain/kotlin/primitives/Net.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import org.openrndr.shape.Circle
import org.openrndr.shape.LineSegment
import org.openrndr.shape.ShapeContour

/**
* Represents a net defined by two points and a circle. The net can be seen as a structure
* that connects the two points with the circle in between, forming a string-like shape.
*
* This class implements basic linear transformations such as scaling and translation
* and defines how nets can interact by addition or subtraction.
*
* @property point0 The starting point of the net.
* @property point1 The ending point of the net.
* @property circle The circle around which the net is constructed.
*/
class Net(val point0: Vector2, val point1: Vector2, val circle: Circle) :
LinearType<Net> {
override fun div(scale: Double) =
Expand Down
11 changes: 11 additions & 0 deletions orx-shapes/src/commonMain/kotlin/primitives/Pulley.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ import org.openrndr.shape.Circle
import org.openrndr.shape.LineSegment
import org.openrndr.shape.ShapeContour

/**
* Represents a pulley system defined by two circles.
*
* This class models a geometric structure where two circles are connected with
* tangential lines, forming a pulley-like system. The `Pulley` class provides
* operations for scaling, addition, and subtraction, as well as generating
* a contour that represents the shape of the pulley.
*
* @property circle0 The first circle in the pulley system.
* @property circle1 The second circle in the pulley system.
*/
class Pulley(val circle0: Circle, val circle1: Circle) : LinearType<Pulley> {
override fun div(scale: Double): Pulley {
return Pulley(circle0 / scale, circle1 / scale)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import kotlin.math.max
import kotlin.math.min

/**
* Find intersection of [this] and [other]
* @return a rectangle shaped intersection or [Rectangle.EMPTY] when the intersection is empty.
* Computes the intersection of two rectangles and returns the resulting rectangle.
* If the rectangles do not intersect, an empty rectangle is returned.
*
* @param other The rectangle to intersect with the current rectangle.
* @return A [Rectangle] representing the overlapping area of the two rectangles,
* or an empty rectangle if there is no intersection.
*/
fun Rectangle.intersection(other: Rectangle) : Rectangle = if (this.intersects(other)) {
val tn = this.normalized
Expand Down
11 changes: 11 additions & 0 deletions orx-shapes/src/commonMain/kotlin/primitives/Tear.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import org.openrndr.shape.Circle
import org.openrndr.shape.LineSegment
import org.openrndr.shape.ShapeContour

/**
* Represents a "Tear" consisting of a point and a circle.
*
* This class allows operations such as addition, subtraction, scaling, and division,
* which are defined element-wise for the point and circle components of the Tear.
* Additionally, it provides a computed property that generates a closed shape contour
* based on the geometry of the Tear.
*
* @property point The [Vector2] coordinate representing a point in the Tear.
* @property circle The [Circle] geometry associated with the Tear.
*/
class Tear(val point: Vector2, val circle: Circle) : LinearType<Tear> {
override fun div(scale: Double) = Tear(point / scale, circle / scale)

Expand Down
Loading

0 comments on commit 2236da0

Please sign in to comment.