diff --git a/orx-shapes/src/commonMain/kotlin/alphashape/AlphaShape.kt b/orx-shapes/src/commonMain/kotlin/alphashape/AlphaShape.kt index 3afa5d04a..1222aa390 100644 --- a/orx-shapes/src/commonMain/kotlin/alphashape/AlphaShape.kt +++ b/orx-shapes/src/commonMain/kotlin/alphashape/AlphaShape.kt @@ -15,6 +15,15 @@ fun List.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 diff --git a/orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch.kt b/orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch.kt index 473d431e7..fee179ee1 100644 --- a/orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch.kt +++ b/orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch.kt @@ -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( val points: List>, val colors: List> = emptyList() diff --git a/orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch3D.kt b/orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch3D.kt index 71ead5629..46ab1d689 100644 --- a/orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch3D.kt +++ b/orx-shapes/src/commonMain/kotlin/bezierpatches/BezierPatch3D.kt @@ -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( val points: List>, val colors: List> = emptyList() diff --git a/orx-shapes/src/commonMain/kotlin/blend/ContourBlend.kt b/orx-shapes/src/commonMain/kotlin/blend/ContourBlend.kt index 99af32b39..da1abed87 100644 --- a/orx-shapes/src/commonMain/kotlin/blend/ContourBlend.kt +++ b/orx-shapes/src/commonMain/kotlin/blend/ContourBlend.kt @@ -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 { @@ -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() diff --git a/orx-shapes/src/commonMain/kotlin/blend/RectifiedContourExtensions.kt b/orx-shapes/src/commonMain/kotlin/blend/RectifiedContourExtensions.kt index 9f0feb69e..37a7e6aca 100644 --- a/orx-shapes/src/commonMain/kotlin/blend/RectifiedContourExtensions.kt +++ b/orx-shapes/src/commonMain/kotlin/blend/RectifiedContourExtensions.kt @@ -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 -> diff --git a/orx-shapes/src/commonMain/kotlin/blend/RectifiedPath3DExtensions.kt b/orx-shapes/src/commonMain/kotlin/blend/RectifiedPath3DExtensions.kt index 2c68a409f..79d434b12 100644 --- a/orx-shapes/src/commonMain/kotlin/blend/RectifiedPath3DExtensions.kt +++ b/orx-shapes/src/commonMain/kotlin/blend/RectifiedPath3DExtensions.kt @@ -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 -> diff --git a/orx-shapes/src/commonMain/kotlin/blend/SegmentExtensions.kt b/orx-shapes/src/commonMain/kotlin/blend/SegmentExtensions.kt index aead11455..e7be94a06 100644 --- a/orx-shapes/src/commonMain/kotlin/blend/SegmentExtensions.kt +++ b/orx-shapes/src/commonMain/kotlin/blend/SegmentExtensions.kt @@ -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 @@ -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 diff --git a/orx-shapes/src/commonMain/kotlin/frames/Frames.kt b/orx-shapes/src/commonMain/kotlin/frames/Frames.kt index 3e55d4aa8..2e2fd23e5 100644 --- a/orx-shapes/src/commonMain/kotlin/frames/Frames.kt +++ b/orx-shapes/src/commonMain/kotlin/frames/Frames.kt @@ -14,6 +14,15 @@ fun List.frames(up0: Vector3): List { 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, directions: List = emptyList(), up0: Vector3): List { require(up0.squaredLength > 0.0) { diff --git a/orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt b/orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt index cf60b6b79..92be2ab6a 100644 --- a/orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt +++ b/orx-shapes/src/commonMain/kotlin/frames/Path3DExtensions.kt @@ -5,6 +5,14 @@ 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, up0: Vector3, analyticalDirections: Boolean) : List { val positions = ascendingTs.map { this.position(it) } val directions = if (analyticalDirections) ascendingTs.map { this.direction(it) } else emptyList() @@ -12,6 +20,15 @@ fun Path3D.frames(ascendingTs: List, up0: Vector3, analyticalDirections: 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, up0: Vector3, analyticalDirections: Boolean = true) : List { val positions = ascendingTs.map { this.position(it) } val directions = if (analyticalDirections) ascendingTs.map { this.direction(it) } else emptyList() diff --git a/orx-shapes/src/commonMain/kotlin/hobbycurve/HobbyCurve.kt b/orx-shapes/src/commonMain/kotlin/hobbycurve/HobbyCurve.kt index 0c1eab94c..0fd3a3781 100644 --- a/orx-shapes/src/commonMain/kotlin/hobbycurve/HobbyCurve.kt +++ b/orx-shapes/src/commonMain/kotlin/hobbycurve/HobbyCurve.kt @@ -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, @@ -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) diff --git a/orx-shapes/src/commonMain/kotlin/primitives/Arc.kt b/orx-shapes/src/commonMain/kotlin/primitives/Arc.kt index 8c88ac4f1..b1a3118a2 100644 --- a/orx-shapes/src/commonMain/kotlin/primitives/Arc.kt +++ b/orx-shapes/src/commonMain/kotlin/primitives/Arc.kt @@ -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 { + /** + * 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 { diff --git a/orx-shapes/src/commonMain/kotlin/primitives/BoxIntersection.kt b/orx-shapes/src/commonMain/kotlin/primitives/BoxIntersection.kt index 42da97072..1791f325e 100644 --- a/orx-shapes/src/commonMain/kotlin/primitives/BoxIntersection.kt +++ b/orx-shapes/src/commonMain/kotlin/primitives/BoxIntersection.kt @@ -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 diff --git a/orx-shapes/src/commonMain/kotlin/primitives/Net.kt b/orx-shapes/src/commonMain/kotlin/primitives/Net.kt index 302b3ac18..b7c7014b8 100644 --- a/orx-shapes/src/commonMain/kotlin/primitives/Net.kt +++ b/orx-shapes/src/commonMain/kotlin/primitives/Net.kt @@ -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 { override fun div(scale: Double) = diff --git a/orx-shapes/src/commonMain/kotlin/primitives/Pulley.kt b/orx-shapes/src/commonMain/kotlin/primitives/Pulley.kt index b736b5477..ad67d1a6a 100644 --- a/orx-shapes/src/commonMain/kotlin/primitives/Pulley.kt +++ b/orx-shapes/src/commonMain/kotlin/primitives/Pulley.kt @@ -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 { override fun div(scale: Double): Pulley { return Pulley(circle0 / scale, circle1 / scale) diff --git a/orx-shapes/src/commonMain/kotlin/primitives/RectangleIntersection.kt b/orx-shapes/src/commonMain/kotlin/primitives/RectangleIntersection.kt index 801c17337..346ffca99 100644 --- a/orx-shapes/src/commonMain/kotlin/primitives/RectangleIntersection.kt +++ b/orx-shapes/src/commonMain/kotlin/primitives/RectangleIntersection.kt @@ -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 diff --git a/orx-shapes/src/commonMain/kotlin/primitives/Tear.kt b/orx-shapes/src/commonMain/kotlin/primitives/Tear.kt index e66f9955c..b345860c3 100644 --- a/orx-shapes/src/commonMain/kotlin/primitives/Tear.kt +++ b/orx-shapes/src/commonMain/kotlin/primitives/Tear.kt @@ -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 { override fun div(scale: Double) = Tear(point / scale, circle / scale) diff --git a/orx-shapes/src/commonMain/kotlin/rectify/RectifiedPath.kt b/orx-shapes/src/commonMain/kotlin/rectify/RectifiedPath.kt index 7ab441671..74dfc9d2f 100644 --- a/orx-shapes/src/commonMain/kotlin/rectify/RectifiedPath.kt +++ b/orx-shapes/src/commonMain/kotlin/rectify/RectifiedPath.kt @@ -7,14 +7,27 @@ import org.openrndr.shape.Path import org.openrndr.shape.ShapeContour /** - * RectifiedContour provides an approximately uniform parameterization for [ShapeContour] + * Provides a rectified representation of a path in N-dimensional Euclidean space. Rectification refers + * to the process of mapping the parameter space of the path to a uniform distribution based on arc length. + * + * @param T The specific type of Euclidean vector representing the dimension of the path. + * @property originalPath The underlying path being rectified. + * @property points Final list of points used in the rectification process, possibly including an additional + * point to close the loop if the original path is closed. + * @property intervals Lazy-evaluated list of parameter intervals corresponding to the `points` property, + * used for mapping and inverse mapping of parameter values. + * + * @param distanceTolerance The acceptable tolerance for the distance error in the rectification process. + * Default value is 0.5. + * @param lengthScale Scale factor to adjust the length of the Look-Up Table (LUT) for the rectified path. + * Default value is 1.0. */ abstract class RectifiedPath>( val originalPath: Path, distanceTolerance: Double = 0.5, lengthScale: Double = 1.0 ) { - val candidatePoints = + private val candidatePoints = originalPath.equidistantPositionsWithT((originalPath.length * lengthScale).toInt().coerceAtLeast(2), distanceTolerance) val points = if (originalPath.closed) candidatePoints + candidatePoints.first().copy(second = 1.0) else candidatePoints @@ -56,6 +69,16 @@ abstract class RectifiedPath>( } } + /** + * Computes an inverse rectified t-value for the given normalized `t` parameter. + * This method determines the original parameter space value from a rectified + * (uniformly distributed) parameter space value. + * + * @param t A normalized parameter (between 0.0 and 1.0) in rectified parameter space. + * Values outside this range will be clamped to 0.0 or 1.0. + * @return A normalized parameter (between 0.0 and 1.0) in the original parameter space. + * Returns 0.0 if the original path is empty. + */ fun inverseRectify(t: Double): Double { if (originalPath.empty) { return 0.0 diff --git a/orx-shapes/src/commonMain/kotlin/splines/CatmullRom.kt b/orx-shapes/src/commonMain/kotlin/splines/CatmullRom.kt index 030b310e1..82923bc79 100644 --- a/orx-shapes/src/commonMain/kotlin/splines/CatmullRom.kt +++ b/orx-shapes/src/commonMain/kotlin/splines/CatmullRom.kt @@ -313,6 +313,15 @@ fun CatmullRomChain2.toContour(): ShapeContour = +/** + * Converts the current 3D Catmull-Rom spline segment into a cubic Bézier curve representation. + * + * This function calculates the four control points required for a cubic Bézier curve + * using the Catmull-Rom spline's positions and its alpha value determining the tension. + * The resulting cubic Bézier curve spans between `p1` and `p2` of the Catmull-Rom segment. + * + * @return A [Segment3D] object representing the equivalent cubic Bézier curve of the Catmull-Rom spline segment. + */ fun CatmullRom3.toSegment(): Segment3D { val d1a2 = (p1 - p0).length.pow(2 * alpha) val d2a2 = (p2 - p1).length.pow(2 * alpha) @@ -329,4 +338,12 @@ fun CatmullRom3.toSegment(): Segment3D { return Segment3D(b0, b1, b2, b3) } +/** + * Converts a 3D Catmull-Rom spline chain into a `Path3D` representation. + * + * The resulting `Path3D` contains the segments generated from the Catmull-Rom spline + * and preserves the information about whether the spline forms a closed loop. + * + * @return A `Path3D` object representing the converted spline chain. + */ fun CatmullRomChain3.toPath3D(): Path3D = Path3D(segments.map { it.toSegment() }, this.loop) \ No newline at end of file diff --git a/orx-shapes/src/commonMain/kotlin/text/TextShapes.kt b/orx-shapes/src/commonMain/kotlin/text/TextShapes.kt index 0bb6dc2fb..1686199a7 100644 --- a/orx-shapes/src/commonMain/kotlin/text/TextShapes.kt +++ b/orx-shapes/src/commonMain/kotlin/text/TextShapes.kt @@ -6,6 +6,16 @@ import org.openrndr.math.Vector2 import org.openrndr.math.transforms.buildTransform import org.openrndr.shape.Shape +/** + * Generates a list of shapes representing the given text with the specified font face, size, and position. + * + * @param face The font face used to render the text. + * @param text The text content to be converted into shapes. + * @param size The font size to be applied to the shapes. + * @param position The starting position for rendering the text, defaulting to the origin vector. + * @param scaler A function that scales the font face. By default, it uses `fontHeightScaler`. + * @return A list of shapes representing the rendered text. + */ fun shapesFromText( face: Face, text: String, diff --git a/orx-shapes/src/commonMain/kotlin/utilities/FromContours.kt b/orx-shapes/src/commonMain/kotlin/utilities/FromContours.kt index 282ed4dae..dc972c22c 100644 --- a/orx-shapes/src/commonMain/kotlin/utilities/FromContours.kt +++ b/orx-shapes/src/commonMain/kotlin/utilities/FromContours.kt @@ -4,7 +4,12 @@ import org.openrndr.shape.ShapeContour import org.openrndr.shape.contour /** - * Create a contour from a list of contours + * Creates a new `ShapeContour` by combining multiple `ShapeContour` instances. + * + * @param contours a list of `ShapeContour` to be combined; empty contours are removed + * @param closed a boolean indicating whether the resulting `ShapeContour` should be closed + * @param connectEpsilon the tolerance for connecting contours, default is 1E-6 + * @return a new `ShapeContour` combining the input contours */ fun ShapeContour.Companion.fromContours(contours: List, closed: Boolean, connectEpsilon:Double=1E-6) : ShapeContour { @Suppress("NAME_SHADOWING") val contours = contours.filter { !it.empty } diff --git a/orx-shapes/src/commonMain/kotlin/utilities/FromPaths.kt b/orx-shapes/src/commonMain/kotlin/utilities/FromPaths.kt index f61fd6c5c..5b99f73c6 100644 --- a/orx-shapes/src/commonMain/kotlin/utilities/FromPaths.kt +++ b/orx-shapes/src/commonMain/kotlin/utilities/FromPaths.kt @@ -4,7 +4,12 @@ import org.openrndr.shape.Path3D import org.openrndr.shape.path3D /** - * Create a [Path3D] from a list of paths + * Creates a new Path3D by combining multiple Path3D contours. + * + * @param contours a list of Path3D to be combined; empty paths are removed + * @param closed whether the resulting Path3D should be closed + * @param connectEpsilon the tolerance for connecting contours, default is 1E-6 + * @return a new Path3D combining the input contours */ fun Path3D.Companion.fromPaths(contours: List, closed: Boolean, connectEpsilon:Double=1E-6) : Path3D { @Suppress("NAME_SHADOWING") val contours = contours.filter { !it.empty } diff --git a/orx-shapes/src/commonMain/kotlin/utilities/SplitAt.kt b/orx-shapes/src/commonMain/kotlin/utilities/SplitAt.kt index 3508c6e9e..ed069ecb5 100644 --- a/orx-shapes/src/commonMain/kotlin/utilities/SplitAt.kt +++ b/orx-shapes/src/commonMain/kotlin/utilities/SplitAt.kt @@ -3,6 +3,13 @@ package org.openrndr.extra.shapes.utilities import org.openrndr.math.EuclideanVector import org.openrndr.shape.* +/** + * Splits the current `ShapeContour` into multiple contours at the specified segment index and position within the segment. + * + * @param segmentIndex the index of the segment where the split should occur + * @param segmentT the normalized position (0.0 to 1.0) within the segment where the split should occur + * @return a list of `ShapeContour` objects resulting from the split + */ fun ShapeContour.splitAt(segmentIndex: Double, segmentT: Double): List { val t = (1.0 / segments.size) * (segmentIndex + segmentT) return splitAt(listOf(t)) @@ -14,6 +21,15 @@ fun Path3D.splitAt(segmentIndex: Double, segmentT: Double): List { } +/** + * Splits the path at the given normalized parameter values (`ascendingTs`) and returns a list of subpaths. + * + * @param ascendingTs a list of normalized parameter values (from 0.0 to 1.0) specifying where the path should be split. + * The list must be in ascending order. + * @param weldEpsilon a small tolerance value used to merge closely adjacent parameter values; defaults to 1E-6. + * @return a list of subpaths representing the segments of the original path split at the specified parameter values. + * If the path is empty or `ascendingTs` is empty, the method returns a list containing the original path. + */ fun > Path.splitAtBase(ascendingTs: List, weldEpsilon: Double = 1E-6): List> { if (empty || ascendingTs.isEmpty()) { return listOf(this) @@ -24,11 +40,29 @@ fun > Path.splitAtBase(ascendingTs: List, weld } } +/** + * Splits the current `ShapeContour` at specified normalized parameter values and returns sub-contours. + * + * @param ascendingTs a list of normalized parameter values (from 0.0 to 1.0) where the contour will be split. + * The values must be in ascending order. + * @param weldEpsilon a small tolerance value to merge closely adjacent parameter values; defaults to 1E-6. + * @return a list of `ShapeContour` objects representing the segments of the original contour split at the specified + * parameter values. If the contour is empty or `ascendingTs` is empty, returns the original contour as a single item in the list. + */ fun ShapeContour.splitAt(ascendingTs: List, weldEpsilon: Double = 1E-6): List { @Suppress("UNCHECKED_CAST") return splitAtBase(ascendingTs, weldEpsilon) as List } +/** + * Splits the current Path3D into multiple subpaths at specified normalized parameter values. + * + * @param ascendingTs a list of normalized parameter values (ranging from 0.0 to 1.0) indicating where the path should be split. + * The list must be in ascending order. + * @param weldEpsilon a small tolerance value used to merge closely adjacent parameter values. The default value is 1E-6. + * @return a list of Path3D objects representing the segments of the original path split at the specified parameter values. + * If the path is empty or the `ascendingTs` list is empty, the method returns a list containing the original path. + */ fun Path3D.splitAt(ascendingTs: List, weldEpsilon: Double = 1E-6): List { @Suppress("UNCHECKED_CAST") return splitAtBase(ascendingTs, weldEpsilon) as List @@ -48,12 +82,29 @@ fun > BezierSegment.splitAtBase( } } +/** + * Splits the current `Segment2D` instance at specified parameter values. + * + * @param ascendingTs a list of parameter values in ascending order where the segment + * should be split. These should be in the range [0.0, 1.0]. + * @param weldEpsilon a tolerance value used to merge parameter values that are + * too close to each other. Defaults to 1E-6. + * @return a list of `Segment2D` parts obtained by splitting the original segment + * at the given parameter values. + */ fun Segment2D.splitAt(ascendingTs: List, weldEpsilon: Double = 1E-6) : List { @Suppress("UNCHECKED_CAST") return splitAtBase(ascendingTs, weldEpsilon) as List } +/** + * Splits the current 3D segment into multiple sub-segments at the specified parameter `t` values. + * + * @param ascendingTs a list of `t` values between 0.0 and 1.0, in ascending order, where the segment should be split + * @param weldEpsilon a small tolerance value to merge very close `t` values, default is 1E-6 + * @return a list of sub-segments of type `Segment3D` resulting from the splits + */ fun Segment3D.splitAt(ascendingTs: List, weldEpsilon: Double = 1E-6) : List { @Suppress("UNCHECKED_CAST") diff --git a/orx-shapes/src/commonMain/kotlin/utilities/WeldAscending.kt b/orx-shapes/src/commonMain/kotlin/utilities/WeldAscending.kt index ee82e00b3..75f8daa49 100644 --- a/orx-shapes/src/commonMain/kotlin/utilities/WeldAscending.kt +++ b/orx-shapes/src/commonMain/kotlin/utilities/WeldAscending.kt @@ -1,7 +1,12 @@ package org.openrndr.extra.shapes.utilities /** - * Weld values if their distance is less than [epsilon] + * Removes values from a list of doubles that are within a specified tolerance (`epsilon`) of the last added value, + * while preserving the ascending order of the list. The input list must already be in ascending order. + * + * @param epsilon the minimum difference between consecutive values in the output list; defaults to 1E-6 + * @return a new list containing the filtered values in the same order, preserving ascending order + * while eliminating near-duplicates */ fun List.weldAscending(epsilon: Double = 1E-6): List { return if (size <= 1) {