[orx-shapes] Comment the ContourAdjuster and its demos
This commit is contained in:
@@ -79,41 +79,113 @@ data class ContourAdjusterEdge(val contourAdjuster: ContourAdjuster, val segment
|
|||||||
contourAdjuster.updateSelection(newEdge.adjustments)
|
contourAdjuster.updateSelection(newEdge.adjustments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the edge to a linear edge, truncating control points if those exist
|
||||||
|
*/
|
||||||
fun toLinear() = wrap { toLinear() }
|
fun toLinear() = wrap { toLinear() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the edge to a cubic edge
|
||||||
|
*/
|
||||||
fun toCubic() = wrap { toCubic() }
|
fun toCubic() = wrap { toCubic() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split the edge at [t]
|
||||||
|
* @param t an edge t value between 0 and 1. No splitting happens when t == 0 or t == 1.
|
||||||
|
*/
|
||||||
fun splitAt(t: Double) = wrap { splitAt(t) }
|
fun splitAt(t: Double) = wrap { splitAt(t) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* split edge in [numberOfParts] parts of equal length
|
* Split the edge in [numberOfParts] parts of equal length
|
||||||
*/
|
*/
|
||||||
fun splitIn(numberOfParts: Int) = wrap { splitIn(numberOfParts) }
|
fun splitIn(numberOfParts: Int) = wrap { splitIn(numberOfParts) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new contour edge by applying a translation to the current edge.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the contour edge.
|
||||||
|
* @param updateTangents whether the tangents of adjacent segments should be updated after the transformation.
|
||||||
|
* @return a new instance of the contour edge, transformed by the given translation.
|
||||||
|
*/
|
||||||
fun moveBy(translation: Vector2, updateTangents: Boolean = true) = wrap { movedBy(translation, updateTangents) }
|
fun moveBy(translation: Vector2, updateTangents: Boolean = true) = wrap { movedBy(translation, updateTangents) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates the current edge by a specified angle around an anchor point relative to the edge.
|
||||||
|
* Optionally updates the tangents of adjacent segments after the rotation.
|
||||||
|
*
|
||||||
|
* @param rotationInDegrees the rotation angle in degrees to be applied.
|
||||||
|
* @param anchorT the relative position along the edge (range 0.0 to 1.0) defining the anchor point of rotation. Defaults to 0.5 (the center of the edge).
|
||||||
|
* @param updateTangents whether the tangents of adjacent segments should be updated after the rotation. Defaults to true.
|
||||||
|
*/
|
||||||
fun rotate(rotationInDegrees: Double, anchorT: Double = 0.5, updateTangents: Boolean = true) =
|
fun rotate(rotationInDegrees: Double, anchorT: Double = 0.5, updateTangents: Boolean = true) =
|
||||||
wrap { rotatedBy(rotationInDegrees, anchorT, updateTangents) }
|
wrap { rotatedBy(rotationInDegrees, anchorT, updateTangents) }
|
||||||
|
|
||||||
fun scale(scaleFactor: Double, anchorT: Double = 0.5, updateTangents: Boolean = true) =
|
/**
|
||||||
|
* Scales the current edge by a specified factor, with an optional anchor point determining the
|
||||||
|
* scaling center along the edge. The scaling operation updates the tangents of the edge.
|
||||||
|
*/
|
||||||
|
fun scale(scaleFactor: Double, anchorT: Double = 0.5) =
|
||||||
wrap { scaledBy(scaleFactor, anchorT, updateTangents = true) }
|
wrap { scaledBy(scaleFactor, anchorT, updateTangents = true) }
|
||||||
|
|
||||||
fun replaceWith(t: Double, updateTangents: Boolean = true) = wrap { replacedWith(t, updateTangents) }
|
/**
|
||||||
|
* Replace this edge with a point at [t]
|
||||||
|
* @param t an edge t value between 0 and 1
|
||||||
|
*/
|
||||||
|
fun replaceWith(t: Double) = wrap { replacedWith(t) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the current edge with the segments of an open shape contour.
|
||||||
|
*
|
||||||
|
* @param openContour the open shape contour whose segments replace the current edge. The provided
|
||||||
|
* contour must not be closed.
|
||||||
|
* @return a new ContourEdge instance with the updated segments from the `openContour`.
|
||||||
|
*/
|
||||||
fun replaceWith(openContour: ShapeContour) = wrap { replacedWith(openContour) }
|
fun replaceWith(openContour: ShapeContour) = wrap { replacedWith(openContour) }
|
||||||
|
|
||||||
|
/**
|
||||||
fun sub(t0: Double, t1: Double, updateTangents: Boolean = true) {
|
* Returns part of the edge between [t0] to [t1].
|
||||||
|
* Preserves topology unless t0 = t1.
|
||||||
|
* @param t0 the edge's start t-value, between 0 and 1
|
||||||
|
* @param t1 the edge's end t-value, between 0 and 1
|
||||||
|
*/
|
||||||
|
fun sub(t0: Double, t1: Double) {
|
||||||
contourAdjuster.contour =
|
contourAdjuster.contour =
|
||||||
ContourEdge(contourAdjuster.contour, segmentIndex())
|
ContourEdge(contourAdjuster.contour, segmentIndex())
|
||||||
.subbed(t0, t1)
|
.subbed(t0, t1)
|
||||||
.contour
|
.contour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the starting point of the contour edge by the given translation vector.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the starting point of the contour edge.
|
||||||
|
* @param updateTangents whether the tangents of adjacent segments should be updated after the transformation. Defaults to true.
|
||||||
|
* @return a new instance of the contour edge with the starting point moved by the given translation.
|
||||||
|
*/
|
||||||
fun moveStartBy(translation: Vector2, updateTangents: Boolean = true) = wrap { startMovedBy(translation, updateTangents) }
|
fun moveStartBy(translation: Vector2, updateTangents: Boolean = true) = wrap { startMovedBy(translation, updateTangents) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the first control point of a contour edge by a specified translation vector.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the first control point of the contour edge.
|
||||||
|
* @return a new instance of the contour edge with the first control point moved by the given translation.
|
||||||
|
*/
|
||||||
fun moveControl0By(translation: Vector2) = wrap { control0MovedBy(translation) }
|
fun moveControl0By(translation: Vector2) = wrap { control0MovedBy(translation) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the second control point (Control1) of a contour edge by a specified translation vector.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the second control point of the contour edge.
|
||||||
|
* @return a new instance of the contour edge with the second control point moved by the given translation.
|
||||||
|
*/
|
||||||
fun moveControl1By(translation: Vector2) = wrap { control1MovedBy(translation) }
|
fun moveControl1By(translation: Vector2) = wrap { control1MovedBy(translation) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the end point of the contour edge by the specified translation vector.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the end point of the contour edge.
|
||||||
|
* @param updateTangents whether the tangents of adjacent segments should be updated after the transformation. Defaults to true.
|
||||||
|
* @return a new instance of the contour edge with the end point moved by the given translation.
|
||||||
|
*/
|
||||||
fun moveEndBy(translation: Vector2, updateTangents: Boolean = true) = wrap { startMovedBy(translation, updateTangents) }
|
fun moveEndBy(translation: Vector2, updateTangents: Boolean = true) = wrap { startMovedBy(translation, updateTangents) }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ data class ContourEdge(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert the edge to a linear edge, truncating control points if those exist
|
* Convert the edge to a linear edge, truncating control points if those exist
|
||||||
*/
|
*/
|
||||||
fun toLinear(): ContourEdge {
|
fun toLinear(): ContourEdge {
|
||||||
return if (contour.segments[segmentIndex].type != SegmentType.LINEAR) {
|
return if (contour.segments[segmentIndex].type != SegmentType.LINEAR) {
|
||||||
@@ -75,7 +75,7 @@ data class ContourEdge(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert the edge to a cubic edge
|
* Convert the edge to a cubic edge
|
||||||
*/
|
*/
|
||||||
fun toCubic(): ContourEdge {
|
fun toCubic(): ContourEdge {
|
||||||
return if (contour.segments[segmentIndex].type != SegmentType.CUBIC) {
|
return if (contour.segments[segmentIndex].type != SegmentType.CUBIC) {
|
||||||
@@ -99,10 +99,10 @@ data class ContourEdge(
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* replace this edge with a point at [t]
|
* Replace this edge with a point at [t]
|
||||||
* @param t an edge t value between 0 and 1
|
* @param t an edge t value between 0 and 1
|
||||||
*/
|
*/
|
||||||
fun replacedWith(t: Double, updateTangents: Boolean): ContourEdge {
|
fun replacedWith(t: Double): ContourEdge {
|
||||||
if (contour.empty) {
|
if (contour.empty) {
|
||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
}
|
}
|
||||||
@@ -125,6 +125,9 @@ data class ContourEdge(
|
|||||||
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex, adjustments)
|
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex, adjustments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split the edge in [numberOfParts] parts of equal length
|
||||||
|
*/
|
||||||
fun splitIn(parts: Int): ContourEdge {
|
fun splitIn(parts: Int): ContourEdge {
|
||||||
if (contour.empty || parts < 2) {
|
if (contour.empty || parts < 2) {
|
||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
@@ -140,13 +143,20 @@ data class ContourEdge(
|
|||||||
return replacedWith(ShapeContour.fromContours(newSegments, false, 1.0))
|
return replacedWith(ShapeContour.fromContours(newSegments, false, 1.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the current edge with the segments of an open shape contour.
|
||||||
|
*
|
||||||
|
* @param openContour the open shape contour whose segments replace the current edge. The provided
|
||||||
|
* contour must not be closed.
|
||||||
|
* @return a new ContourEdge instance with the updated segments from the `openContour`.
|
||||||
|
*/
|
||||||
fun replacedWith(openContour: ShapeContour): ContourEdge {
|
fun replacedWith(openContour: ShapeContour): ContourEdge {
|
||||||
if (contour.empty) {
|
if (contour.empty) {
|
||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
}
|
}
|
||||||
require(!openContour.closed) { "openContour should be open" }
|
require(!openContour.closed) { "openContour should be open" }
|
||||||
val segment = contour.segments[segmentIndex]
|
val segment = contour.segments[segmentIndex]
|
||||||
var newSegments = contour.segments.toMutableList()
|
val newSegments = contour.segments.toMutableList()
|
||||||
|
|
||||||
var insertIndex = segmentIndex
|
var insertIndex = segmentIndex
|
||||||
val adjustments = newSegments.adjust {
|
val adjustments = newSegments.adjust {
|
||||||
@@ -169,11 +179,12 @@ data class ContourEdge(
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* subs the edge from [t0] to [t1], preserves topology unless t0 = t1
|
* Returns part of the edge between [t0] to [t1].
|
||||||
* @param t0 the start edge t-value, between 0 and 1
|
* Preserves topology unless t0 = t1.
|
||||||
* @param t1 the end edge t-value, between 0 and 1
|
* @param t0 the edge's start t-value, between 0 and 1
|
||||||
|
* @param t1 the edge's end t-value, between 0 and 1
|
||||||
*/
|
*/
|
||||||
fun subbed(t0: Double, t1: Double, updateTangents: Boolean = true): ContourEdge {
|
fun subbed(t0: Double, t1: Double): ContourEdge {
|
||||||
if (contour.empty) {
|
if (contour.empty) {
|
||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
}
|
}
|
||||||
@@ -195,23 +206,23 @@ data class ContourEdge(
|
|||||||
newSegments[segmentIndex] = sub
|
newSegments[segmentIndex] = sub
|
||||||
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex)
|
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex)
|
||||||
} else {
|
} else {
|
||||||
return replacedWith(t0, updateTangents)
|
return replacedWith(t0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* split the edge at [t]
|
* Split the edge at [t]
|
||||||
* @param t an edge t value between 0 and 1, will not split when t == 0 or t == 1
|
* @param t An edge t value between 0 and 1. No splitting happens when t == 0 or t == 1.
|
||||||
*/
|
*/
|
||||||
fun splitAt(t: Double): ContourEdge {
|
fun splitAt(t: Double): ContourEdge {
|
||||||
if (contour.empty) {
|
if (contour.empty) {
|
||||||
return withoutAdjustments()
|
return withoutAdjustments()
|
||||||
}
|
}
|
||||||
val newContour = contour.insertPointAt(segmentIndex, t)
|
val newContour = contour.insertPointAt(segmentIndex, t)
|
||||||
if (newContour.segments.size == contour.segments.size + 1) {
|
return if (newContour.segments.size == contour.segments.size + 1) {
|
||||||
return ContourEdge(newContour, segmentIndex, listOf(SegmentOperation.Insert(segmentIndex + 1, 1)))
|
ContourEdge(newContour, segmentIndex, listOf(SegmentOperation.Insert(segmentIndex + 1, 1)))
|
||||||
} else {
|
} else {
|
||||||
return this.copy(adjustments = emptyList())
|
this.copy(adjustments = emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,25 +293,58 @@ data class ContourEdge(
|
|||||||
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex)
|
return ContourEdge(ShapeContour.fromSegments(newSegments, contour.closed), segmentIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the starting point of the contour edge by the given translation vector.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the starting point of the contour edge.
|
||||||
|
* @param updateTangents whether the tangents of adjacent segments should be updated after the transformation. Defaults to true.
|
||||||
|
* @return a new instance of the contour edge with the starting point moved by the given translation.
|
||||||
|
*/
|
||||||
fun startMovedBy(translation: Vector2, updateTangents: Boolean = true): ContourEdge =
|
fun startMovedBy(translation: Vector2, updateTangents: Boolean = true): ContourEdge =
|
||||||
transformedBy(buildTransform {
|
transformedBy(buildTransform {
|
||||||
translate(translation)
|
translate(translation)
|
||||||
}, updateTangents = updateTangents, mask = maskOf(ControlMask.START))
|
}, updateTangents = updateTangents, mask = maskOf(ControlMask.START))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the first control point of a contour edge by a specified translation vector.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the first control point of the contour edge.
|
||||||
|
* @return a new instance of the contour edge with the first control point moved by the given translation.
|
||||||
|
*/
|
||||||
fun control0MovedBy(translation: Vector2): ContourEdge = transformedBy(buildTransform {
|
fun control0MovedBy(translation: Vector2): ContourEdge = transformedBy(buildTransform {
|
||||||
translate(translation)
|
translate(translation)
|
||||||
}, updateTangents = false, mask = maskOf(ControlMask.CONTROL0), promoteToCubic = true)
|
}, updateTangents = false, mask = maskOf(ControlMask.CONTROL0), promoteToCubic = true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the second control point (Control1) of a contour edge by a specified translation vector.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the second control point of the contour edge.
|
||||||
|
* @return a new instance of the contour edge with the second control point moved by the given translation.
|
||||||
|
*/
|
||||||
fun control1MovedBy(translation: Vector2): ContourEdge = transformedBy(buildTransform {
|
fun control1MovedBy(translation: Vector2): ContourEdge = transformedBy(buildTransform {
|
||||||
translate(translation)
|
translate(translation)
|
||||||
}, updateTangents = false, mask = maskOf(ControlMask.CONTROL1), promoteToCubic = true)
|
}, updateTangents = false, mask = maskOf(ControlMask.CONTROL1), promoteToCubic = true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the end point of the contour edge by the specified translation vector.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the end point of the contour edge.
|
||||||
|
* @param updateTangents whether the tangents of adjacent segments should be updated after the transformation. Defaults to true.
|
||||||
|
* @return a new instance of the contour edge with the end point moved by the given translation.
|
||||||
|
*/
|
||||||
fun endMovedBy(translation: Vector2, updateTangents: Boolean = true): ContourEdge {
|
fun endMovedBy(translation: Vector2, updateTangents: Boolean = true): ContourEdge {
|
||||||
return transformedBy(buildTransform {
|
return transformedBy(buildTransform {
|
||||||
translate(translation)
|
translate(translation)
|
||||||
}, updateTangents = updateTangents, mask = maskOf(ControlMask.END))
|
}, updateTangents = updateTangents, mask = maskOf(ControlMask.END))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new contour edge by applying a translation to the current edge.
|
||||||
|
*
|
||||||
|
* @param translation the translation vector to apply to the contour edge.
|
||||||
|
* @param updateTangents whether the tangents of adjacent segments should be updated after the transformation.
|
||||||
|
* @return a new instance of the contour edge, transformed by the given translation.
|
||||||
|
*/
|
||||||
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)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import kotlin.math.cos
|
|||||||
/**
|
/**
|
||||||
* Demonstrates an `adjustContour` animated effect where edge 0 of a contour
|
* Demonstrates an `adjustContour` animated effect where edge 0 of a contour
|
||||||
* is replaced by a point sampled on that edge. The specific edge point oscillates between
|
* is replaced by a point sampled on that edge. The specific edge point oscillates between
|
||||||
* 0.0 (at the start) and 1.0 (at the end) using a cosine and the `seconds` variable.
|
* 0.0 (at the start of the segment) and 1.0 (at the end) using a cosine and the `seconds` variable.
|
||||||
*
|
*
|
||||||
* The base contour used for the effect alternates every second
|
* The base contour used for the effect alternates every second
|
||||||
* between a rectangular and a circular contour.
|
* between a rectangular and a circular contour.
|
||||||
|
|||||||
@@ -6,6 +6,19 @@ import org.openrndr.extra.shapes.adjust.adjustContour
|
|||||||
import org.openrndr.shape.Circle
|
import org.openrndr.shape.Circle
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates animated modifications to a circular contour using `adjustContour`.
|
||||||
|
*
|
||||||
|
* The application creates a circular contour and dynamically alters its edges
|
||||||
|
* based on the current time in seconds. Each edge of the contour is selected
|
||||||
|
* and transformed through a series of operations:
|
||||||
|
*
|
||||||
|
* - The currently active edge (based on time modulo 4) is replaced with a point at 0.5.
|
||||||
|
* - All other edges are reshaped by reducing their length dynamically, with the reduction
|
||||||
|
* calculated using a cosine function involving the current time in seconds.
|
||||||
|
*
|
||||||
|
* The resulting contour is then drawn with a red stroke color.
|
||||||
|
*/
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
configure {
|
configure {
|
||||||
width = 800
|
width = 800
|
||||||
|
|||||||
@@ -6,6 +6,22 @@ import org.openrndr.extra.shapes.adjust.adjustContour
|
|||||||
import org.openrndr.shape.Circle
|
import org.openrndr.shape.Circle
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates the use of `adjustContour`
|
||||||
|
* to create an animated effect where edges are split, vertices are selected,
|
||||||
|
* and transformations such as scaling are applied.
|
||||||
|
*
|
||||||
|
* The program creates a circular contour which is modified on each animation frame.
|
||||||
|
*
|
||||||
|
* - Edges of the circular contour are split dynamically based on a time-based cosine function.
|
||||||
|
* - Newly created vertices are selected and scaled around the center of the contour
|
||||||
|
* using time-dependent transformations.
|
||||||
|
*
|
||||||
|
* The selection of vertices happens automatically thanks to
|
||||||
|
* `parameters.clearSelectedVertices` and `parameters.selectInsertedVertices`
|
||||||
|
*
|
||||||
|
* The modified animated contour is finally drawn.
|
||||||
|
*/
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
configure {
|
configure {
|
||||||
width = 800
|
width = 800
|
||||||
@@ -27,7 +43,7 @@ fun main() = application {
|
|||||||
for (e in edges) {
|
for (e in edges) {
|
||||||
e.splitAt(splitT)
|
e.splitAt(splitT)
|
||||||
}
|
}
|
||||||
// as a resut of the clearSelectedVertices and selectInsertedVertices settings
|
// as a result of the clearSelectedVertices and selectInsertedVertices settings,
|
||||||
// the vertex selection is set to the newly inserted vertices
|
// the vertex selection is set to the newly inserted vertices
|
||||||
for ((index, v) in vertices.withIndex()) {
|
for ((index, v) in vertices.withIndex()) {
|
||||||
v.scale(cos(seconds + i + index) * 0.5 * (1.0 / (1.0 + i)) + 1.0, drawer.bounds.center)
|
v.scale(cos(seconds + i + index) * 0.5 * (1.0 / (1.0 + i)) + 1.0, drawer.bounds.center)
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ import org.openrndr.math.Vector2
|
|||||||
import org.openrndr.shape.contour
|
import org.openrndr.shape.contour
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates how to create and manipulate a contour dynamically using the `adjustContour` function.
|
||||||
|
*
|
||||||
|
* The program initializes a simple linear contour and applies transformations to it on each animation frame:
|
||||||
|
* - The only edge of the contour is split into many equal parts.
|
||||||
|
* - A value between 0 and 1 is calculated based on the cosine of the current time in seconds.
|
||||||
|
* - That value is used to calculate an anchor point and to select all vertices to its right
|
||||||
|
* - The selected vertices are rotated around an anchor, as if rolling a straight line into a spiral.
|
||||||
|
*/
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
configure {
|
configure {
|
||||||
width = 800
|
width = 800
|
||||||
@@ -22,7 +31,7 @@ fun main() = application {
|
|||||||
contour = adjustContour(contour) {
|
contour = adjustContour(contour) {
|
||||||
selectEdge(0)
|
selectEdge(0)
|
||||||
edge.splitIn(128)
|
edge.splitIn(128)
|
||||||
val tr = cos(seconds) * 0.5 + 0.5
|
val tr = cos(seconds + 2.0) * 0.5 + 0.5
|
||||||
|
|
||||||
selectVertices { i, v -> v.t >= tr }
|
selectVertices { i, v -> v.t >= tr }
|
||||||
val anchor = contour.position(tr)
|
val anchor = contour.position(tr)
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ import org.openrndr.extra.shapes.adjust.adjustContour
|
|||||||
import org.openrndr.math.Vector2
|
import org.openrndr.math.Vector2
|
||||||
import org.openrndr.shape.contour
|
import org.openrndr.shape.contour
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates how to adjust and manipulate the vertices and edges of a contour.
|
||||||
|
*
|
||||||
|
* This method shows two approaches for transforming contours:
|
||||||
|
*
|
||||||
|
* 1. Adjusting vertices directly by selecting specific vertices in a contour and modifying their control points.
|
||||||
|
* 2. Adjusting edges of a contour by transforming their control points.
|
||||||
|
*
|
||||||
|
* For each approach, a red line is drawn representing the transformed contour.
|
||||||
|
*/
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
configure {
|
configure {
|
||||||
width = 800
|
width = 800
|
||||||
@@ -13,6 +23,7 @@ fun main() = application {
|
|||||||
}
|
}
|
||||||
program {
|
program {
|
||||||
extend {
|
extend {
|
||||||
|
// Adjust a contour by transforming its vertices
|
||||||
var contour = contour {
|
var contour = contour {
|
||||||
moveTo(drawer.bounds.position(0.5, 0.1) - Vector2(300.0, 0.0))
|
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))
|
lineTo(drawer.bounds.position(0.5, 0.1) + Vector2(300.0, 0.0))
|
||||||
@@ -29,7 +40,7 @@ fun main() = application {
|
|||||||
drawer.stroke = ColorRGBa.RED
|
drawer.stroke = ColorRGBa.RED
|
||||||
drawer.contour(contour)
|
drawer.contour(contour)
|
||||||
|
|
||||||
|
// Achieve the same effect by transforming the control points of its edge
|
||||||
contour = contour {
|
contour = contour {
|
||||||
moveTo(drawer.bounds.position(0.5, 0.2) - Vector2(300.0, 0.0))
|
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))
|
lineTo(drawer.bounds.position(0.5, 0.2) + Vector2(300.0, 0.0))
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package adjust
|
|||||||
|
|
||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.draw.loadFont
|
||||||
|
import org.openrndr.extra.color.presets.DARK_CYAN
|
||||||
import org.openrndr.extra.shapes.adjust.adjustContour
|
import org.openrndr.extra.shapes.adjust.adjustContour
|
||||||
import org.openrndr.extra.shapes.adjust.extensions.averageTangents
|
import org.openrndr.extra.shapes.adjust.extensions.averageTangents
|
||||||
import org.openrndr.extra.shapes.adjust.extensions.switchTangents
|
import org.openrndr.extra.shapes.adjust.extensions.switchTangents
|
||||||
@@ -9,14 +11,36 @@ import org.openrndr.extra.shapes.tunni.tunniLine
|
|||||||
import org.openrndr.extra.shapes.tunni.tunniPoint
|
import org.openrndr.extra.shapes.tunni.tunniPoint
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates how to manipulate a contour by adjusting and transforming its vertices
|
||||||
|
* and edges, and subsequently visualizing the result using different drawing styles.
|
||||||
|
*
|
||||||
|
* The program creates a rectangular contour derived by shrinking the bounds of the drawing area.
|
||||||
|
* It then applies multiple transformations to selected vertices. These transformations include:
|
||||||
|
*
|
||||||
|
* - Averaging tangents for selected vertices
|
||||||
|
* - Scaling and rotating vertex positions based on the horizontal mouse position
|
||||||
|
* - Switching tangents for specific vertices
|
||||||
|
*
|
||||||
|
* The resulting contour is drawn in black. Additionally:
|
||||||
|
*
|
||||||
|
* - Control line segments are visualized in red, connecting segment endpoints to control points.
|
||||||
|
* - Vertices are numbered and highlighted with black-filled circles.
|
||||||
|
* - Tunni lines, which represent optimized control line placements, are visualized in cyan.
|
||||||
|
* - Tunni points, marking the Tunni line's control, are emphasized with yellow-filled circles.
|
||||||
|
*
|
||||||
|
*/
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
configure {
|
configure {
|
||||||
width = 800
|
width = 800
|
||||||
height = 800
|
height = 800
|
||||||
}
|
}
|
||||||
program {
|
program {
|
||||||
|
val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0)
|
||||||
extend {
|
extend {
|
||||||
drawer.clear(ColorRGBa.WHITE)
|
drawer.clear(ColorRGBa.WHITE)
|
||||||
|
drawer.fontMap = font
|
||||||
|
|
||||||
var contour = drawer.bounds.offsetEdges(-200.0).contour
|
var contour = drawer.bounds.offsetEdges(-200.0).contour
|
||||||
|
|
||||||
drawer.fill = null
|
drawer.fill = null
|
||||||
@@ -26,7 +50,7 @@ fun main() = application {
|
|||||||
for (v in vertices) {
|
for (v in vertices) {
|
||||||
v.averageTangents()
|
v.averageTangents()
|
||||||
v.scale(sqrt(2.0))
|
v.scale(sqrt(2.0))
|
||||||
v.rotate(45.0)
|
v.rotate(mouse.position.x - 45.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
selectVertices(2)
|
selectVertices(2)
|
||||||
@@ -38,19 +62,25 @@ fun main() = application {
|
|||||||
drawer.contour(contour)
|
drawer.contour(contour)
|
||||||
|
|
||||||
drawer.stroke = ColorRGBa.RED
|
drawer.stroke = ColorRGBa.RED
|
||||||
|
|
||||||
for (s in contour.segments) {
|
for (s in contour.segments) {
|
||||||
drawer.lineSegment(s.start, s.cubic.control[0])
|
drawer.lineSegment(s.start, s.cubic.control[0])
|
||||||
drawer.lineSegment(s.end, s.cubic.control[1])
|
drawer.lineSegment(s.end, s.cubic.control[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw points and numbers
|
||||||
drawer.fill = ColorRGBa.BLACK
|
drawer.fill = ColorRGBa.BLACK
|
||||||
drawer.stroke = null
|
drawer.stroke = null
|
||||||
drawer.circles(contour.segments.map { it.start }, 5.0)
|
contour.segments.forEachIndexed { i, it ->
|
||||||
|
drawer.text(i.toString(), it.start + 10.0)
|
||||||
|
drawer.circle(it.start, 5.0)
|
||||||
|
}
|
||||||
|
|
||||||
drawer.stroke = ColorRGBa.GRAY
|
drawer.fill = ColorRGBa.YELLOW
|
||||||
|
drawer.stroke = ColorRGBa.CYAN
|
||||||
for (s in contour.segments) {
|
for (s in contour.segments) {
|
||||||
|
drawer.strokeWeight = 3.0
|
||||||
drawer.lineSegment(s.tunniLine)
|
drawer.lineSegment(s.tunniLine)
|
||||||
drawer.fill = ColorRGBa.CYAN
|
drawer.strokeWeight = 1.0
|
||||||
drawer.circle(s.tunniPoint, 5.0)
|
drawer.circle(s.tunniPoint, 5.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,24 @@ import org.openrndr.math.Vector2
|
|||||||
import org.openrndr.shape.Segment2D
|
import org.openrndr.shape.Segment2D
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates how to adjust and animate contour segments and vertices.
|
||||||
|
*
|
||||||
|
* The method initially creates a contour by offsetting the edges of the window's bounds. A process is
|
||||||
|
* defined to sequence through various transformations on the contour, such as selecting edges, selecting
|
||||||
|
* vertices, rotating points, or modifying segment attributes based on mathematical transformations.
|
||||||
|
*
|
||||||
|
* The adjusted contour and its modified segments and vertices are iterated through a sequence
|
||||||
|
* and updated in real time. Rendering involves visualizing the contour, its control points, the
|
||||||
|
* Tunni lines, Tunni points, as well as the selected segments and points with distinct styles
|
||||||
|
* for better visualization.
|
||||||
|
*
|
||||||
|
* The complex animation sequence is implemented using coroutines. Two loops in the code alternate
|
||||||
|
* between rotating vertices and adjusting Tunni lines while the `extend` function takes care of
|
||||||
|
* rendering the composition in its current state.
|
||||||
|
*
|
||||||
|
* The core elements to study to in this demo are `adjustContourSequence` and `launch`.
|
||||||
|
*/
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
configure {
|
configure {
|
||||||
width = 800
|
width = 800
|
||||||
@@ -39,7 +57,6 @@ fun main() = application {
|
|||||||
selectVertices((i * 3).mod(4))
|
selectVertices((i * 3).mod(4))
|
||||||
for (v in vertices) {
|
for (v in vertices) {
|
||||||
yield(status)
|
yield(status)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +64,10 @@ fun main() = application {
|
|||||||
selectEdges(i.mod(4))
|
selectEdges(i.mod(4))
|
||||||
for (j in 0 until 30) {
|
for (j in 0 until 30) {
|
||||||
for (e in edges) {
|
for (e in edges) {
|
||||||
e.withTunniLine(e.tunniLine.position(0.5) + e.tunniLine.normal * cos(i.toDouble() + e.segmentIndex()) * 50.0 / 30.0)
|
e.withTunniLine(
|
||||||
|
e.tunniLine.position(0.5) +
|
||||||
|
e.tunniLine.normal * cos(i.toDouble() + e.segmentIndex()) * 50.0 / 30.0
|
||||||
|
)
|
||||||
yield(status)
|
yield(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user