-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[orx-shapes] Add WIP ContourAdjuster framework
- Loading branch information
Showing
17 changed files
with
940 additions
and
0 deletions.
There are no files selected for viewing
159 changes: 159 additions & 0 deletions
159
orx-shapes/src/commonMain/kotlin/adjust/ContourAdjuster.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package org.openrndr.extra.shapes.adjust | ||
|
||
import org.openrndr.extra.shapes.vertex.ContourVertex | ||
import org.openrndr.shape.ShapeContour | ||
|
||
/** | ||
* Adjusts [ShapeContour] using an accessible interface. | ||
* | ||
* [ContourAdjuster] | ||
*/ | ||
class ContourAdjuster(var contour: ShapeContour) { | ||
/** | ||
* selected vertex indices | ||
*/ | ||
var vertexIndices = listOf(0) | ||
|
||
/** | ||
* selected edge indices | ||
*/ | ||
var edgeIndices = listOf(0) | ||
|
||
/** | ||
* the selected vertex | ||
*/ | ||
val vertex: ContourAdjusterVertex | ||
get() { | ||
return ContourAdjusterVertex(this, vertexIndices.first()) | ||
} | ||
|
||
/** | ||
* the selected edge | ||
*/ | ||
val edge: ContourAdjusterEdge | ||
get() { | ||
return ContourAdjusterEdge(this, edgeIndices.first()) | ||
} | ||
|
||
/** | ||
* select a vertex by index | ||
*/ | ||
fun selectVertex(index: Int) { | ||
vertexIndices = listOf(index) | ||
} | ||
|
||
/** | ||
* deselect a vertex by index | ||
*/ | ||
fun deselectVertex(index: Int) { | ||
vertexIndices = vertexIndices.filter { it != index } | ||
} | ||
|
||
/** | ||
* select multiple vertices | ||
*/ | ||
fun selectVertices(vararg indices: Int) { | ||
vertexIndices = indices.toList().distinct() | ||
} | ||
|
||
/** | ||
* select multiple vertices using an index based [predicate] | ||
*/ | ||
fun selectVertices(predicate: (Int) -> Boolean) { | ||
vertexIndices = | ||
(0 until if (contour.closed) contour.segments.size else contour.segments.size + 1).filter(predicate) | ||
} | ||
|
||
/** | ||
* select multiple vertices using an index-vertex based [predicate] | ||
*/ | ||
fun selectVertices(predicate: (Int, ContourVertex) -> Boolean) { | ||
vertexIndices = | ||
(0 until if (contour.closed) contour.segments.size else contour.segments.size + 1).filter { index -> | ||
predicate(index, ContourVertex(contour, index) ) | ||
} | ||
} | ||
|
||
/** | ||
* select an edge by index | ||
*/ | ||
fun selectEdge(index: Int) { | ||
selectEdges(index) | ||
} | ||
|
||
/** | ||
* select multiple edges by index | ||
*/ | ||
fun selectEdges(vararg indices: Int) { | ||
edgeIndices = indices.toList().distinct() | ||
} | ||
|
||
/** | ||
* select multiple vertices using an index based [predicate] | ||
*/ | ||
fun selectEdges(predicate: (Int) -> Boolean) { | ||
edgeIndices = | ||
contour.segments.indices.filter(predicate) | ||
} | ||
|
||
/** | ||
* select multiple edges using an index-edge based [predicate] | ||
*/ | ||
fun selectEdges(predicate: (Int, ContourEdge) -> Boolean) { | ||
vertexIndices = | ||
(0 until if (contour.closed) contour.segments.size else contour.segments.size + 1).filter { index -> | ||
predicate(index, ContourEdge(contour, index) ) | ||
} | ||
} | ||
|
||
fun updateSelection(adjustments: List<SegmentOperation>) { | ||
var newVertexIndices = vertexIndices | ||
var newEdgeIndices = edgeIndices | ||
|
||
for (adjustment in adjustments) { | ||
when (adjustment) { | ||
is SegmentOperation.Insert -> { | ||
fun insert(list: List<Int>) = list.map { | ||
if (it >= adjustment.index) { | ||
it + adjustment.amount | ||
} else { | ||
it | ||
} | ||
} | ||
newVertexIndices = insert(newVertexIndices) | ||
newEdgeIndices = insert(newEdgeIndices) | ||
} | ||
is SegmentOperation.Remove -> { | ||
// TODO: handling of vertices in open contours is wrong here | ||
newVertexIndices = newVertexIndices.mapNotNull { | ||
if (it in adjustment.index ..< adjustment.index+adjustment.amount) { | ||
null | ||
} else if (it > adjustment.index) { | ||
it - adjustment.amount | ||
} else { | ||
it | ||
} | ||
} | ||
newEdgeIndices = newEdgeIndices.mapNotNull { | ||
if (it in adjustment.index ..< adjustment.index+adjustment.amount) { | ||
null | ||
} else if (it > adjustment.index) { | ||
it - adjustment.amount | ||
} else { | ||
it | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Build a contour adjuster | ||
*/ | ||
fun adjustContour(contour: ShapeContour, adjuster: ContourAdjuster.() -> Unit): ShapeContour { | ||
val ca = ContourAdjuster(contour) | ||
ca.apply(adjuster) | ||
return ca.contour | ||
} |
73 changes: 73 additions & 0 deletions
73
orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterEdge.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package org.openrndr.extra.shapes.adjust | ||
|
||
import org.openrndr.math.Vector2 | ||
|
||
data class ContourAdjusterEdge(val contourAdjuster: ContourAdjuster, val segmentIndex: Int) { | ||
|
||
/** | ||
* A [ContourAdjusterVertex] interface for the start-vertex of the edge | ||
*/ | ||
val start | ||
get() = ContourAdjusterVertex(contourAdjuster, segmentIndex) | ||
|
||
/** | ||
* A [ContourAdjusterVertex] interface for the end-vertex of the edge | ||
*/ | ||
val end | ||
get() = ContourAdjusterVertex(contourAdjuster, (segmentIndex + 1).mod(contourAdjuster.contour.segments.size)) | ||
|
||
/** | ||
* A link to the edge before this edge | ||
*/ | ||
val previous: ContourAdjusterEdge? | ||
get() = if (contourAdjuster.contour.closed) { | ||
this.copy(segmentIndex = (segmentIndex - 1).mod(contourAdjuster.contour.segments.size)) | ||
} else { | ||
if (segmentIndex > 0) { | ||
this.copy(segmentIndex = segmentIndex - 1) | ||
} else { | ||
null | ||
} | ||
} | ||
|
||
/** | ||
* A link to the edge after this edge | ||
*/ | ||
val next: ContourAdjusterEdge? | ||
get() = if (contourAdjuster.contour.closed) { | ||
this.copy(segmentIndex = (segmentIndex + 1).mod(contourAdjuster.contour.segments.size)) | ||
} else { | ||
if (segmentIndex < contourAdjuster.contour.segments.size - 1) { | ||
this.copy(segmentIndex = segmentIndex + 1) | ||
} else { | ||
null | ||
} | ||
} | ||
|
||
fun select() { | ||
contourAdjuster.selectEdge(segmentIndex) | ||
} | ||
private fun wrap(block: ContourEdge.() -> ContourEdge) { | ||
val newEdge = ContourEdge(contourAdjuster.contour, segmentIndex).block() | ||
contourAdjuster.contour = newEdge.contour | ||
contourAdjuster.updateSelection(newEdge.adjustments) | ||
} | ||
fun toCubic() = wrap { toCubic() } | ||
fun splitAt(t: Double) = wrap { splitAt(t) } | ||
fun moveBy(translation: Vector2, updateTangents: Boolean = true) = wrap { movedBy(translation, updateTangents) } | ||
fun rotate(rotationInDegrees: Double, anchorT: Double = 0.5, updateTangents: Boolean = true) = | ||
wrap { rotatedBy(rotationInDegrees, anchorT, updateTangents) } | ||
fun scale(scaleFactor: Double, anchorT: Double = 0.5, updateTangents: Boolean = true) = | ||
wrap { scaledBy(scaleFactor, anchorT, updateTangents = true) } | ||
|
||
fun replaceWith(t:Double, updateTangents: Boolean = true) { | ||
wrap { replacedWith(t, updateTangents) } | ||
} | ||
|
||
fun sub(t0:Double, t1: Double, updateTangents: Boolean = true) { | ||
contourAdjuster.contour = | ||
ContourEdge(contourAdjuster.contour, segmentIndex) | ||
.subbed(t0, t1) | ||
.contour | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterExtensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package org.openrndr.extra.shapes.adjust | ||
|
||
/* | ||
A collection of extension functions for ContourAdjuster. It is encouraged to keep the ContourAdjuster class at a minimum | ||
size by adding extension functions here. | ||
*/ | ||
|
||
/** | ||
* Apply a sub to the subject contour | ||
*/ | ||
fun ContourAdjuster.sub(t0: Double, t1: Double, updateSelection: Boolean = true) { | ||
val oldSegments = contour.segments | ||
contour = contour.sub(t0, t1) | ||
val newSegments = contour.segments | ||
|
||
// TODO: this update of the selections is not right | ||
if (updateSelection && oldSegments.size != newSegments.size) { | ||
selectEdges() | ||
selectVertices() | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterVertex.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.openrndr.extra.shapes.adjust | ||
|
||
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) { | ||
val newVertex = ContourVertex(contourAdjuster.contour, segmentIndex).block() | ||
contourAdjuster.contour = newVertex.contour | ||
contourAdjuster.updateSelection(newVertex.adjustments) | ||
} | ||
fun select() { | ||
contourAdjuster.selectVertex(segmentIndex) | ||
} | ||
fun remove(updateTangents: Boolean = true) = wrap { remove(updateTangents) } | ||
fun moveBy(translation: Vector2, updateTangents: Boolean = true) = wrap { movedBy(translation, updateTangents) } | ||
fun rotate(rotationInDegrees: Double) = wrap { rotatedBy(rotationInDegrees) } | ||
fun scale(scaleFactor: Double) = wrap { scaledBy(scaleFactor) } | ||
|
||
} |
Oops, something went wrong.