[orx-shapes] Add WIP ContourAdjuster framework

This commit is contained in:
Edwin Jakobs
2023-10-23 12:27:45 +02:00
parent 7f41c263a7
commit e7f8add84e
17 changed files with 940 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
package org.openrndr.extra.shapes.utilities
import org.openrndr.shape.ShapeContour
import org.openrndr.shape.contour
/**
* Create a contour from a list of contours
*/
fun ShapeContour.Companion.fromContours(contours: List<ShapeContour>, closed: Boolean, connectEpsilon:Double=1E-6) : ShapeContour {
val contours = contours.filter { !it.empty }
if (contours.isEmpty()) {
return EMPTY
}
return contour {
moveTo(contours.first().position(0.0))
for (c in contours.windowed(2,1,true)) {
copy(c[0])
if (c.size == 2) {
if (c[0].position(1.0).distanceTo(c[1].position(0.0)) > connectEpsilon ) {
lineTo(c[1].position(0.0))
}
}
}
if (closed) {
close()
}
}
}

View File

@@ -0,0 +1,35 @@
package org.openrndr.extra.shapes.utilities
import org.openrndr.shape.ShapeContour
/**
* Insert point at [t]
* @param ascendingTs a list of ascending T values
* @param weldEpsilon minimum distance between T values
*/
fun ShapeContour.insertPointAt(t: Double, weldEpsilon: Double = 1E-6): ShapeContour {
val splitContours = splitAt(listOf(t), weldEpsilon)
return ShapeContour.fromContours(splitContours, closed)
}
/**
* Insert point at [segmentIndex], [segmentT]
* @param weldEpsilon minimum distance between T values
*/
fun ShapeContour.insertPointAt(segmentIndex: Int, segmentT: Double, weldEpsilon: Double = 1E-6): ShapeContour {
val t = (1.0 / segments.size) * (segmentIndex + segmentT)
val splitContours = splitAt(listOf(t), weldEpsilon)
return ShapeContour.fromContours(splitContours, closed)
}
/**
* Insert points at [ascendingTs]
* @param ascendingTs a list of ascending T values
* @param weldEpsilon minimum distance between T values
*/
fun ShapeContour.insertPointsAt(ascendingTs: List<Double>, weldEpsilon:Double = 1E-6) : ShapeContour {
val splitContours = splitAt(ascendingTs, weldEpsilon)
return ShapeContour.fromContours(splitContours, closed)
}

View File

@@ -0,0 +1,30 @@
package org.openrndr.extra.shapes.utilities
import org.openrndr.shape.Segment
import org.openrndr.shape.ShapeContour
fun ShapeContour.splitAt(segmentIndex: Double, segmentT: Double): List<ShapeContour> {
val t = (1.0 / segments.size) * (segmentIndex + segmentT)
return splitAt(listOf(t))
}
fun ShapeContour.splitAt(ascendingTs: List<Double>, weldEpsilon: Double = 1E-6): List<ShapeContour> {
if (empty || ascendingTs.isEmpty()) {
return listOf(this)
}
@Suppress("NAME_SHADOWING") val ascendingTs = (listOf(0.0) + ascendingTs + listOf(1.0)).weldAscending(weldEpsilon)
return ascendingTs.windowed(2, 1).map {
sub(it[0], it[1])
}
}
fun Segment.splitAt(ascendingTs: List<Double>, weldEpsilon: Double = 1E-6): List<Segment> {
if (ascendingTs.isEmpty()) {
return listOf(this)
}
@Suppress("NAME_SHADOWING") val ascendingTs = (listOf(0.0) + ascendingTs + listOf(1.0)).weldAscending(weldEpsilon)
return ascendingTs.windowed(2, 1).map {
sub(it[0], it[1])
}
}

View File

@@ -0,0 +1,21 @@
package org.openrndr.extra.shapes.utilities
/**
* Weld values if their distance is less than [epsilon]
*/
fun List<Double>.weldAscending(epsilon: Double = 1E-6): List<Double> {
return if (size <= 1) {
this
} else {
val result = mutableListOf(first())
var lastPassed = first()
for (i in 1 until size) {
require(this[i] >= lastPassed) { "input list is not in ascending order" }
if (this[i] - lastPassed > epsilon) {
result.add(this[i])
lastPassed = this[i]
}
}
result
}
}