[orx-shapes] Handle RectifiedPath3D.path better
This commit is contained in:
@@ -28,8 +28,8 @@ fun ContourBlend(a: ShapeContour, b: ShapeContour): ContourBlend {
|
|||||||
val rb = b.rectified()
|
val rb = b.rectified()
|
||||||
val sa = ra.splitForBlend(rb)
|
val sa = ra.splitForBlend(rb)
|
||||||
val sb = rb.splitForBlend(ra)
|
val sb = rb.splitForBlend(ra)
|
||||||
require(sa.path.segments.size == sb.path.segments.size) {
|
require(sa.originalPath.segments.size == sb.originalPath.segments.size) {
|
||||||
"preprocessing for contours failed to produce equal number of segments. ${sa.path.segments.size}, ${sb.path.segments.size}"
|
"preprocessing for contours failed to produce equal number of segments. ${sa.originalPath.segments.size}, ${sb.originalPath.segments.size}"
|
||||||
}
|
}
|
||||||
return ContourBlend(sa, sb)
|
return ContourBlend(sa, sb)
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package org.openrndr.extra.shapes.blend
|
package org.openrndr.extra.shapes.blend
|
||||||
|
|
||||||
import org.openrndr.extra.shapes.rectify.RectifiedContour
|
|
||||||
import org.openrndr.extra.shapes.rectify.RectifiedPath3D
|
import org.openrndr.extra.shapes.rectify.RectifiedPath3D
|
||||||
import org.openrndr.extra.shapes.rectify.rectified
|
import org.openrndr.extra.shapes.rectify.rectified
|
||||||
import org.openrndr.shape.Path3D
|
import org.openrndr.shape.Path3D
|
||||||
import org.openrndr.shape.ShapeContour
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ContourBlend holds two rectified contours with an equal amount of segments
|
* ContourBlend holds two rectified contours with an equal amount of segments
|
||||||
@@ -30,8 +28,8 @@ fun Path3DBlend(a: Path3D, b: Path3D): Path3DBlend {
|
|||||||
val rb = b.rectified()
|
val rb = b.rectified()
|
||||||
val sa = ra.splitForBlend(rb)
|
val sa = ra.splitForBlend(rb)
|
||||||
val sb = rb.splitForBlend(ra)
|
val sb = rb.splitForBlend(ra)
|
||||||
require(sa.path.segments.size == sb.path.segments.size) {
|
require(sa.originalPath.segments.size == sb.originalPath.segments.size) {
|
||||||
"preprocessing for contours failed to produce equal number of segments. ${sa.path.segments.size}, ${sb.path.segments.size}"
|
"preprocessing for contours failed to produce equal number of segments. ${sa.originalPath.segments.size}, ${sb.originalPath.segments.size}"
|
||||||
}
|
}
|
||||||
return Path3DBlend(sa, sb)
|
return Path3DBlend(sa, sb)
|
||||||
}
|
}
|
||||||
@@ -10,21 +10,21 @@ import org.openrndr.shape.ShapeContour
|
|||||||
* Split for blending with [other]
|
* Split for blending with [other]
|
||||||
*/
|
*/
|
||||||
fun RectifiedContour.splitForBlend(other: RectifiedContour): RectifiedContour {
|
fun RectifiedContour.splitForBlend(other: RectifiedContour): RectifiedContour {
|
||||||
val ts = (0 until other.path.segments.size + 1).map { it.toDouble() / other.path.segments.size }
|
val ts = (0 until other.originalPath.segments.size + 1).map { it.toDouble() / other.originalPath.segments.size }
|
||||||
val rts = ts.map { other.inverseRectify(it) }
|
val rts = ts.map { other.inverseRectify(it) }
|
||||||
|
|
||||||
return ShapeContour.fromContours(splitAt(rts), path.closed && other.path.closed).rectified()
|
return ShapeContour.fromContours(splitAt(rts), originalPath.closed && other.originalPath.closed).rectified()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RectifiedContour.mix(other: RectifiedContour, blendFunction: (Double) -> Double): ShapeContour {
|
fun RectifiedContour.mix(other: RectifiedContour, blendFunction: (Double) -> Double): ShapeContour {
|
||||||
val n = this.path.segments.size.toDouble()
|
val n = this.originalPath.segments.size.toDouble()
|
||||||
val segs = (this.path.segments zip other.path.segments).mapIndexed { index, it ->
|
val segs = (this.originalPath.segments zip other.originalPath.segments).mapIndexed { index, it ->
|
||||||
val t0 = inverseRectify(index / n)
|
val t0 = inverseRectify(index / n)
|
||||||
val t1 = inverseRectify((index + 1 / 3.0) / n)
|
val t1 = inverseRectify((index + 1 / 3.0) / n)
|
||||||
val t2 = inverseRectify((index + 2 / 3.0) / n)
|
val t2 = inverseRectify((index + 2 / 3.0) / n)
|
||||||
val t3 = inverseRectify((index + 1) / n)
|
val t3 = inverseRectify((index + 1) / n)
|
||||||
(it.first as Segment2D).mix(it.second as Segment2D, blendFunction(t0), blendFunction(t1), blendFunction(t2), blendFunction(t3))
|
(it.first as Segment2D).mix(it.second as Segment2D, blendFunction(t0), blendFunction(t1), blendFunction(t2), blendFunction(t3))
|
||||||
}
|
}
|
||||||
return ShapeContour.fromSegments(segs, path.closed && other.path.closed)
|
return ShapeContour.fromSegments(segs, originalPath.closed && other.originalPath.closed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,21 +10,21 @@ import org.openrndr.shape.Segment3D
|
|||||||
* Split for blending with [other]
|
* Split for blending with [other]
|
||||||
*/
|
*/
|
||||||
fun RectifiedPath3D.splitForBlend(other: RectifiedPath3D): RectifiedPath3D {
|
fun RectifiedPath3D.splitForBlend(other: RectifiedPath3D): RectifiedPath3D {
|
||||||
val ts = (0 until other.path.segments.size + 1).map { it.toDouble() / other.path.segments.size }
|
val ts = (0 until other.originalPath.segments.size + 1).map { it.toDouble() / other.originalPath.segments.size }
|
||||||
val rts = ts.map { other.inverseRectify(it) }
|
val rts = ts.map { other.inverseRectify(it) }
|
||||||
|
|
||||||
return Path3D.fromPaths(splitAt(rts), path.closed && other.path.closed).rectified()
|
return Path3D.fromPaths(splitAt(rts), originalPath.closed && other.originalPath.closed).rectified()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RectifiedPath3D.mix(other: RectifiedPath3D, blendFunction: (Double) -> Double): Path3D {
|
fun RectifiedPath3D.mix(other: RectifiedPath3D, blendFunction: (Double) -> Double): Path3D {
|
||||||
val n = this.path.segments.size.toDouble()
|
val n = this.originalPath.segments.size.toDouble()
|
||||||
val segs = (this.path.segments zip other.path.segments).mapIndexed { index, it ->
|
val segs = (this.originalPath.segments zip other.originalPath.segments).mapIndexed { index, it ->
|
||||||
val t0 = inverseRectify(index / n)
|
val t0 = inverseRectify(index / n)
|
||||||
val t1 = inverseRectify((index + 1 / 3.0) / n)
|
val t1 = inverseRectify((index + 1 / 3.0) / n)
|
||||||
val t2 = inverseRectify((index + 2 / 3.0) / n)
|
val t2 = inverseRectify((index + 2 / 3.0) / n)
|
||||||
val t3 = inverseRectify((index + 1) / n)
|
val t3 = inverseRectify((index + 1) / n)
|
||||||
(it.first as Segment3D).mix(it.second as Segment3D, blendFunction(t0), blendFunction(t1), blendFunction(t2), blendFunction(t3))
|
(it.first as Segment3D).mix(it.second as Segment3D, blendFunction(t0), blendFunction(t1), blendFunction(t2), blendFunction(t3))
|
||||||
}
|
}
|
||||||
return Path3D.fromSegments(segs, path.closed && other.path.closed)
|
return Path3D.fromSegments(segs, originalPath.closed && other.originalPath.closed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,41 +8,41 @@ import kotlin.math.floor
|
|||||||
class RectifiedContour(contour: ShapeContour, distanceTolerance: Double = 0.5, lengthScale: Double = 1.0) :
|
class RectifiedContour(contour: ShapeContour, distanceTolerance: Double = 0.5, lengthScale: Double = 1.0) :
|
||||||
RectifiedPath<Vector2>(contour, distanceTolerance, lengthScale) {
|
RectifiedPath<Vector2>(contour, distanceTolerance, lengthScale) {
|
||||||
fun velocity(t: Double): Vector2 {
|
fun velocity(t: Double): Vector2 {
|
||||||
return if (path.empty) {
|
return if (originalPath.empty) {
|
||||||
Vector2.ZERO
|
Vector2.ZERO
|
||||||
} else {
|
} else {
|
||||||
val (segment, st) = path.segment(rectify(safe(t)))
|
val (segment, st) = originalPath.segment(rectify(safe(t)))
|
||||||
path.segments[segment].direction(st)
|
originalPath.segments[segment].direction(st)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun normal(t: Double): Vector2 {
|
fun normal(t: Double): Vector2 {
|
||||||
return if (path.empty) {
|
return if (originalPath.empty) {
|
||||||
Vector2.UNIT_Y
|
Vector2.UNIT_Y
|
||||||
} else {
|
} else {
|
||||||
(path as ShapeContour).normal(rectify(safe(t)))
|
(originalPath as ShapeContour).normal(rectify(safe(t)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pose(t: Double): Matrix44 {
|
fun pose(t: Double): Matrix44 {
|
||||||
path as ShapeContour
|
originalPath as ShapeContour
|
||||||
return if (path.empty) {
|
return if (originalPath.empty) {
|
||||||
Matrix44.IDENTITY
|
Matrix44.IDENTITY
|
||||||
} else {
|
} else {
|
||||||
path.pose(rectify(safe(t)))
|
originalPath.pose(rectify(safe(t)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sub(t0: Double, t1: Double): ShapeContour {
|
override fun sub(t0: Double, t1: Double): ShapeContour {
|
||||||
path as ShapeContour
|
originalPath as ShapeContour
|
||||||
if (path.empty) {
|
if (originalPath.empty) {
|
||||||
return ShapeContour.EMPTY
|
return ShapeContour.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (path.closed) {
|
return if (originalPath.closed) {
|
||||||
path.sub(rectify(t0.mod(1.0)) + floor(t0), rectify(t1.mod(1.0)) + floor(t1))
|
originalPath.sub(rectify(t0.mod(1.0)) + floor(t0), rectify(t1.mod(1.0)) + floor(t1))
|
||||||
} else {
|
} else {
|
||||||
path.sub(rectify(t0), rectify(t1))
|
originalPath.sub(rectify(t0), rectify(t1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,5 +51,5 @@ class RectifiedContour(contour: ShapeContour, distanceTolerance: Double = 0.5, l
|
|||||||
return super.splitAt(ascendingTs, weldEpsilon) as List<ShapeContour>
|
return super.splitAt(ascendingTs, weldEpsilon) as List<ShapeContour>
|
||||||
}
|
}
|
||||||
|
|
||||||
val contour: ShapeContour get() = path as ShapeContour
|
val contour: ShapeContour get() = originalPath as ShapeContour
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.openrndr.extra.shapes.rectify
|
package org.openrndr.extra.shapes.rectify
|
||||||
|
|
||||||
import org.openrndr.extra.shapes.utilities.splitAt
|
|
||||||
import org.openrndr.extra.shapes.utilities.splitAtBase
|
import org.openrndr.extra.shapes.utilities.splitAtBase
|
||||||
import org.openrndr.math.EuclideanVector
|
import org.openrndr.math.EuclideanVector
|
||||||
import org.openrndr.math.clamp
|
import org.openrndr.math.clamp
|
||||||
@@ -11,12 +10,12 @@ import org.openrndr.shape.ShapeContour
|
|||||||
* RectifiedContour provides an approximately uniform parameterization for [ShapeContour]
|
* RectifiedContour provides an approximately uniform parameterization for [ShapeContour]
|
||||||
*/
|
*/
|
||||||
abstract class RectifiedPath<T : EuclideanVector<T>>(
|
abstract class RectifiedPath<T : EuclideanVector<T>>(
|
||||||
open val path: Path<T>,
|
val originalPath: Path<T>,
|
||||||
distanceTolerance: Double = 0.5,
|
distanceTolerance: Double = 0.5,
|
||||||
lengthScale: Double = 1.0
|
lengthScale: Double = 1.0
|
||||||
) {
|
) {
|
||||||
val points =
|
val points =
|
||||||
path.equidistantPositionsWithT((path.length * lengthScale).toInt().coerceAtLeast(2), distanceTolerance)
|
originalPath.equidistantPositionsWithT((originalPath.length * lengthScale).toInt().coerceAtLeast(2), distanceTolerance)
|
||||||
|
|
||||||
val intervals by lazy {
|
val intervals by lazy {
|
||||||
points.zipWithNext().map {
|
points.zipWithNext().map {
|
||||||
@@ -25,7 +24,7 @@ abstract class RectifiedPath<T : EuclideanVector<T>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun safe(t: Double): Double {
|
internal fun safe(t: Double): Double {
|
||||||
return if (path.closed) {
|
return if (originalPath.closed) {
|
||||||
t.mod(1.0)
|
t.mod(1.0)
|
||||||
} else {
|
} else {
|
||||||
t.clamp(0.0, 1.0)
|
t.clamp(0.0, 1.0)
|
||||||
@@ -33,10 +32,10 @@ abstract class RectifiedPath<T : EuclideanVector<T>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* computes a rectified t-value for [path]
|
* computes a rectified t-value for [originalPath]
|
||||||
*/
|
*/
|
||||||
fun rectify(t: Double): Double {
|
fun rectify(t: Double): Double {
|
||||||
if (path.empty) {
|
if (originalPath.empty) {
|
||||||
return 0.0
|
return 0.0
|
||||||
} else {
|
} else {
|
||||||
if (t <= 0.0) {
|
if (t <= 0.0) {
|
||||||
@@ -56,7 +55,7 @@ abstract class RectifiedPath<T : EuclideanVector<T>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun inverseRectify(t: Double): Double {
|
fun inverseRectify(t: Double): Double {
|
||||||
if (path.empty) {
|
if (originalPath.empty) {
|
||||||
return 0.0
|
return 0.0
|
||||||
} else {
|
} else {
|
||||||
if (t <= 0.0) {
|
if (t <= 0.0) {
|
||||||
@@ -85,18 +84,18 @@ abstract class RectifiedPath<T : EuclideanVector<T>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun position(t: Double): T {
|
fun position(t: Double): T {
|
||||||
return if (path.empty) {
|
return if (originalPath.empty) {
|
||||||
path.infinity
|
originalPath.infinity
|
||||||
} else {
|
} else {
|
||||||
path.position(rectify(safe(t)))
|
originalPath.position(rectify(safe(t)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun direction(t: Double): T {
|
fun direction(t: Double): T {
|
||||||
return if (path.empty) {
|
return if (originalPath.empty) {
|
||||||
path.infinity
|
originalPath.infinity
|
||||||
} else {
|
} else {
|
||||||
path.direction(rectify(safe(t)))
|
originalPath.direction(rectify(safe(t)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +106,6 @@ abstract class RectifiedPath<T : EuclideanVector<T>>(
|
|||||||
* @since orx 0.4.4
|
* @since orx 0.4.4
|
||||||
*/
|
*/
|
||||||
open fun splitAt(ascendingTs: List<Double>, weldEpsilon: Double = 1E-6): List<Path<T>> {
|
open fun splitAt(ascendingTs: List<Double>, weldEpsilon: Double = 1E-6): List<Path<T>> {
|
||||||
return path.splitAtBase(ascendingTs.map { rectify(it) }, weldEpsilon)
|
return originalPath.splitAtBase(ascendingTs.map { rectify(it) }, weldEpsilon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ import kotlin.math.floor
|
|||||||
class RectifiedPath3D(contour: Path3D, distanceTolerance: Double = 0.5, lengthScale: Double = 1.0) :
|
class RectifiedPath3D(contour: Path3D, distanceTolerance: Double = 0.5, lengthScale: Double = 1.0) :
|
||||||
RectifiedPath<Vector3>(contour, distanceTolerance, lengthScale) {
|
RectifiedPath<Vector3>(contour, distanceTolerance, lengthScale) {
|
||||||
|
|
||||||
override val path: Path3D = super.path as Path3D
|
val path: Path3D get() = originalPath as Path3D
|
||||||
|
|
||||||
override fun sub(t0: Double, t1: Double): Path3D {
|
override fun sub(t0: Double, t1: Double): Path3D {
|
||||||
if (path.empty) {
|
if (path.empty) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package frames
|
package frames
|
||||||
|
|
||||||
|
import org.openrndr.WindowMultisample
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.draw.DrawPrimitive
|
import org.openrndr.draw.DrawPrimitive
|
||||||
@@ -19,6 +20,7 @@ fun main() {
|
|||||||
configure {
|
configure {
|
||||||
width = 720
|
width = 720
|
||||||
height = 720
|
height = 720
|
||||||
|
multisample = WindowMultisample.SampleCount(4)
|
||||||
}
|
}
|
||||||
program {
|
program {
|
||||||
val random = Random(0)
|
val random = Random(0)
|
||||||
|
|||||||
Reference in New Issue
Block a user