[orx-shapes] Improve support for open contours in adjustContour
This commit is contained in:
@@ -184,8 +184,8 @@ class ContourAdjuster(var contour: ShapeContour) {
|
||||
* select multiple edges using an index-edge based [predicate]
|
||||
*/
|
||||
fun selectEdges(predicate: (Int, ContourEdge) -> Boolean) {
|
||||
vertexSelection =
|
||||
(0 until if (contour.closed) contour.segments.size else contour.segments.size + 1).filter { index ->
|
||||
edgeSelection =
|
||||
(contour.segments.indices).filter { index ->
|
||||
predicate(index, ContourEdge(contour, index))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ data class ContourAdjusterEdge(val contourAdjuster: ContourAdjuster, val segment
|
||||
return contourAdjuster.contour.segments[segmentIndex()].normal(t)
|
||||
}
|
||||
|
||||
val length: Double
|
||||
get() {
|
||||
return contourAdjuster.contour.segments[segmentIndex()].length
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A [ContourAdjusterVertex] interface for the start-vertex of the edge
|
||||
|
||||
@@ -10,10 +10,34 @@ class ContourAdjusterVertex(val contourAdjuster: ContourAdjuster, val segmentInd
|
||||
contourAdjuster.updateSelection(newVertex.adjustments)
|
||||
}
|
||||
|
||||
val position: Vector2
|
||||
val previous: ContourAdjusterVertex?
|
||||
get() {
|
||||
return contourAdjuster.contour.segments[segmentIndex()].start
|
||||
return if (contourAdjuster.contour.closed || segmentIndex() > 0) {
|
||||
ContourAdjusterVertex(contourAdjuster, { (segmentIndex() - 1).mod(contourAdjuster.contour.segments.size) })
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
val next: ContourAdjusterVertex?
|
||||
get() {
|
||||
return if (contourAdjuster.contour.closed || segmentIndex() < contourAdjuster.contour.segments.size-1) {
|
||||
ContourAdjusterVertex(contourAdjuster, { (segmentIndex() + 1).mod(contourAdjuster.contour.segments.size) })
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val t: Double
|
||||
get() = ContourVertex(contourAdjuster.contour, segmentIndex(), emptyList()).t
|
||||
|
||||
|
||||
val position: Vector2
|
||||
get() = ContourVertex(contourAdjuster.contour, segmentIndex(), emptyList()).position
|
||||
|
||||
val normal: Vector2
|
||||
get() = ContourVertex(contourAdjuster.contour, segmentIndex(), emptyList()).normal
|
||||
|
||||
|
||||
fun select() {
|
||||
contourAdjuster.selectVertex(segmentIndex())
|
||||
|
||||
@@ -85,6 +85,11 @@ data class ContourEdge(
|
||||
}
|
||||
}
|
||||
|
||||
val length: Double
|
||||
get() {
|
||||
return contour.segments[segmentIndex].length
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* replace this edge with a point at [t]
|
||||
@@ -124,7 +129,8 @@ data class ContourEdge(
|
||||
}.windowed(2, 1).map {
|
||||
r.sub(it[0], it[1])
|
||||
}
|
||||
return replacedWith(ShapeContour.fromContours(newSegments, false))
|
||||
require(newSegments.size == parts)
|
||||
return replacedWith(ShapeContour.fromContours(newSegments, false, 1.0))
|
||||
}
|
||||
|
||||
fun replacedWith(openContour: ShapeContour): ContourEdge {
|
||||
|
||||
@@ -6,7 +6,11 @@ import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.transforms.buildTransform
|
||||
import org.openrndr.shape.ShapeContour
|
||||
|
||||
data class ContourVertex(val contour: ShapeContour, val segmentIndex: Int, val adjustments: List<SegmentOperation> = emptyList()) {
|
||||
data class ContourVertex(
|
||||
val contour: ShapeContour,
|
||||
val segmentIndex: Int,
|
||||
val adjustments: List<SegmentOperation> = emptyList()
|
||||
) {
|
||||
fun withoutAdjustments(): ContourVertex {
|
||||
return if (adjustments.isEmpty()) {
|
||||
this
|
||||
@@ -15,9 +19,32 @@ data class ContourVertex(val contour: ShapeContour, val segmentIndex: Int, val a
|
||||
}
|
||||
}
|
||||
|
||||
val normal: Vector2
|
||||
get() {
|
||||
return if (contour.closed || segmentIndex > 0 || segmentIndex < contour.segments.size) {
|
||||
val segmentIn = contour.segments[(segmentIndex - 1).mod(contour.segments.size)]
|
||||
val normalIn = segmentIn.normal(1.0)
|
||||
val normalOut = contour.segments[segmentIndex].normal(0.0)
|
||||
(normalIn + normalOut).normalized
|
||||
} else if (segmentIndex == 0) {
|
||||
contour.normal(0.0)
|
||||
} else if (segmentIndex == contour.segments.size) {
|
||||
contour.normal(1.0)
|
||||
} else {
|
||||
error("segmentIndex out of bounds ${segmentIndex} >= ${contour.segments.size}")
|
||||
}
|
||||
}
|
||||
|
||||
val t: Double
|
||||
get() = segmentIndex.toDouble() / contour.segments.size
|
||||
|
||||
val position: Vector2
|
||||
get() {
|
||||
return contour.segments[segmentIndex].start
|
||||
return if (contour.closed || segmentIndex < contour.segments.size) {
|
||||
contour.segments[segmentIndex].start
|
||||
} else {
|
||||
contour.segments[segmentIndex - 1].end
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(updateTangents: Boolean = true): ContourVertex {
|
||||
@@ -58,7 +85,7 @@ data class ContourVertex(val contour: ShapeContour, val segmentIndex: Int, val a
|
||||
}
|
||||
val transform = buildTransform {
|
||||
translate(position)
|
||||
this.rotate(rotationInDegrees)
|
||||
this@buildTransform.rotate(rotationInDegrees)
|
||||
translate(-position)
|
||||
}
|
||||
return transformTangents(transform, transform)
|
||||
@@ -110,36 +137,65 @@ data class ContourVertex(val contour: ShapeContour, val segmentIndex: Int, val a
|
||||
}, updateTangents)
|
||||
}
|
||||
|
||||
fun transformedBy(transform: Matrix44, updateTangents: Boolean = true): ContourVertex {
|
||||
fun transformedBy(transform: Matrix44, updateTangents: Boolean): ContourVertex {
|
||||
return transformedBy(updateTangents) { v -> v.transformedBy(transform) }
|
||||
}
|
||||
|
||||
fun transformedBy(updateTangents: Boolean = true, transform: (Vector2) -> Vector2): ContourVertex {
|
||||
if (contour.empty) {
|
||||
return withoutAdjustments()
|
||||
}
|
||||
|
||||
val newSegments = contour.segments.map { it }.toMutableList()
|
||||
val refOut = contour.segments[segmentIndex]
|
||||
val refIn = if (contour.closed) contour.segments[(segmentIndex - 1).mod(contour.segments.size)] else
|
||||
val refOut = if (contour.closed || segmentIndex < contour.segments.size) {
|
||||
contour.segments[segmentIndex]
|
||||
} else {
|
||||
contour.segments.last()
|
||||
}
|
||||
val refIn = if (contour.closed) {
|
||||
contour.segments[(segmentIndex - 1).mod(contour.segments.size)]
|
||||
} else {
|
||||
contour.segments.getOrNull(segmentIndex - 1)
|
||||
val newPosition = refOut.start.transformedBy(transform)
|
||||
}
|
||||
val newPosition = if (contour.closed || segmentIndex < contour.segments.size) {
|
||||
transform(refOut.start)
|
||||
} else {
|
||||
transform(refOut.end)
|
||||
}
|
||||
|
||||
if (contour.closed || segmentIndex< contour.segments.size) {
|
||||
newSegments[segmentIndex] = if (updateTangents && !refOut.linear) {
|
||||
val cubicSegment = refOut.cubic
|
||||
val newControls = arrayOf(cubicSegment.control[0].transformedBy(transform), cubicSegment.control[1])
|
||||
val newControls = arrayOf(transform(cubicSegment.control[0]), cubicSegment.control[1])
|
||||
refOut.copy(start = newPosition, control = newControls)
|
||||
} else {
|
||||
newSegments[segmentIndex].copy(start = newPosition)
|
||||
}
|
||||
} else {
|
||||
newSegments[segmentIndex-1] = if (updateTangents && !refOut.linear) {
|
||||
val cubicSegment = refOut.cubic
|
||||
val newControls = arrayOf(cubicSegment.control[0], transform(cubicSegment.control[1]))
|
||||
refOut.copy(end = newPosition, control = newControls)
|
||||
} else {
|
||||
newSegments[segmentIndex-1].copy(end = newPosition)
|
||||
}
|
||||
}
|
||||
|
||||
val segmentIndexIn = (segmentIndex - 1).mod(contour.segments.size)
|
||||
if (refIn != null) {
|
||||
if (refIn != null && (contour.closed || segmentIndex < contour.segments.size)) {
|
||||
newSegments[segmentIndexIn] =
|
||||
if (updateTangents && !refIn.linear) {
|
||||
val cubicSegment = refIn.cubic
|
||||
val newControls = arrayOf(cubicSegment.control[0], cubicSegment.control[1].transformedBy(transform))
|
||||
val newControls = arrayOf(cubicSegment.control[0], transform(cubicSegment.control[1]))
|
||||
newSegments[segmentIndexIn].copy(control = newControls, end = newPosition)
|
||||
} else {
|
||||
|
||||
newSegments[segmentIndexIn].copy(end = newPosition)
|
||||
}
|
||||
}
|
||||
for (s in newSegments.windowed(2, 1)) {
|
||||
require(s[0].end.distanceTo(s[1].start) < 1E-3)
|
||||
}
|
||||
val newContour = ShapeContour.fromSegments(newSegments, contour.closed, contour.polarity)
|
||||
|
||||
return ContourVertex(newContour, segmentIndex)
|
||||
|
||||
@@ -16,7 +16,8 @@ fun ShapeContour.Companion.fromContours(contours: List<ShapeContour>, closed: Bo
|
||||
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 ) {
|
||||
val d = c[0].position(1.0).distanceTo(c[1].position(0.0))
|
||||
if (d > connectEpsilon ) {
|
||||
lineTo(c[1].position(0.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ fun main() {
|
||||
parameters.selectInsertedVertices = true
|
||||
|
||||
|
||||
|
||||
for (i in 0 until 4) {
|
||||
val splitT = cos(seconds + i * Math.PI*0.5)*0.2+0.5
|
||||
selectEdges { it -> true }
|
||||
|
||||
@@ -2,10 +2,8 @@ import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.shape.Circle
|
||||
import org.openrndr.shape.contour
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
fun main() {
|
||||
application {
|
||||
@@ -15,38 +13,22 @@ fun main() {
|
||||
}
|
||||
program {
|
||||
extend {
|
||||
var contour =
|
||||
Circle(drawer.bounds.center, 300.0).contour
|
||||
var contour = contour {
|
||||
moveTo(drawer.bounds.center - Vector2(300.0, 0.0))
|
||||
lineTo(drawer.bounds.center + Vector2(300.0, 0.0))
|
||||
}
|
||||
|
||||
contour = adjustContour(contour) {
|
||||
selectEdges(0, 2)
|
||||
selectEdge(0)
|
||||
edge.splitIn(128)
|
||||
val tr = cos(seconds) * 0.5 + 0.5
|
||||
|
||||
for (e in edges) {
|
||||
e.replaceWith(contour {
|
||||
moveTo(e.startPosition)
|
||||
lineTo(e.position(0.5) + e.normal(0.5) * cos(seconds) * 150.0)
|
||||
lineTo(e.endPosition)
|
||||
})
|
||||
}
|
||||
selectEdges(0, 1)
|
||||
selectVertices { i, v -> v.t >= tr }
|
||||
val anchor = contour.position(tr)
|
||||
|
||||
for (e in edges) {
|
||||
e.replaceWith(contour {
|
||||
moveTo(e.startPosition)
|
||||
val t = 0.5
|
||||
lineTo(e.position(t) + e.normal(t) * cos(seconds) * 50.0)
|
||||
lineTo(e.endPosition)
|
||||
})
|
||||
}
|
||||
|
||||
selectEdges(0, 1)
|
||||
for (e in edges) {
|
||||
e.replaceWith(contour {
|
||||
moveTo(e.startPosition)
|
||||
val t = 0.5
|
||||
lineTo(e.position(t) + e.normal(t) * sin(seconds) * 50.0)
|
||||
lineTo(e.endPosition)
|
||||
})
|
||||
for (v in vertices) {
|
||||
v.rotate((v.t - tr) * 2000.0, anchor)
|
||||
v.scale(0.05, anchor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user