[orx-shapes] Improve contour adjuster framework
This commit is contained in:
@@ -39,6 +39,21 @@ Collection of 2D shape generators and modifiers.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### DemoAdjustContour08
|
||||||
|
[source code](src/jvmDemo/kotlin/DemoAdjustContour08.kt)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### DemoAdjustContour09
|
||||||
|
[source code](src/jvmDemo/kotlin/DemoAdjustContour09.kt)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### DemoAdjustContourContinue01
|
||||||
|
[source code](src/jvmDemo/kotlin/DemoAdjustContourContinue01.kt)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### DemoAlphaShape
|
### DemoAlphaShape
|
||||||
[source code](src/jvmDemo/kotlin/DemoAlphaShape.kt)
|
[source code](src/jvmDemo/kotlin/DemoAlphaShape.kt)
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ kotlin {
|
|||||||
implementation(libs.openrndr.draw)
|
implementation(libs.openrndr.draw)
|
||||||
implementation(libs.openrndr.filter)
|
implementation(libs.openrndr.filter)
|
||||||
implementation(libs.kotlin.reflect)
|
implementation(libs.kotlin.reflect)
|
||||||
|
implementation(libs.kotlin.coroutines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
package org.openrndr.extra.shapes
|
||||||
|
|
||||||
import org.openrndr.math.Polar
|
import org.openrndr.math.Polar
|
||||||
import org.openrndr.math.Vector2
|
import org.openrndr.math.Vector2
|
||||||
import org.openrndr.math.transforms.buildTransform
|
import org.openrndr.math.transforms.buildTransform
|
||||||
|
|||||||
@@ -2,7 +2,17 @@ package org.openrndr.extra.shapes.adjust
|
|||||||
|
|
||||||
import org.openrndr.collections.pop
|
import org.openrndr.collections.pop
|
||||||
import org.openrndr.extra.shapes.vertex.ContourVertex
|
import org.openrndr.extra.shapes.vertex.ContourVertex
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
import org.openrndr.shape.Segment
|
||||||
import org.openrndr.shape.ShapeContour
|
import org.openrndr.shape.ShapeContour
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
|
||||||
|
class ContourAdjusterStatus(
|
||||||
|
val contour: ShapeContour,
|
||||||
|
val selectedSegments: List<Segment>,
|
||||||
|
val selectedPoints: List<Vector2>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,6 +27,7 @@ class ContourAdjuster(var contour: ShapeContour) {
|
|||||||
var clearSelectedEdges: Boolean = false,
|
var clearSelectedEdges: Boolean = false,
|
||||||
var clearSelectedVertices: Boolean = false
|
var clearSelectedVertices: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
var parameters = Parameters()
|
var parameters = Parameters()
|
||||||
|
|
||||||
val parameterStack = ArrayDeque<Parameters>()
|
val parameterStack = ArrayDeque<Parameters>()
|
||||||
@@ -75,6 +86,15 @@ class ContourAdjuster(var contour: ShapeContour) {
|
|||||||
popVertexSelection()
|
popVertexSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val status: ContourAdjusterStatus
|
||||||
|
get() {
|
||||||
|
return ContourAdjusterStatus(contour,
|
||||||
|
edgeSelection.map { contour.segments[it] },
|
||||||
|
vertexSelection.map { if (it < contour.segments.size) contour.segments[it].start else contour.segments[it - 1].end }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the selected vertex
|
* the selected vertex
|
||||||
*/
|
*/
|
||||||
@@ -149,7 +169,6 @@ class ContourAdjuster(var contour: ShapeContour) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* select multiple vertices using an index-vertex based [predicate]
|
* select multiple vertices using an index-vertex based [predicate]
|
||||||
*/
|
*/
|
||||||
@@ -273,3 +292,12 @@ fun adjustContour(contour: ShapeContour, adjuster: ContourAdjuster.() -> Unit):
|
|||||||
ca.apply(adjuster)
|
ca.apply(adjuster)
|
||||||
return ca.contour
|
return ca.contour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmName("adjustContourSequenceStatus")
|
||||||
|
fun adjustContourSequence(
|
||||||
|
contour: ShapeContour,
|
||||||
|
adjuster: ContourAdjuster.() -> Sequence<ContourAdjusterStatus>
|
||||||
|
): Sequence<ContourAdjusterStatus> {
|
||||||
|
val ca = ContourAdjuster(contour)
|
||||||
|
return ca.adjuster()
|
||||||
|
}
|
||||||
@@ -105,4 +105,13 @@ data class ContourAdjusterEdge(val contourAdjuster: ContourAdjuster, val segment
|
|||||||
.subbed(t0, t1)
|
.subbed(t0, t1)
|
||||||
.contour
|
.contour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun moveStartBy(translation: Vector2, updateTangents: Boolean = true) = wrap { startMovedBy(translation, updateTangents) }
|
||||||
|
|
||||||
|
fun moveControl0By(translation: Vector2) = wrap { control0MovedBy(translation) }
|
||||||
|
|
||||||
|
fun moveControl1By(translation: Vector2) = wrap { control1MovedBy(translation) }
|
||||||
|
fun moveEndBy(translation: Vector2, updateTangents: Boolean = true) = wrap { startMovedBy(translation, updateTangents) }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import org.openrndr.extra.shapes.vertex.ContourVertex
|
|||||||
import org.openrndr.math.Vector2
|
import org.openrndr.math.Vector2
|
||||||
|
|
||||||
class ContourAdjusterVertex(val contourAdjuster: ContourAdjuster, val segmentIndex: () -> Int) {
|
class ContourAdjusterVertex(val contourAdjuster: ContourAdjuster, val segmentIndex: () -> Int) {
|
||||||
private fun wrap(block: ContourVertex.() -> ContourVertex) {
|
fun wrap(block: ContourVertex.() -> ContourVertex) {
|
||||||
val newVertex = ContourVertex(contourAdjuster.contour, segmentIndex()).block()
|
val newVertex = ContourVertex(contourAdjuster.contour, segmentIndex()).block()
|
||||||
contourAdjuster.contour = newVertex.contour
|
contourAdjuster.contour = newVertex.contour
|
||||||
contourAdjuster.updateSelection(newVertex.adjustments)
|
contourAdjuster.updateSelection(newVertex.adjustments)
|
||||||
@@ -43,7 +43,19 @@ class ContourAdjusterVertex(val contourAdjuster: ContourAdjuster, val segmentInd
|
|||||||
contourAdjuster.selectVertex(segmentIndex())
|
contourAdjuster.selectVertex(segmentIndex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val controlIn: Vector2?
|
||||||
|
get() = ContourVertex(contourAdjuster.contour, segmentIndex()).controlIn
|
||||||
|
|
||||||
|
val controlOut: Vector2?
|
||||||
|
get() = ContourVertex(contourAdjuster.contour, segmentIndex()).controlOut
|
||||||
|
|
||||||
|
|
||||||
fun remove(updateTangents: Boolean = true) = wrap { remove(updateTangents) }
|
fun remove(updateTangents: Boolean = true) = wrap { remove(updateTangents) }
|
||||||
|
|
||||||
|
fun moveControlInBy(translation: Vector2) = wrap { controlInMovedBy(translation) }
|
||||||
|
|
||||||
|
fun moveControlOutBy(translation: Vector2) = wrap { controlOutMovedBy(translation) }
|
||||||
|
|
||||||
fun moveBy(translation: Vector2, updateTangents: Boolean = true) = wrap { movedBy(translation, updateTangents) }
|
fun moveBy(translation: Vector2, updateTangents: Boolean = true) = wrap { movedBy(translation, updateTangents) }
|
||||||
fun moveTo(position: Vector2, updateTangents: Boolean = true) = wrap { movedBy(position - this.position, updateTangents) }
|
fun moveTo(position: Vector2, updateTangents: Boolean = true) = wrap { movedBy(position - this.position, updateTangents) }
|
||||||
fun rotate(rotationInDegrees: Double) = wrap { rotatedBy(rotationInDegrees) }
|
fun rotate(rotationInDegrees: Double) = wrap { rotatedBy(rotationInDegrees) }
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ import org.openrndr.shape.SegmentType
|
|||||||
import org.openrndr.shape.ShapeContour
|
import org.openrndr.shape.ShapeContour
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
internal fun Vector2.transformedBy(t: Matrix44) = (t * (this.xy01)).xy
|
internal fun Vector2.transformedBy(t: Matrix44, mask: Int = 0x0f, maskRef: Int = 0x0f) =
|
||||||
|
if ((mask and maskRef) != 0)
|
||||||
|
(t * (this.xy01)).xy else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
fun <E> List<E>.update(vararg updates: Pair<Int, E>): List<E> {
|
fun <E> List<E>.update(vararg updates: Pair<Int, E>): List<E> {
|
||||||
if (updates.isEmpty()) {
|
if (updates.isEmpty()) {
|
||||||
return this
|
return this
|
||||||
@@ -209,16 +214,36 @@ data class ContourEdge(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class ControlMask(val mask: Int) {
|
||||||
|
START(1),
|
||||||
|
CONTROL0(2),
|
||||||
|
CONTROL1(4),
|
||||||
|
END(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun maskOf(vararg control: ControlMask): Int {
|
||||||
|
var mask = 0
|
||||||
|
for (c in control) {
|
||||||
|
mask = mask or c.mask
|
||||||
|
}
|
||||||
|
return mask
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* apply [transform] to the edge
|
* apply [transform] to the edge
|
||||||
* @param transform a [Matrix44]
|
* @param transform a [Matrix44]
|
||||||
*/
|
*/
|
||||||
fun transformedBy(transform: Matrix44, updateTangents: Boolean = true): ContourEdge {
|
fun transformedBy(
|
||||||
val segment = contour.segments[segmentIndex]
|
transform: Matrix44,
|
||||||
|
updateTangents: Boolean = true,
|
||||||
|
mask: Int = 0xf,
|
||||||
|
promoteToCubic: Boolean = false
|
||||||
|
): ContourEdge {
|
||||||
|
val segment = contour.segments[segmentIndex].let { if (promoteToCubic) it.cubic else it }
|
||||||
val newSegment = segment.copy(
|
val newSegment = segment.copy(
|
||||||
start = segment.start.transformedBy(transform),
|
start = segment.start.transformedBy(transform, mask, ControlMask.START.mask),
|
||||||
control = segment.control.map { it.transformedBy(transform) },
|
control = segment.control.mapIndexed { index, it -> it.transformedBy(transform, mask, 1 shl (index + 1)) },
|
||||||
end = segment.end.transformedBy(transform)
|
end = segment.end.transformedBy(transform, mask, ControlMask.END.mask)
|
||||||
)
|
)
|
||||||
val segmentInIndex = if (contour.closed) (segmentIndex - 1).mod(contour.segments.size) else segmentIndex - 1
|
val segmentInIndex = if (contour.closed) (segmentIndex - 1).mod(contour.segments.size) else segmentIndex - 1
|
||||||
val segmentOutIndex = if (contour.closed) (segmentIndex + 1).mod(contour.segments.size) else segmentIndex + 1
|
val segmentOutIndex = if (contour.closed) (segmentIndex + 1).mod(contour.segments.size) else segmentIndex + 1
|
||||||
@@ -255,6 +280,25 @@ data class ContourEdge(
|
|||||||
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex)
|
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startMovedBy(translation: Vector2, updateTangents: Boolean = true): ContourEdge =
|
||||||
|
transformedBy(buildTransform {
|
||||||
|
translate(translation)
|
||||||
|
}, updateTangents = updateTangents, mask = maskOf(ControlMask.START))
|
||||||
|
|
||||||
|
fun control0MovedBy(translation: Vector2): ContourEdge = transformedBy(buildTransform {
|
||||||
|
translate(translation)
|
||||||
|
}, updateTangents = false, mask = maskOf(ControlMask.CONTROL0), promoteToCubic = true)
|
||||||
|
|
||||||
|
fun control1MovedBy(translation: Vector2): ContourEdge = transformedBy(buildTransform {
|
||||||
|
translate(translation)
|
||||||
|
}, updateTangents = false, mask = maskOf(ControlMask.CONTROL1), promoteToCubic = true)
|
||||||
|
|
||||||
|
fun endMovedBy(translation: Vector2, updateTangents: Boolean = true): ContourEdge {
|
||||||
|
return transformedBy(buildTransform {
|
||||||
|
translate(translation)
|
||||||
|
}, updateTangents = updateTangents, mask = maskOf(ControlMask.END))
|
||||||
|
}
|
||||||
|
|
||||||
fun movedBy(translation: Vector2, updateTangents: Boolean = true): ContourEdge {
|
fun movedBy(translation: Vector2, updateTangents: Boolean = true): ContourEdge {
|
||||||
return transformedBy(buildTransform {
|
return transformedBy(buildTransform {
|
||||||
translate(translation)
|
translate(translation)
|
||||||
|
|||||||
@@ -47,6 +47,33 @@ data class ContourVertex(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val controlIn: Vector2?
|
||||||
|
get() {
|
||||||
|
return if (contour.closed || (segmentIndex > 0 && segmentIndex < contour.segments.size)) {
|
||||||
|
contour.segments[(segmentIndex-1).mod(contour.segments.size)].cubic.control[1]
|
||||||
|
} else if (segmentIndex == 0) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
contour.segments[segmentIndex-1].cubic.control[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val controlOut: Vector2?
|
||||||
|
get() {
|
||||||
|
return if (contour.closed || segmentIndex < contour.segments.size) {
|
||||||
|
contour.segments[segmentIndex].cubic.control[0]
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val tangentIn: Vector2?
|
||||||
|
get() = controlIn?.minus(position)
|
||||||
|
|
||||||
|
val tangentOut: Vector2?
|
||||||
|
get() = controlOut?.minus(position)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun remove(updateTangents: Boolean = true): ContourVertex {
|
fun remove(updateTangents: Boolean = true): ContourVertex {
|
||||||
if (contour.empty) {
|
if (contour.empty) {
|
||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
@@ -67,6 +94,27 @@ data class ContourVertex(
|
|||||||
return ContourVertex(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex, adjustments)
|
return ContourVertex(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex, adjustments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun controlInMovedBy(translation: Vector2): ContourVertex {
|
||||||
|
if (contour.empty) {
|
||||||
|
return withoutAdjustments()
|
||||||
|
}
|
||||||
|
val transform = buildTransform {
|
||||||
|
translate(translation)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformTangents(transform, Matrix44.IDENTITY)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun controlOutMovedBy(translation: Vector2): ContourVertex {
|
||||||
|
if (contour.empty) {
|
||||||
|
return withoutAdjustments()
|
||||||
|
}
|
||||||
|
val transform = buildTransform {
|
||||||
|
translate(translation)
|
||||||
|
}
|
||||||
|
return transformTangents(Matrix44.IDENTITY, transform)
|
||||||
|
}
|
||||||
|
|
||||||
fun scaledBy(scaleFactor: Double): ContourVertex {
|
fun scaledBy(scaleFactor: Double): ContourVertex {
|
||||||
if (contour.empty) {
|
if (contour.empty) {
|
||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
@@ -96,13 +144,16 @@ data class ContourVertex(
|
|||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
}
|
}
|
||||||
val newSegments = contour.segments.map { it }.toMutableList()
|
val newSegments = contour.segments.map { it }.toMutableList()
|
||||||
val refOut = contour.segments[segmentIndex]
|
val refOut = contour.segments.getOrNull(segmentIndex)
|
||||||
val refIn = if (contour.closed) contour.segments[(segmentIndex - 1).mod(contour.segments.size)] else
|
val refIn = if (contour.closed) contour.segments[(segmentIndex - 1).mod(contour.segments.size)] else
|
||||||
contour.segments.getOrNull(segmentIndex - 1)
|
contour.segments.getOrNull(segmentIndex - 1)
|
||||||
newSegments[segmentIndex] = run {
|
|
||||||
val cubicSegment = refOut.cubic
|
if (refOut != null) {
|
||||||
val newControls = listOf((transformOut * cubicSegment.control[0].xy01).xy, cubicSegment.control[1])
|
newSegments[segmentIndex] = run {
|
||||||
refOut.copy(control = newControls)
|
val cubicSegment = refOut.cubic
|
||||||
|
val newControls = listOf((transformOut * cubicSegment.control[0].xy01).xy, cubicSegment.control[1])
|
||||||
|
refOut.copy(control = newControls)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val segmentIndexIn = (segmentIndex - 1).mod(contour.segments.size)
|
val segmentIndexIn = (segmentIndex - 1).mod(contour.segments.size)
|
||||||
if (refIn != null) {
|
if (refIn != null) {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.openrndr.extra.shapes.adjust.extensions
|
||||||
|
|
||||||
|
import org.openrndr.extra.shapes.adjust.ContourAdjusterVertex
|
||||||
|
import org.openrndr.extra.shapes.vertex.ContourVertex
|
||||||
|
|
||||||
|
fun ContourVertex.tangentsAveraged(): ContourVertex {
|
||||||
|
if (contour.empty || tangentIn == null || tangentOut == null) return withoutAdjustments()
|
||||||
|
|
||||||
|
val sum = (tangentIn!! - tangentOut!!).normalized
|
||||||
|
val lengthIn = tangentIn!!.length
|
||||||
|
val lengthOut = tangentOut!!.length
|
||||||
|
|
||||||
|
val positionIn = position + sum * lengthIn
|
||||||
|
val positionOut = position - sum * lengthOut
|
||||||
|
|
||||||
|
return controlOutMovedBy(positionOut - controlOut!!).controlInMovedBy(positionIn - controlIn!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Average the in and out tangents
|
||||||
|
*/
|
||||||
|
fun ContourAdjusterVertex.averageTangents() = wrap { tangentsAveraged() }
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.openrndr.extra.shapes.adjust.extensions
|
||||||
|
|
||||||
|
import org.openrndr.extra.shapes.adjust.ContourAdjusterVertex
|
||||||
|
import org.openrndr.extra.shapes.vertex.ContourVertex
|
||||||
|
import org.openrndr.math.transforms.buildTransform
|
||||||
|
|
||||||
|
fun ContourVertex.tangentInReflectedToOut(tangentScale: Double = 1.0): ContourVertex {
|
||||||
|
if (contour.empty || tangentIn == null || tangentOut == null) return withoutAdjustments()
|
||||||
|
return controlOutMovedBy(position - tangentIn!! * tangentScale - controlOut!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ContourVertex.tangentOutReflectedToIn(tangentScale: Double = 1.0): ContourVertex {
|
||||||
|
if (contour.empty || tangentIn == null || tangentOut == null) return withoutAdjustments()
|
||||||
|
return controlInMovedBy(position - tangentOut!! * tangentScale - controlIn!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ContourVertex.switchedTangents(preserveLength: Boolean = false): ContourVertex {
|
||||||
|
if (contour.empty || tangentIn == null || tangentOut == null) return withoutAdjustments()
|
||||||
|
|
||||||
|
val sIn = if (preserveLength) tangentIn!!.length / tangentOut!!.length else 1.0
|
||||||
|
val sOut = if (preserveLength) 1.0 / sIn else 1.0
|
||||||
|
|
||||||
|
val newControlIn = position + tangentOut!! * sIn
|
||||||
|
val newControlOut = position + tangentIn!! * sOut
|
||||||
|
|
||||||
|
return transformTangents(
|
||||||
|
buildTransform { translate(newControlIn - controlIn!!) },
|
||||||
|
buildTransform { translate(newControlOut - controlOut!!) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch in and out tangents
|
||||||
|
*/
|
||||||
|
fun ContourAdjusterVertex.switchTangents(preserveLength: Boolean = false) = wrap { switchedTangents(preserveLength) }
|
||||||
|
|
||||||
|
|
||||||
|
fun ContourAdjusterVertex.reflectTangentInToOut(tangentScale: Double) = wrap { tangentInReflectedToOut(tangentScale) }
|
||||||
|
|
||||||
|
fun ContourAdjusterVertex.reflectTangentOutToIn(tangentScale: Double) = wrap { tangentOutReflectedToIn(tangentScale) }
|
||||||
@@ -58,6 +58,7 @@ fun Segment.withTunniPoint(tunniPoint: Vector2): Segment {
|
|||||||
* @since orx 0.4.5
|
* @since orx 0.4.5
|
||||||
*/
|
*/
|
||||||
fun Segment.withTunniLine(pointOnLine: Vector2): Segment {
|
fun Segment.withTunniLine(pointOnLine: Vector2): Segment {
|
||||||
|
println("hoi! $pointOnLine")
|
||||||
val ls = LineSegment(pointOnLine, pointOnLine + this.cubic.control[0] - this.cubic.control[1])
|
val ls = LineSegment(pointOnLine, pointOnLine + this.cubic.control[0] - this.cubic.control[1])
|
||||||
val ac0 = LineSegment(start, this.cubic.control[0])
|
val ac0 = LineSegment(start, this.cubic.control[0])
|
||||||
val bc1 = LineSegment(end, this.cubic.control[1])
|
val bc1 = LineSegment(end, this.cubic.control[1])
|
||||||
@@ -66,6 +67,10 @@ fun Segment.withTunniLine(pointOnLine: Vector2): Segment {
|
|||||||
val cp1 = intersection(ls, bc1, Double.POSITIVE_INFINITY)
|
val cp1 = intersection(ls, bc1, Double.POSITIVE_INFINITY)
|
||||||
|
|
||||||
return if (cp0 != Vector2.INFINITY && cp1 != Vector2.INFINITY) {
|
return if (cp0 != Vector2.INFINITY && cp1 != Vector2.INFINITY) {
|
||||||
|
println("$cp0 $cp1")
|
||||||
copy(start, listOf(cp0, cp1), end)
|
copy(start, listOf(cp0, cp1), end)
|
||||||
} else this
|
} else {
|
||||||
|
println("${cp0} ${cp1}")
|
||||||
|
this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ fun ContourEdge.withTunniLine(pointOnLine: Vector2): ContourEdge {
|
|||||||
if (contour.empty) {
|
if (contour.empty) {
|
||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
} else {
|
} else {
|
||||||
val segment = contour.segments[segmentIndex].withTunniPoint(pointOnLine)
|
val segment = contour.segments[segmentIndex].withTunniLine(pointOnLine)
|
||||||
val newSegments = contour.segments.map { it }.toMutableList()
|
val newSegments = contour.segments.map { it }.toMutableList()
|
||||||
newSegments[segmentIndex] = segment
|
newSegments[segmentIndex] = segment
|
||||||
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex)
|
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
52
orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour08.kt
Normal file
52
orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour08.kt
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
|
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.contour
|
||||||
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
application {
|
||||||
|
configure {
|
||||||
|
width = 800
|
||||||
|
height = 800
|
||||||
|
}
|
||||||
|
program {
|
||||||
|
extend {
|
||||||
|
var contour = contour {
|
||||||
|
moveTo(drawer.bounds.position(0.5, 0.1) - Vector2(300.0, 0.0))
|
||||||
|
lineTo(drawer.bounds.position(0.5, 0.1) + Vector2(300.0, 0.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
contour = adjustContour(contour) {
|
||||||
|
selectVertex(0)
|
||||||
|
vertex.moveControlOutBy(Vector2(0.0, 100.0))
|
||||||
|
|
||||||
|
selectVertex(1)
|
||||||
|
vertex.moveControlInBy(Vector2(0.0, -100.0))
|
||||||
|
|
||||||
|
}
|
||||||
|
drawer.stroke = ColorRGBa.RED
|
||||||
|
drawer.contour(contour)
|
||||||
|
|
||||||
|
|
||||||
|
contour = contour {
|
||||||
|
moveTo(drawer.bounds.position(0.5, 0.2) - Vector2(300.0, 0.0))
|
||||||
|
lineTo(drawer.bounds.position(0.5, 0.2) + Vector2(300.0, 0.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
contour = adjustContour(contour) {
|
||||||
|
selectEdge(0)
|
||||||
|
edge.moveControl0By(Vector2(0.0, 100.0))
|
||||||
|
edge.moveControl1By(Vector2(0.0, -100.0))
|
||||||
|
|
||||||
|
}
|
||||||
|
drawer.stroke = ColorRGBa.RED
|
||||||
|
drawer.contour(contour)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour09.kt
Normal file
60
orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour09.kt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
|
import org.openrndr.application
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
|
import org.openrndr.extra.shapes.adjust.extensions.averageTangents
|
||||||
|
import org.openrndr.extra.shapes.adjust.extensions.switchTangents
|
||||||
|
import org.openrndr.extra.shapes.tunni.tunniLine
|
||||||
|
import org.openrndr.extra.shapes.tunni.tunniPoint
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
application {
|
||||||
|
configure {
|
||||||
|
width = 800
|
||||||
|
height = 800
|
||||||
|
}
|
||||||
|
program {
|
||||||
|
extend {
|
||||||
|
drawer.clear(ColorRGBa.WHITE)
|
||||||
|
var contour = drawer.bounds.offsetEdges(-200.0).contour
|
||||||
|
|
||||||
|
drawer.fill = null
|
||||||
|
|
||||||
|
contour = adjustContour(contour) {
|
||||||
|
selectVertices(0, 1)
|
||||||
|
for (v in vertices) {
|
||||||
|
v.averageTangents()
|
||||||
|
v.scale(sqrt(2.0))
|
||||||
|
v.rotate(45.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectVertices(2)
|
||||||
|
for (v in vertices) {
|
||||||
|
v.switchTangents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawer.stroke = ColorRGBa.BLACK
|
||||||
|
drawer.contour(contour)
|
||||||
|
|
||||||
|
drawer.stroke = ColorRGBa.RED
|
||||||
|
|
||||||
|
for (s in contour.segments) {
|
||||||
|
drawer.lineSegment(s.start, s.cubic.control[0])
|
||||||
|
drawer.lineSegment(s.end, s.cubic.control[1])
|
||||||
|
}
|
||||||
|
drawer.fill = ColorRGBa.BLACK
|
||||||
|
drawer.stroke = null
|
||||||
|
drawer.circles(contour.segments.map { it.start }, 5.0)
|
||||||
|
|
||||||
|
drawer.stroke = ColorRGBa.GRAY
|
||||||
|
for (s in contour.segments) {
|
||||||
|
drawer.lineSegment(s.tunniLine)
|
||||||
|
drawer.fill = ColorRGBa.CYAN
|
||||||
|
drawer.circle(s.tunniPoint, 5.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
//package adjust
|
||||||
|
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
|
import org.openrndr.application
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.extra.shapes.adjust.adjustContourSequence
|
||||||
|
import org.openrndr.extra.shapes.tunni.tunniLine
|
||||||
|
import org.openrndr.extra.shapes.tunni.tunniPoint
|
||||||
|
import org.openrndr.extra.shapes.tunni.withTunniLine
|
||||||
|
import org.openrndr.launch
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
import org.openrndr.shape.Segment
|
||||||
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
application {
|
||||||
|
configure {
|
||||||
|
width = 800
|
||||||
|
height = 800
|
||||||
|
}
|
||||||
|
program {
|
||||||
|
|
||||||
|
var res = drawer.bounds.offsetEdges(-200.0).contour
|
||||||
|
var selectedSegments = emptyList<Segment>()
|
||||||
|
var selectedPoints = emptyList<Vector2>()
|
||||||
|
|
||||||
|
val contourSeq = adjustContourSequence(res) {
|
||||||
|
|
||||||
|
sequence {
|
||||||
|
for (i in 0 until 1000) {
|
||||||
|
selectEdges()
|
||||||
|
selectVertices((i*7).mod(4))
|
||||||
|
for (v in vertices) {
|
||||||
|
for (j in 0 until 30) {
|
||||||
|
v.rotate(45.0/30.0)
|
||||||
|
yield(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectVertices((i*3).mod(4))
|
||||||
|
for (v in vertices) {
|
||||||
|
yield(status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
selectVertices()
|
||||||
|
selectEdges(i.mod(4))
|
||||||
|
for (j in 0 until 30) {
|
||||||
|
for (e in edges) {
|
||||||
|
e.withTunniLine(e.tunniLine.position(0.5) + e.tunniLine.normal * cos(i.toDouble() + e.segmentIndex()) * 50.0/30.0)
|
||||||
|
yield(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
launch {
|
||||||
|
for (i in contourSeq) {
|
||||||
|
res = i.contour
|
||||||
|
selectedPoints = i.selectedPoints
|
||||||
|
selectedSegments = i.selectedSegments
|
||||||
|
yield()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extend {
|
||||||
|
drawer.clear(ColorRGBa.WHITE)
|
||||||
|
drawer.stroke = ColorRGBa.BLACK
|
||||||
|
drawer.contour(res)
|
||||||
|
|
||||||
|
drawer.stroke = ColorRGBa.RED
|
||||||
|
|
||||||
|
for (s in res.segments) {
|
||||||
|
drawer.lineSegment(s.start, s.cubic.control[0])
|
||||||
|
drawer.lineSegment(s.end, s.cubic.control[1])
|
||||||
|
}
|
||||||
|
drawer.fill = ColorRGBa.BLACK
|
||||||
|
drawer.stroke = null
|
||||||
|
drawer.circles(res.segments.map { it.start }, 5.0)
|
||||||
|
|
||||||
|
drawer.stroke = ColorRGBa.GRAY
|
||||||
|
for (s in res.segments) {
|
||||||
|
drawer.lineSegment(s.tunniLine)
|
||||||
|
drawer.fill = ColorRGBa.CYAN
|
||||||
|
drawer.circle(s.tunniPoint, 5.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawer.stroke = ColorRGBa.MAGENTA
|
||||||
|
drawer.strokeWeight = 3.0
|
||||||
|
for (s in selectedSegments) {
|
||||||
|
drawer.segment(s)
|
||||||
|
}
|
||||||
|
for (p in selectedPoints) {
|
||||||
|
drawer.circle(p, 5.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ import kotlin.math.cos
|
|||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
|
|
||||||
class TestAdjustContour {
|
class TestAdjustContour {
|
||||||
@@ -25,6 +27,31 @@ class TestAdjustContour {
|
|||||||
assertEquals(150.0, adjusted.segments[0].end.y, 1E-6)
|
assertEquals(150.0, adjusted.segments[0].end.y, 1E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSingleLinearSegmentMoveControlIn() {
|
||||||
|
val adjusted = adjustContour(LineSegment(0.0, 0.0, 100.0, 100.0).contour) {
|
||||||
|
selectVertex(0)
|
||||||
|
for (v in vertices) {
|
||||||
|
assertNull(v.controlIn)
|
||||||
|
assertNotNull(v.controlOut)
|
||||||
|
v.moveControlOutBy(Vector2(20.0, 20.0))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
selectVertex(1)
|
||||||
|
for (v in vertices) {
|
||||||
|
assertNull(v.controlOut)
|
||||||
|
assertNotNull(v.controlIn)
|
||||||
|
v.moveControlInBy(Vector2(20.0, 20.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSingleLinearSegmentDefaultVertexSelection() {
|
fun testSingleLinearSegmentDefaultVertexSelection() {
|
||||||
val adjusted = adjustContour(LineSegment(0.0, 0.0, 100.0, 100.0).contour) {
|
val adjusted = adjustContour(LineSegment(0.0, 0.0, 100.0, 100.0).contour) {
|
||||||
|
|||||||
Reference in New Issue
Block a user