Update for OPENRNDR segment and path generalizations
This commit is contained in:
@@ -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)
|
||||
}
|
||||
37
orx-shapes/src/commonMain/kotlin/blend/Path3DBlend.kt
Normal file
37
orx-shapes/src/commonMain/kotlin/blend/Path3DBlend.kt
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user