From c8b9dcf62cc67aa5743ddafeb041b7e839f4440c Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Thu, 28 Dec 2023 21:22:00 +0100 Subject: [PATCH] [orx-shapes] Add inverseRectify to RectifiedContour --- .../kotlin/rectify/RectifiedContour.kt | 40 ++++++++++++++++++- .../commonTest/kotlin/TestRectifiedContour.kt | 20 ++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 orx-shapes/src/commonTest/kotlin/TestRectifiedContour.kt diff --git a/orx-shapes/src/commonMain/kotlin/rectify/RectifiedContour.kt b/orx-shapes/src/commonMain/kotlin/rectify/RectifiedContour.kt index 09dfaccc..217539bc 100644 --- a/orx-shapes/src/commonMain/kotlin/rectify/RectifiedContour.kt +++ b/orx-shapes/src/commonMain/kotlin/rectify/RectifiedContour.kt @@ -10,8 +10,15 @@ import kotlin.math.floor /** * RectifiedContour provides an approximately uniform parameterization for [ShapeContour] */ -class RectifiedContour(val contour: ShapeContour, distanceTolerance: Double = 0.5, lengthScale: Double = 1.0, ) { - val points = contour.equidistantPositionsWithT((contour.length * lengthScale).toInt().coerceAtLeast(2), distanceTolerance) +class RectifiedContour(val contour: ShapeContour, distanceTolerance: Double = 0.5, lengthScale: Double = 1.0) { + val points = + contour.equidistantPositionsWithT((contour.length * lengthScale).toInt().coerceAtLeast(2), distanceTolerance) + + val intervals by lazy { + points.zipWithNext().map { + Pair(it.first.second, it.second.second) + } + } private fun safe(t: Double): Double { return if (contour.closed) { @@ -44,6 +51,35 @@ class RectifiedContour(val contour: ShapeContour, distanceTolerance: Double = 0. } } + fun inverseRectify(t: Double): Double { + if (contour.empty) { + return 0.0 + } else { + if (t <= 0.0) { + return 0.0 + } else if (t >= 1.0) { + return 1.0 + } else { + val index = intervals.binarySearch { + if (t < it.first) { + 1 + } else if (t > it.second) { + -1 + } else { + 0 + } + } + val t0 = t - intervals[index].first + val dt = intervals[index].second - intervals[index].first + val f = t0 / dt + val f0 = index.toDouble() / intervals.size + val f1 = (index + 1.0) / intervals.size + + return f0 * (1.0 - f) + f1 * f + } + } + } + fun position(t: Double): Vector2 { return if (contour.empty) { Vector2.INFINITY diff --git a/orx-shapes/src/commonTest/kotlin/TestRectifiedContour.kt b/orx-shapes/src/commonTest/kotlin/TestRectifiedContour.kt new file mode 100644 index 00000000..d0290361 --- /dev/null +++ b/orx-shapes/src/commonTest/kotlin/TestRectifiedContour.kt @@ -0,0 +1,20 @@ +import org.openrndr.extra.shapes.rectify.rectified +import org.openrndr.shape.Circle +import org.openrndr.shape.Ellipse +import kotlin.math.abs +import kotlin.test.Test +import kotlin.test.assertTrue + +class TestRectifiedContour { + + @Test + fun testInverse() { + val c = Ellipse(40.0, 40.0, 40.0, 80.0).contour.sub(0.0, 0.333) + val r = c.rectified() + val rt = r.rectify(0.125) + val ri = r.inverseRectify(rt) + + assertTrue(abs(ri-0.125) < 1E-5) + + } +} \ No newline at end of file