Skip to content

Commit

Permalink
[orx-shapes] Add WIP ContourAdjuster framework
Browse files Browse the repository at this point in the history
  • Loading branch information
edwinRNDR committed Oct 23, 2023
1 parent 7f41c26 commit e7f8add
Show file tree
Hide file tree
Showing 17 changed files with 940 additions and 0 deletions.
159 changes: 159 additions & 0 deletions orx-shapes/src/commonMain/kotlin/adjust/ContourAdjuster.kt
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 orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterEdge.kt
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
}
}
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 orx-shapes/src/commonMain/kotlin/adjust/ContourAdjusterVertex.kt
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) }

}
Loading

0 comments on commit e7f8add

Please sign in to comment.