Update for OPENRNDR segment and path generalizations

This commit is contained in:
Edwin Jakobs
2024-03-19 16:31:45 +01:00
parent 8fe7631570
commit af6d35c59b
37 changed files with 579 additions and 277 deletions

View File

@@ -28,8 +28,8 @@ fun ContourBlend(a: ShapeContour, b: ShapeContour): ContourBlend {
val rb = b.rectified()
val sa = ra.splitForBlend(rb)
val sb = rb.splitForBlend(ra)
require(sa.contour.segments.size == sb.contour.segments.size) {
"preprocessing for contours failed to produce equal number of segments. ${sa.contour.segments.size}, ${sb.contour.segments.size}"
require(sa.path.segments.size == sb.path.segments.size) {
"preprocessing for contours failed to produce equal number of segments. ${sa.path.segments.size}, ${sb.path.segments.size}"
}
return ContourBlend(sa, sb)
}

View File

@@ -0,0 +1,37 @@
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.rectified
import org.openrndr.shape.Path3D
import org.openrndr.shape.ShapeContour
/**
* ContourBlend holds two rectified contours with an equal amount of segments
*/
class Path3DBlend(val a: RectifiedPath3D, val b: RectifiedPath3D) {
fun mix(blendFunction: (Double) -> Double): Path3D {
return a.mix(b, blendFunction)
}
fun mix(blend: Double): Path3D {
return a.mix(b) { blend }
}
}
/**
* Create a [ContourBlend] for contours [a] and [b]
*
* Finding the pose that minimizes the error between [a] and [b] is not part of this function's work.
*
*/
fun Path3DBlend(a: Path3D, b: Path3D): Path3DBlend {
val ra = a.rectified()
val rb = b.rectified()
val sa = ra.splitForBlend(rb)
val sb = rb.splitForBlend(ra)
require(sa.path.segments.size == sb.path.segments.size) {
"preprocessing for contours failed to produce equal number of segments. ${sa.path.segments.size}, ${sb.path.segments.size}"
}
return Path3DBlend(sa, sb)
}

View File

@@ -3,28 +3,28 @@ package org.openrndr.extra.shapes.blend
import org.openrndr.extra.shapes.rectify.RectifiedContour
import org.openrndr.extra.shapes.rectify.rectified
import org.openrndr.extra.shapes.utilities.fromContours
import org.openrndr.shape.Segment2D
import org.openrndr.shape.ShapeContour
/**
* Split for blending with [other]
*/
fun RectifiedContour.splitForBlend(other: RectifiedContour): RectifiedContour {
val ts = (0 until other.contour.segments.size + 1).map { it.toDouble() / other.contour.segments.size }
val ts = (0 until other.path.segments.size + 1).map { it.toDouble() / other.path.segments.size }
val rts = ts.map { other.inverseRectify(it) }
return ShapeContour.fromContours(splitAt(rts), contour.closed && other.contour.closed).rectified()
return ShapeContour.fromContours(splitAt(rts), path.closed && other.path.closed).rectified()
}
fun RectifiedContour.mix(other: RectifiedContour, blendFunction: (Double) -> Double): ShapeContour {
val n = this.contour.segments.size.toDouble()
val segs = (this.contour.segments zip other.contour.segments).mapIndexed { index, it ->
val n = this.path.segments.size.toDouble()
val segs = (this.path.segments zip other.path.segments).mapIndexed { index, it ->
val t0 = inverseRectify(index / n)
val t1 = inverseRectify((index + 1 / 3.0) / n)
val t2 = inverseRectify((index + 2 / 3.0) / n)
val t3 = inverseRectify((index + 1) / n)
it.first.mix(it.second, 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, contour.closed && other.contour.closed)
return ShapeContour.fromSegments(segs, path.closed && other.path.closed)
}

View File

@@ -0,0 +1,30 @@
package org.openrndr.extra.shapes.blend
import org.openrndr.extra.shapes.rectify.RectifiedPath3D
import org.openrndr.extra.shapes.rectify.rectified
import org.openrndr.extra.shapes.utilities.fromPaths
import org.openrndr.shape.Path3D
import org.openrndr.shape.Segment3D
/**
* Split for blending with [other]
*/
fun RectifiedPath3D.splitForBlend(other: RectifiedPath3D): RectifiedPath3D {
val ts = (0 until other.path.segments.size + 1).map { it.toDouble() / other.path.segments.size }
val rts = ts.map { other.inverseRectify(it) }
return Path3D.fromPaths(splitAt(rts), path.closed && other.path.closed).rectified()
}
fun RectifiedPath3D.mix(other: RectifiedPath3D, blendFunction: (Double) -> Double): Path3D {
val n = this.path.segments.size.toDouble()
val segs = (this.path.segments zip other.path.segments).mapIndexed { index, it ->
val t0 = inverseRectify(index / n)
val t1 = inverseRectify((index + 1 / 3.0) / n)
val t2 = inverseRectify((index + 2 / 3.0) / n)
val t3 = inverseRectify((index + 1) / n)
(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)
}

View File

@@ -1,7 +1,9 @@
package org.openrndr.extra.shapes.blend
import org.openrndr.math.mix
import org.openrndr.shape.Segment
import org.openrndr.shape.Segment2D
import org.openrndr.shape.Segment3D
/**
* Cubic segment mix
@@ -11,14 +13,14 @@ import org.openrndr.shape.Segment
* @param f2 the mix factor for the second control point
* @param f3 the mix factor for the end point
*/
fun Segment.mix(other: Segment, f0: Double, f1: Double, f2: Double, f3: Double): Segment {
fun Segment2D.mix(other: Segment2D, f0: Double, f1: Double, f2: Double, f3: Double): Segment2D {
val ac = this.cubic
val bc = other.cubic
val acc = if (ac.corner) 1.0 else 0.0
val bcc = if (bc.corner) 1.0 else 0.0
return Segment(
return Segment2D(
ac.start.mix(bc.start, f0),
ac.control[0].mix(bc.control[0], f1),
ac.control[1].mix(bc.control[1], f2),
@@ -26,3 +28,23 @@ fun Segment.mix(other: Segment, f0: Double, f1: Double, f2: Double, f3: Double):
corner = mix(acc, bcc, f0) >= 0.5
)
}
/**
* 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
*/
fun Segment3D.mix(other: Segment3D, f0: Double, f1: Double, f2: Double, f3: Double): Segment3D {
val ac = this.cubic
val bc = other.cubic
return Segment3D(
ac.start.mix(bc.start, f0),
ac.control[0].mix(bc.control[0], f1),
ac.control[1].mix(bc.control[1], f2),
ac.end.mix(bc.end, f3),
)
}