From 44824e3d213ba8eaa0045c3673f3dd2ada31f748 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Mon, 22 Jan 2024 15:22:20 +0100 Subject: [PATCH] [orx-shapes] Improve contour adjuster framework --- orx-shapes/README.md | 15 +++ orx-shapes/build.gradle.kts | 1 + orx-shapes/src/commonMain/kotlin/Circle.kt | 2 + .../kotlin/adjust/ContourAdjuster.kt | 30 +++++- .../kotlin/adjust/ContourAdjusterEdge.kt | 9 ++ .../kotlin/adjust/ContourAdjusterVertex.kt | 14 ++- .../commonMain/kotlin/adjust/ContourEdge.kt | 56 ++++++++-- .../commonMain/kotlin/adjust/ContourVertex.kt | 61 ++++++++++- .../adjust/extensions/TangentAverage.kt | 22 ++++ .../adjust/extensions/TangentReflection.kt | 39 +++++++ .../src/commonMain/kotlin/tunni/Tunni.kt | 7 +- .../tunni/TunniContourEdgeExtensions.kt | 2 +- .../{ => adjust}/DemoAdjustContour01.kt | 2 + .../{ => adjust}/DemoAdjustContour02.kt | 2 + .../{ => adjust}/DemoAdjustContour03.kt | 2 + .../{ => adjust}/DemoAdjustContour04.kt | 2 + .../{ => adjust}/DemoAdjustContour05.kt | 2 + .../{ => adjust}/DemoAdjustContour06.kt | 2 + .../{ => adjust}/DemoAdjustContour07.kt | 2 + .../kotlin/adjust/DemoAdjustContour08.kt | 52 +++++++++ .../kotlin/adjust/DemoAdjustContour09.kt | 60 +++++++++++ .../adjust/DemoAdjustContourContinue01.kt | 102 ++++++++++++++++++ .../src/jvmTest/kotlin/TestAdjustContour.kt | 27 +++++ 23 files changed, 498 insertions(+), 15 deletions(-) create mode 100644 orx-shapes/src/commonMain/kotlin/adjust/extensions/TangentAverage.kt create mode 100644 orx-shapes/src/commonMain/kotlin/adjust/extensions/TangentReflection.kt rename orx-shapes/src/jvmDemo/kotlin/{ => adjust}/DemoAdjustContour01.kt (98%) rename orx-shapes/src/jvmDemo/kotlin/{ => adjust}/DemoAdjustContour02.kt (98%) rename orx-shapes/src/jvmDemo/kotlin/{ => adjust}/DemoAdjustContour03.kt (98%) rename orx-shapes/src/jvmDemo/kotlin/{ => adjust}/DemoAdjustContour04.kt (98%) rename orx-shapes/src/jvmDemo/kotlin/{ => adjust}/DemoAdjustContour05.kt (98%) rename orx-shapes/src/jvmDemo/kotlin/{ => adjust}/DemoAdjustContour06.kt (98%) rename orx-shapes/src/jvmDemo/kotlin/{ => adjust}/DemoAdjustContour07.kt (98%) create mode 100644 orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour08.kt create mode 100644 orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour09.kt create mode 100644 orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContourContinue01.kt diff --git a/orx-shapes/README.md b/orx-shapes/README.md index d3a30770..bce588e7 100644 --- a/orx-shapes/README.md +++ b/orx-shapes/README.md @@ -39,6 +39,21 @@ Collection of 2D shape generators and modifiers. ![DemoAdjustContour07Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-shapes/images/DemoAdjustContour07Kt.png) +### DemoAdjustContour08 +[source code](src/jvmDemo/kotlin/DemoAdjustContour08.kt) + +![DemoAdjustContour08Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-shapes/images/DemoAdjustContour08Kt.png) + +### DemoAdjustContour09 +[source code](src/jvmDemo/kotlin/DemoAdjustContour09.kt) + +![DemoAdjustContour09Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-shapes/images/DemoAdjustContour09Kt.png) + +### DemoAdjustContourContinue01 +[source code](src/jvmDemo/kotlin/DemoAdjustContourContinue01.kt) + +![DemoAdjustContourContinue01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-shapes/images/DemoAdjustContourContinue01Kt.png) + ### DemoAlphaShape [source code](src/jvmDemo/kotlin/DemoAlphaShape.kt) diff --git a/orx-shapes/build.gradle.kts b/orx-shapes/build.gradle.kts index 96e19e61..8f8a95fc 100644 --- a/orx-shapes/build.gradle.kts +++ b/orx-shapes/build.gradle.kts @@ -15,6 +15,7 @@ kotlin { implementation(libs.openrndr.draw) implementation(libs.openrndr.filter) implementation(libs.kotlin.reflect) + implementation(libs.kotlin.coroutines) } } diff --git a/orx-shapes/src/commonMain/kotlin/Circle.kt b/orx-shapes/src/commonMain/kotlin/Circle.kt index df96eda5..8babb224 100644 --- a/orx-shapes/src/commonMain/kotlin/Circle.kt +++ b/orx-shapes/src/commonMain/kotlin/Circle.kt @@ -1,3 +1,5 @@ +package org.openrndr.extra.shapes + import org.openrndr.math.Polar import org.openrndr.math.Vector2 import org.openrndr.math.transforms.buildTransform diff --git a/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjuster.kt b/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjuster.kt index 773f0391..dbb258ec 100644 --- a/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjuster.kt +++ b/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjuster.kt @@ -2,7 +2,17 @@ package org.openrndr.extra.shapes.adjust import org.openrndr.collections.pop import org.openrndr.extra.shapes.vertex.ContourVertex +import org.openrndr.math.Vector2 +import org.openrndr.shape.Segment import org.openrndr.shape.ShapeContour +import kotlin.jvm.JvmName + + +class ContourAdjusterStatus( + val contour: ShapeContour, + val selectedSegments: List, + val selectedPoints: List +) /** @@ -17,6 +27,7 @@ class ContourAdjuster(var contour: ShapeContour) { var clearSelectedEdges: Boolean = false, var clearSelectedVertices: Boolean = false ) + var parameters = Parameters() val parameterStack = ArrayDeque() @@ -75,6 +86,15 @@ class ContourAdjuster(var contour: ShapeContour) { 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 */ @@ -149,7 +169,6 @@ class ContourAdjuster(var contour: ShapeContour) { } - /** * select multiple vertices using an index-vertex based [predicate] */ @@ -272,4 +291,13 @@ fun adjustContour(contour: ShapeContour, adjuster: ContourAdjuster.() -> Unit): val ca = ContourAdjuster(contour) ca.apply(adjuster) return ca.contour +} + +@JvmName("adjustContourSequenceStatus") +fun adjustContourSequence( + contour: ShapeContour, + adjuster: ContourAdjuster.() -> Sequence +): Sequence { + val ca = ContourAdjuster(contour) + return ca.adjuster() } \ No newline at end of file diff --git a/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterEdge.kt b/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterEdge.kt index 61f746ae..45083656 100644 --- a/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterEdge.kt +++ b/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterEdge.kt @@ -105,4 +105,13 @@ data class ContourAdjusterEdge(val contourAdjuster: ContourAdjuster, val segment .subbed(t0, t1) .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) } + + } diff --git a/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterVertex.kt b/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterVertex.kt index 6f0f8414..50c55f96 100644 --- a/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterVertex.kt +++ b/orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterVertex.kt @@ -4,7 +4,7 @@ import org.openrndr.extra.shapes.vertex.ContourVertex import org.openrndr.math.Vector2 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() contourAdjuster.contour = newVertex.contour contourAdjuster.updateSelection(newVertex.adjustments) @@ -43,7 +43,19 @@ class ContourAdjusterVertex(val contourAdjuster: ContourAdjuster, val segmentInd 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 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 moveTo(position: Vector2, updateTangents: Boolean = true) = wrap { movedBy(position - this.position, updateTangents) } fun rotate(rotationInDegrees: Double) = wrap { rotatedBy(rotationInDegrees) } diff --git a/orx-shapes/src/commonMain/kotlin/adjust/ContourEdge.kt b/orx-shapes/src/commonMain/kotlin/adjust/ContourEdge.kt index dafa6c82..3b5405a9 100644 --- a/orx-shapes/src/commonMain/kotlin/adjust/ContourEdge.kt +++ b/orx-shapes/src/commonMain/kotlin/adjust/ContourEdge.kt @@ -11,7 +11,12 @@ import org.openrndr.shape.SegmentType import org.openrndr.shape.ShapeContour 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 List.update(vararg updates: Pair): List { if (updates.isEmpty()) { 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 * @param transform a [Matrix44] */ - fun transformedBy(transform: Matrix44, updateTangents: Boolean = true): ContourEdge { - val segment = contour.segments[segmentIndex] + fun transformedBy( + 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( - start = segment.start.transformedBy(transform), - control = segment.control.map { it.transformedBy(transform) }, - end = segment.end.transformedBy(transform) + start = segment.start.transformedBy(transform, mask, ControlMask.START.mask), + control = segment.control.mapIndexed { index, it -> it.transformedBy(transform, mask, 1 shl (index + 1)) }, + end = segment.end.transformedBy(transform, mask, ControlMask.END.mask) ) 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 @@ -255,6 +280,25 @@ data class ContourEdge( 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 { return transformedBy(buildTransform { translate(translation) diff --git a/orx-shapes/src/commonMain/kotlin/adjust/ContourVertex.kt b/orx-shapes/src/commonMain/kotlin/adjust/ContourVertex.kt index 19a7b64a..dc7408c6 100644 --- a/orx-shapes/src/commonMain/kotlin/adjust/ContourVertex.kt +++ b/orx-shapes/src/commonMain/kotlin/adjust/ContourVertex.kt @@ -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 { if (contour.empty) { return withoutAdjustments() @@ -67,6 +94,27 @@ data class ContourVertex( 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 { if (contour.empty) { return withoutAdjustments() @@ -96,13 +144,16 @@ data class ContourVertex( return withoutAdjustments() } 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 contour.segments.getOrNull(segmentIndex - 1) - newSegments[segmentIndex] = run { - val cubicSegment = refOut.cubic - val newControls = listOf((transformOut * cubicSegment.control[0].xy01).xy, cubicSegment.control[1]) - refOut.copy(control = newControls) + + if (refOut != null) { + newSegments[segmentIndex] = run { + 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) if (refIn != null) { diff --git a/orx-shapes/src/commonMain/kotlin/adjust/extensions/TangentAverage.kt b/orx-shapes/src/commonMain/kotlin/adjust/extensions/TangentAverage.kt new file mode 100644 index 00000000..0c5bf3d7 --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/adjust/extensions/TangentAverage.kt @@ -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() } diff --git a/orx-shapes/src/commonMain/kotlin/adjust/extensions/TangentReflection.kt b/orx-shapes/src/commonMain/kotlin/adjust/extensions/TangentReflection.kt new file mode 100644 index 00000000..be13bb41 --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/adjust/extensions/TangentReflection.kt @@ -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) } diff --git a/orx-shapes/src/commonMain/kotlin/tunni/Tunni.kt b/orx-shapes/src/commonMain/kotlin/tunni/Tunni.kt index 7df6b889..8fadb99d 100644 --- a/orx-shapes/src/commonMain/kotlin/tunni/Tunni.kt +++ b/orx-shapes/src/commonMain/kotlin/tunni/Tunni.kt @@ -58,6 +58,7 @@ fun Segment.withTunniPoint(tunniPoint: Vector2): Segment { * @since orx 0.4.5 */ fun Segment.withTunniLine(pointOnLine: Vector2): Segment { + println("hoi! $pointOnLine") val ls = LineSegment(pointOnLine, pointOnLine + this.cubic.control[0] - this.cubic.control[1]) val ac0 = LineSegment(start, this.cubic.control[0]) 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) return if (cp0 != Vector2.INFINITY && cp1 != Vector2.INFINITY) { + println("$cp0 $cp1") copy(start, listOf(cp0, cp1), end) - } else this + } else { + println("${cp0} ${cp1}") + this + } } \ No newline at end of file diff --git a/orx-shapes/src/commonMain/kotlin/tunni/TunniContourEdgeExtensions.kt b/orx-shapes/src/commonMain/kotlin/tunni/TunniContourEdgeExtensions.kt index 2e2ced8a..f6927d80 100644 --- a/orx-shapes/src/commonMain/kotlin/tunni/TunniContourEdgeExtensions.kt +++ b/orx-shapes/src/commonMain/kotlin/tunni/TunniContourEdgeExtensions.kt @@ -44,7 +44,7 @@ fun ContourEdge.withTunniLine(pointOnLine: Vector2): ContourEdge { if (contour.empty) { return withoutAdjustments() } else { - val segment = contour.segments[segmentIndex].withTunniPoint(pointOnLine) + val segment = contour.segments[segmentIndex].withTunniLine(pointOnLine) val newSegments = contour.segments.map { it }.toMutableList() newSegments[segmentIndex] = segment return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex) diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour01.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour01.kt similarity index 98% rename from orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour01.kt rename to orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour01.kt index 9034a90a..1f934636 100644 --- a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour01.kt +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour01.kt @@ -1,3 +1,5 @@ +//package adjust + import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.extra.shapes.adjust.adjustContour diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour02.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour02.kt similarity index 98% rename from orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour02.kt rename to orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour02.kt index 0ba783cb..51d1ec7c 100644 --- a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour02.kt +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour02.kt @@ -1,3 +1,5 @@ +//package adjust + import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.extra.shapes.adjust.adjustContour diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour03.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour03.kt similarity index 98% rename from orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour03.kt rename to orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour03.kt index 842394a2..b1b59994 100644 --- a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour03.kt +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour03.kt @@ -1,3 +1,5 @@ +//package adjust + import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.extra.shapes.adjust.adjustContour diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour04.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour04.kt similarity index 98% rename from orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour04.kt rename to orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour04.kt index 1cd4fef9..8fffabe1 100644 --- a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour04.kt +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour04.kt @@ -1,3 +1,5 @@ +//package adjust + import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.extra.shapes.adjust.adjustContour diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour05.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour05.kt similarity index 98% rename from orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour05.kt rename to orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour05.kt index 6a58392b..6f2522df 100644 --- a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour05.kt +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour05.kt @@ -1,3 +1,5 @@ +//package adjust + import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.extra.shapes.adjust.adjustContour diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour06.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour06.kt similarity index 98% rename from orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour06.kt rename to orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour06.kt index dfc2155b..40109799 100644 --- a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour06.kt +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour06.kt @@ -1,3 +1,5 @@ +//package adjust + import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.extra.shapes.adjust.adjustContour diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour07.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour07.kt similarity index 98% rename from orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour07.kt rename to orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour07.kt index ae0a1c01..6ccf1157 100644 --- a/orx-shapes/src/jvmDemo/kotlin/DemoAdjustContour07.kt +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour07.kt @@ -1,3 +1,5 @@ +//package adjust + import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.extra.shapes.adjust.adjustContour diff --git a/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour08.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour08.kt new file mode 100644 index 00000000..5a2cb446 --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour08.kt @@ -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) + + } + } + } +} \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour09.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour09.kt new file mode 100644 index 00000000..4cd24c26 --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContour09.kt @@ -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) + } + } + } + } +} \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContourContinue01.kt b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContourContinue01.kt new file mode 100644 index 00000000..f49d765e --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/adjust/DemoAdjustContourContinue01.kt @@ -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() + var selectedPoints = emptyList() + + 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) + } + } + } + } +} \ No newline at end of file diff --git a/orx-shapes/src/jvmTest/kotlin/TestAdjustContour.kt b/orx-shapes/src/jvmTest/kotlin/TestAdjustContour.kt index 8ac90e0e..11675ae2 100644 --- a/orx-shapes/src/jvmTest/kotlin/TestAdjustContour.kt +++ b/orx-shapes/src/jvmTest/kotlin/TestAdjustContour.kt @@ -6,6 +6,8 @@ import kotlin.math.cos import kotlin.math.sin import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull class TestAdjustContour { @@ -25,6 +27,31 @@ class TestAdjustContour { 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 fun testSingleLinearSegmentDefaultVertexSelection() { val adjusted = adjustContour(LineSegment(0.0, 0.0, 100.0, 100.0).contour) {