From c3ef5053c110fee33cd595cbee0410726df9f8aa Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Wed, 1 Feb 2023 22:21:52 +0100 Subject: [PATCH] [orx-shapes] Add RectifiedContour --- .../kotlin/rectify/RectifiedContour.kt | 81 +++++++++++++++++++ .../jvmDemo/kotlin/DemoRectifiedContour01.kt | 30 +++++++ .../jvmDemo/kotlin/DemoRectifiedContour02.kt | 33 ++++++++ 3 files changed, 144 insertions(+) create mode 100644 orx-shapes/src/commonMain/kotlin/rectify/RectifiedContour.kt create mode 100644 orx-shapes/src/jvmDemo/kotlin/DemoRectifiedContour01.kt create mode 100644 orx-shapes/src/jvmDemo/kotlin/DemoRectifiedContour02.kt diff --git a/orx-shapes/src/commonMain/kotlin/rectify/RectifiedContour.kt b/orx-shapes/src/commonMain/kotlin/rectify/RectifiedContour.kt new file mode 100644 index 00000000..2ca357ff --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/rectify/RectifiedContour.kt @@ -0,0 +1,81 @@ +package org.openrndr.extra.shapes.rectify + +import org.openrndr.math.Matrix44 +import org.openrndr.math.Vector2 +import org.openrndr.math.clamp +import org.openrndr.shape.Segment +import org.openrndr.shape.ShapeContour +import kotlin.math.floor + +/** + * RectifiedContour provides an approximately uniform parameterization for [ShapeContour] + */ +class RectifiedContour(val contour: ShapeContour, lengthScale: Double = 1.0, distanceTolerance: Double = 0.5) { + val points = contour.equidistantPositionsWithT((contour.length * lengthScale).toInt(), distanceTolerance) + + private fun safe(t: Double): Double { + return if (contour.closed) { + t.mod(1.0) + } else { + t.clamp(0.0, 1.0) + } + } + + /** + * computes a rectified t-value for [contour] + */ + fun rectify(t: Double): Double { + if (t <= 0.0) { + return 0.0 + } + val fi = t * (points.size - 1.0) + val fr = fi.mod(1.0) + val i0 = fi.toInt() + val i1 = i0 + 1 + + return if (i0 >= points.size - 1) { + 1.0 + } else { + (points[i0].second * (1.0 - fr) + points[i1].second * fr) + } + } + + fun position(t: Double): Vector2 { + return contour.position(rectify(safe(t))) + } + + fun velocity(t: Double): Vector2 { + val (segment, st) = contour.segment(rectify(safe(t))) + return contour.segments[segment].direction(st) + } + + fun normal(t: Double): Vector2 { + return contour.normal(rectify(safe(t))) + } + + fun pose(t: Double): Matrix44 { + return contour.pose(rectify(safe(t))) + } + + fun sub(t0: Double, t1: Double): ShapeContour { + return if (contour.closed) { + contour.sub(rectify(t0.mod(1.0)) + floor(t0), rectify(t1.mod(1.0)) + floor(t1)) + } else { + contour.sub(rectify(t0), rectify(t1)) + } + } +} + +/** create a rectified contour + * @param distanceTolerance distance tolerance to use, 0.5 is the default distance tolerance + **/ +fun ShapeContour.rectified(distanceTolerance: Double = 0.5): RectifiedContour { + return RectifiedContour(this, distanceTolerance = distanceTolerance) +} + +/** create a rectified contour + * @param distanceTolerance distance tolerance to use, 0.5 is the default distance tolerance + * */ +fun Segment.rectified(distanceTolerance: Double = 0.5): RectifiedContour { + return RectifiedContour(this.contour, distanceTolerance = distanceTolerance) +} \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoRectifiedContour01.kt b/orx-shapes/src/jvmDemo/kotlin/DemoRectifiedContour01.kt new file mode 100644 index 00000000..81f44f67 --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/DemoRectifiedContour01.kt @@ -0,0 +1,30 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.noise.scatter +import org.openrndr.extra.shapes.hobbyCurve +import org.openrndr.extra.shapes.rectify.rectified +import kotlin.random.Random + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + val points = drawer.bounds.scatter(50.0, distanceToEdge = 100.0, random = Random(0)) + val curve = hobbyCurve(points) + val rectified = curve.rectified() + extend { + drawer.clear(ColorRGBa.BLACK) + drawer.stroke = ColorRGBa.PINK + drawer.contour(curve) + drawer.fill = ColorRGBa.RED + drawer.circle(curve.position(seconds*0.05), 10.0) + drawer.fill = ColorRGBa.GREEN + drawer.circle(rectified.position(seconds*0.05), 10.0) + + } + } + } +} \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoRectifiedContour02.kt b/orx-shapes/src/jvmDemo/kotlin/DemoRectifiedContour02.kt new file mode 100644 index 00000000..cf613faf --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/DemoRectifiedContour02.kt @@ -0,0 +1,33 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.noise.scatter +import org.openrndr.extra.shapes.hobbyCurve +import org.openrndr.extra.shapes.rectify.rectified +import kotlin.random.Random + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + val points = drawer.bounds.scatter(80.0, distanceToEdge = 100.0, random = Random(0)) + val curve = hobbyCurve(points, closed = true) + val rectified = curve.rectified() + extend { + drawer.clear(ColorRGBa.BLACK) + drawer.fill = null + drawer.stroke = ColorRGBa.GRAY + drawer.contour(curve) + drawer.strokeWeight = 4.0 + + drawer.stroke = ColorRGBa.RED + drawer.contour(curve.sub(seconds*0.1, seconds*0.1+0.01)) + + drawer.stroke = ColorRGBa.GREEN + drawer.contour(rectified.sub(seconds*0.1, seconds*0.1+0.01)) + } + } + } +} \ No newline at end of file