[orx-composition] Add explicit contract checks for lambdas with callsInPlace
This commit is contained in:
@@ -12,6 +12,9 @@ import org.openrndr.math.Vector3
|
|||||||
import org.openrndr.math.YPolarity
|
import org.openrndr.math.YPolarity
|
||||||
import org.openrndr.math.transforms.*
|
import org.openrndr.math.transforms.*
|
||||||
import org.openrndr.shape.*
|
import org.openrndr.shape.*
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
import kotlin.jvm.JvmRecord
|
import kotlin.jvm.JvmRecord
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,7 +83,12 @@ data class ShapeNodeIntersection(val node: ShapeNode, val intersection: ContourI
|
|||||||
* in a [ShapeContour] closest to some other 2D point.
|
* in a [ShapeContour] closest to some other 2D point.
|
||||||
*/
|
*/
|
||||||
@JvmRecord
|
@JvmRecord
|
||||||
data class ShapeNodeNearestContour(val node: ShapeNode, val point: ContourPoint, val distanceDirection: Vector2, val distance: Double)
|
data class ShapeNodeNearestContour(
|
||||||
|
val node: ShapeNode,
|
||||||
|
val point: ContourPoint,
|
||||||
|
val distanceDirection: Vector2,
|
||||||
|
val distance: Double
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges two lists of [ShapeNodeIntersection] removing duplicates under the
|
* Merges two lists of [ShapeNodeIntersection] removing duplicates under the
|
||||||
@@ -104,9 +112,10 @@ fun List<ShapeNodeIntersection>.merge(threshold: Double = 0.5): List<ShapeNodeIn
|
|||||||
* A Drawer-like interface for the creation of Compositions
|
* A Drawer-like interface for the creation of Compositions
|
||||||
* This should be easier than creating Compositions manually
|
* This should be easier than creating Compositions manually
|
||||||
*/
|
*/
|
||||||
class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositionDimensions,
|
class CompositionDrawer(
|
||||||
composition: Composition? = null,
|
documentBounds: CompositionDimensions = defaultCompositionDimensions,
|
||||||
cursor: GroupNode? = composition?.root as? GroupNode
|
composition: Composition? = null,
|
||||||
|
cursor: GroupNode? = composition?.root as? GroupNode
|
||||||
) {
|
) {
|
||||||
val root = (composition?.root as? GroupNode) ?: GroupNode()
|
val root = (composition?.root as? GroupNode) ?: GroupNode()
|
||||||
val composition = composition ?: Composition(root, documentBounds)
|
val composition = composition ?: Composition(root, documentBounds)
|
||||||
@@ -194,7 +203,11 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
drawStyle = styleStack.removeLast()
|
drawStyle = styleStack.removeLast()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
fun isolated(draw: CompositionDrawer.() -> Unit) {
|
fun isolated(draw: CompositionDrawer.() -> Unit) {
|
||||||
|
contract {
|
||||||
|
callsInPlace(draw, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
pushModel()
|
pushModel()
|
||||||
pushStyle()
|
pushStyle()
|
||||||
draw()
|
draw()
|
||||||
@@ -202,7 +215,11 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
popStyle()
|
popStyle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
fun GroupNode.with(builder: CompositionDrawer.() -> Unit): GroupNode {
|
fun GroupNode.with(builder: CompositionDrawer.() -> Unit): GroupNode {
|
||||||
|
contract {
|
||||||
|
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
val oldCursor = cursor
|
val oldCursor = cursor
|
||||||
cursor = this
|
cursor = this
|
||||||
builder()
|
builder()
|
||||||
@@ -216,7 +233,12 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
* @param id an optional identifier
|
* @param id an optional identifier
|
||||||
* @param builder the function that is executed inside the group context
|
* @param builder the function that is executed inside the group context
|
||||||
*/
|
*/
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
fun group(insert: Boolean = true, id: String? = null, builder: CompositionDrawer.() -> Unit): GroupNode {
|
fun group(insert: Boolean = true, id: String? = null, builder: CompositionDrawer.() -> Unit): GroupNode {
|
||||||
|
contract {
|
||||||
|
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
|
||||||
val group = GroupNode()
|
val group = GroupNode()
|
||||||
group.id = id
|
group.id = id
|
||||||
val oldCursor = cursor
|
val oldCursor = cursor
|
||||||
@@ -267,8 +289,8 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
* @return an optional org.openrndr.shape.ShapeNodeNearestContour instance
|
* @return an optional org.openrndr.shape.ShapeNodeNearestContour instance
|
||||||
*/
|
*/
|
||||||
fun nearest(
|
fun nearest(
|
||||||
point: Vector2,
|
point: Vector2,
|
||||||
searchFrom: CompositionNode = composition.root as GroupNode
|
searchFrom: CompositionNode = composition.root as GroupNode
|
||||||
): ShapeNodeNearestContour? {
|
): ShapeNodeNearestContour? {
|
||||||
return distances(point, searchFrom).firstOrNull()
|
return distances(point, searchFrom).firstOrNull()
|
||||||
}
|
}
|
||||||
@@ -297,13 +319,13 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
* @return a sorted list of [ShapeNodeNearestContour] describing distance to every contour
|
* @return a sorted list of [ShapeNodeNearestContour] describing distance to every contour
|
||||||
*/
|
*/
|
||||||
fun distances(
|
fun distances(
|
||||||
point: Vector2,
|
point: Vector2,
|
||||||
searchFrom: CompositionNode = composition.root as GroupNode
|
searchFrom: CompositionNode = composition.root as GroupNode
|
||||||
): List<ShapeNodeNearestContour> {
|
): List<ShapeNodeNearestContour> {
|
||||||
return searchFrom.findShapes().flatMap { node ->
|
return searchFrom.findShapes().flatMap { node ->
|
||||||
node.shape.contours.filter { !it.empty }
|
node.shape.contours.filter { !it.empty }
|
||||||
.map { it.nearest(point) }
|
.map { it.nearest(point) }
|
||||||
.map { ShapeNodeNearestContour(node, it, point - it.position, it.position.distanceTo(point)) }
|
.map { ShapeNodeNearestContour(node, it, point - it.position, it.position.distanceTo(point)) }
|
||||||
}.sortedBy { it.distance }
|
}.sortedBy { it.distance }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +364,7 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun CompositionNode.intersections(contour: ShapeContour, mergeThreshold: Double = 0.5) =
|
fun CompositionNode.intersections(contour: ShapeContour, mergeThreshold: Double = 0.5) =
|
||||||
intersections(contour, this, mergeThreshold)
|
intersections(contour, this, mergeThreshold)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test a given `shape` against org.openrndr.shape.contours in the composition tree
|
* Test a given `shape` against org.openrndr.shape.contours in the composition tree
|
||||||
@@ -361,7 +383,7 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun CompositionNode.intersections(shape: Shape, mergeThreshold: Double = 0.5) =
|
fun CompositionNode.intersections(shape: Shape, mergeThreshold: Double = 0.5) =
|
||||||
intersections(shape, this, mergeThreshold)
|
intersections(shape, this, mergeThreshold)
|
||||||
|
|
||||||
|
|
||||||
fun shape(shape: Shape, insert: Boolean = true): ShapeNode? {
|
fun shape(shape: Shape, insert: Boolean = true): ShapeNode? {
|
||||||
@@ -388,6 +410,7 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
shapeNode.transform = model
|
shapeNode.transform = model
|
||||||
Matrix44.IDENTITY
|
Matrix44.IDENTITY
|
||||||
}
|
}
|
||||||
|
|
||||||
TransformMode.APPLY -> {
|
TransformMode.APPLY -> {
|
||||||
shapeNode.transform = Matrix44.IDENTITY
|
shapeNode.transform = Matrix44.IDENTITY
|
||||||
model
|
model
|
||||||
@@ -408,6 +431,7 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
}
|
}
|
||||||
toInsert
|
toInsert
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> error("unreachable")
|
else -> error("unreachable")
|
||||||
}
|
}
|
||||||
shapeNode.stroke = stroke
|
shapeNode.stroke = stroke
|
||||||
@@ -425,18 +449,19 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
}
|
}
|
||||||
shapeNode
|
shapeNode
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val shapeNodes = (if (!clipMode.grouped) composition.findShapes() else cursor.findShapes())
|
val shapeNodes = (if (!clipMode.grouped) composition.findShapes() else cursor.findShapes())
|
||||||
val toRemove = shapeNodes.pmap { shapeNode ->
|
val toRemove = shapeNodes.pmap { shapeNode ->
|
||||||
val inverse = shapeNode.effectiveTransform.inversed
|
val inverse = shapeNode.effectiveTransform.inversed
|
||||||
val transformedShape = postShape.transform(inverse * model)
|
val transformedShape = postShape.transform(inverse * model)
|
||||||
val operated =
|
val operated =
|
||||||
when (clipMode.op) {
|
when (clipMode.op) {
|
||||||
ClipOp.INTERSECT -> intersection(shapeNode.shape, transformedShape)
|
ClipOp.INTERSECT -> intersection(shapeNode.shape, transformedShape)
|
||||||
ClipOp.UNION -> union(shapeNode.shape, transformedShape)
|
ClipOp.UNION -> union(shapeNode.shape, transformedShape)
|
||||||
ClipOp.DIFFERENCE -> difference(shapeNode.shape, transformedShape)
|
ClipOp.DIFFERENCE -> difference(shapeNode.shape, transformedShape)
|
||||||
else -> error("unsupported base op ${clipMode.op}")
|
else -> error("unsupported base op ${clipMode.op}")
|
||||||
}
|
}
|
||||||
return@pmap if (!operated.empty) {
|
return@pmap if (!operated.empty) {
|
||||||
shapeNode.shape = operated
|
shapeNode.shape = operated
|
||||||
null
|
null
|
||||||
@@ -454,38 +479,45 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
|
|
||||||
fun shapes(shapes: List<Shape>, insert: Boolean = true) = shapes.map { shape(it, insert) }
|
fun shapes(shapes: List<Shape>, insert: Boolean = true) = shapes.map { shape(it, insert) }
|
||||||
|
|
||||||
fun rectangle(rectangle: Rectangle, closed: Boolean = true, insert: Boolean = true) = contour(rectangle.contour.let {
|
fun rectangle(rectangle: Rectangle, closed: Boolean = true, insert: Boolean = true) =
|
||||||
if (closed) {
|
contour(rectangle.contour.let {
|
||||||
it
|
if (closed) {
|
||||||
} else {
|
it
|
||||||
it.open
|
} else {
|
||||||
}
|
it.open
|
||||||
}, insert = insert)
|
}
|
||||||
|
}, insert = insert)
|
||||||
|
|
||||||
fun rectangle(x: Double, y: Double, width: Double, height: Double, closed: Boolean = true, insert: Boolean = true) = rectangle(
|
fun rectangle(x: Double, y: Double, width: Double, height: Double, closed: Boolean = true, insert: Boolean = true) =
|
||||||
Rectangle(x, y, width, height), closed, insert)
|
rectangle(
|
||||||
|
Rectangle(x, y, width, height), closed, insert
|
||||||
|
)
|
||||||
|
|
||||||
fun rectangles(rectangles: List<Rectangle>, insert: Boolean = true) = rectangles.map { rectangle(it, insert) }
|
fun rectangles(rectangles: List<Rectangle>, insert: Boolean = true) = rectangles.map { rectangle(it, insert) }
|
||||||
|
|
||||||
fun rectangles(positions: List<Vector2>, width: Double, height: Double, insert: Boolean = true) = rectangles(positions.map {
|
fun rectangles(positions: List<Vector2>, width: Double, height: Double, insert: Boolean = true) =
|
||||||
Rectangle(it, width, height)
|
rectangles(positions.map {
|
||||||
}, insert)
|
Rectangle(it, width, height)
|
||||||
|
}, insert)
|
||||||
|
|
||||||
fun rectangles(positions: List<Vector2>, dimensions: List<Vector2>, insert: Boolean) = rectangles((positions zip dimensions).map {
|
fun rectangles(positions: List<Vector2>, dimensions: List<Vector2>, insert: Boolean) =
|
||||||
Rectangle(it.first, it.second.x, it.second.y)
|
rectangles((positions zip dimensions).map {
|
||||||
}, insert)
|
Rectangle(it.first, it.second.x, it.second.y)
|
||||||
|
}, insert)
|
||||||
|
|
||||||
fun circle(x: Double, y: Double, radius: Double, closed: Boolean = true, insert: Boolean = true) = circle(
|
fun circle(x: Double, y: Double, radius: Double, closed: Boolean = true, insert: Boolean = true) = circle(
|
||||||
Circle(
|
Circle(
|
||||||
Vector2(x, y),
|
Vector2(x, y),
|
||||||
radius
|
radius
|
||||||
), closed, insert)
|
), closed, insert
|
||||||
|
)
|
||||||
|
|
||||||
fun circle(position: Vector2, radius: Double, closed: Boolean = true, insert: Boolean = true) = circle(
|
fun circle(position: Vector2, radius: Double, closed: Boolean = true, insert: Boolean = true) = circle(
|
||||||
Circle(
|
Circle(
|
||||||
position,
|
position,
|
||||||
radius
|
radius
|
||||||
), closed, insert)
|
), closed, insert
|
||||||
|
)
|
||||||
|
|
||||||
fun circle(circle: Circle, closed: Boolean = true, insert: Boolean = true) = contour(circle.contour.let {
|
fun circle(circle: Circle, closed: Boolean = true, insert: Boolean = true) = contour(circle.contour.let {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
@@ -504,12 +536,13 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
)
|
)
|
||||||
}, insert)
|
}, insert)
|
||||||
|
|
||||||
fun circles(positions: List<Vector2>, radii: List<Double>, insert: Boolean = true) = circles((positions zip radii).map {
|
fun circles(positions: List<Vector2>, radii: List<Double>, insert: Boolean = true) =
|
||||||
Circle(
|
circles((positions zip radii).map {
|
||||||
it.first,
|
Circle(
|
||||||
it.second
|
it.first,
|
||||||
)
|
it.second
|
||||||
}, insert)
|
)
|
||||||
|
}, insert)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
fun ellipse(
|
fun ellipse(
|
||||||
@@ -540,17 +573,17 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
|
|
||||||
|
|
||||||
fun lineSegment(
|
fun lineSegment(
|
||||||
startX: Double,
|
startX: Double,
|
||||||
startY: Double,
|
startY: Double,
|
||||||
endX: Double,
|
endX: Double,
|
||||||
endY: Double,
|
endY: Double,
|
||||||
insert: Boolean = true
|
insert: Boolean = true
|
||||||
) = lineSegment(LineSegment(startX, startY, endX, endY), insert)
|
) = lineSegment(LineSegment(startX, startY, endX, endY), insert)
|
||||||
|
|
||||||
fun lineSegment(
|
fun lineSegment(
|
||||||
start: Vector2,
|
start: Vector2,
|
||||||
end: Vector2,
|
end: Vector2,
|
||||||
insert: Boolean = true
|
insert: Boolean = true
|
||||||
) = lineSegment(LineSegment(start, end), insert)
|
) = lineSegment(LineSegment(start, end), insert)
|
||||||
|
|
||||||
fun lineSegment(
|
fun lineSegment(
|
||||||
@@ -599,19 +632,19 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun lineStrip(
|
fun lineStrip(
|
||||||
points: List<Vector2>,
|
points: List<Vector2>,
|
||||||
insert: Boolean = true
|
insert: Boolean = true
|
||||||
) = contour(ShapeContour.fromPoints(points, false, YPolarity.CW_NEGATIVE_Y), insert)
|
) = contour(ShapeContour.fromPoints(points, false, YPolarity.CW_NEGATIVE_Y), insert)
|
||||||
|
|
||||||
fun lineLoop(
|
fun lineLoop(
|
||||||
points: List<Vector2>,
|
points: List<Vector2>,
|
||||||
insert: Boolean = true
|
insert: Boolean = true
|
||||||
) = contour(ShapeContour.fromPoints(points, true, YPolarity.CW_NEGATIVE_Y), insert)
|
) = contour(ShapeContour.fromPoints(points, true, YPolarity.CW_NEGATIVE_Y), insert)
|
||||||
|
|
||||||
fun text(
|
fun text(
|
||||||
text: String,
|
text: String,
|
||||||
position: Vector2,
|
position: Vector2,
|
||||||
insert: Boolean = true
|
insert: Boolean = true
|
||||||
): TextNode {
|
): TextNode {
|
||||||
val g = GroupNode()
|
val g = GroupNode()
|
||||||
g.style.transform = Transform.Matrix(transform { translate(position.xy0) })
|
g.style.transform = Transform.Matrix(transform { translate(position.xy0) })
|
||||||
@@ -642,18 +675,18 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun texts(text: List<String>, positions: List<Vector2>) =
|
fun texts(text: List<String>, positions: List<Vector2>) =
|
||||||
(text zip positions).map {
|
(text zip positions).map {
|
||||||
text(it.first, it.second)
|
text(it.first, it.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an image to the composition tree
|
* Adds an image to the composition tree
|
||||||
*/
|
*/
|
||||||
fun image(
|
fun image(
|
||||||
image: ColorBuffer,
|
image: ColorBuffer,
|
||||||
x: Double = 0.0,
|
x: Double = 0.0,
|
||||||
y: Double = 0.0,
|
y: Double = 0.0,
|
||||||
insert: Boolean = true
|
insert: Boolean = true
|
||||||
): ImageNode {
|
): ImageNode {
|
||||||
val node = ImageNode(image, x, y, width = image.width.toDouble(), height = image.height.toDouble())
|
val node = ImageNode(image, x, y, width = image.width.toDouble(), height = image.height.toDouble())
|
||||||
node.style.transform = Transform.Matrix(this.model)
|
node.style.transform = Transform.Matrix(this.model)
|
||||||
@@ -697,7 +730,11 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
fun CompositionNode.transform(builder: TransformBuilder.() -> Unit) {
|
fun CompositionNode.transform(builder: TransformBuilder.() -> Unit) {
|
||||||
|
contract {
|
||||||
|
callsInPlace(builder, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
return this.transform(builder)
|
return this.transform(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,12 +750,15 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
is ImageNode -> {
|
is ImageNode -> {
|
||||||
ImageNode(node.image, node.x, node.y, node.width, node.height)
|
ImageNode(node.image, node.x, node.y, node.width, node.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ShapeNode -> {
|
is ShapeNode -> {
|
||||||
ShapeNode(node.shape)
|
ShapeNode(node.shape)
|
||||||
}
|
}
|
||||||
|
|
||||||
is TextNode -> {
|
is TextNode -> {
|
||||||
TextNode(node.text, node.contour)
|
TextNode(node.text, node.contour)
|
||||||
}
|
}
|
||||||
|
|
||||||
is GroupNode -> {
|
is GroupNode -> {
|
||||||
val children = node.children.map { nodeCopy(it) }.toMutableList()
|
val children = node.children.map { nodeCopy(it) }.toMutableList()
|
||||||
val groupNode = GroupNode(children)
|
val groupNode = GroupNode(children)
|
||||||
@@ -742,20 +782,39 @@ class CompositionDrawer(documentBounds: CompositionDimensions = defaultCompositi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a [Composition]. The draw operations contained inside
|
* Draws a vector composition by applying a provided drawing function.
|
||||||
* the [drawFunction] do not render graphics to the screen,
|
*
|
||||||
* but populate the Composition instead.
|
* @param documentBounds Defines the dimensions and bounds of the composition. Defaults to `defaultCompositionDimensions`.
|
||||||
|
* @param composition The target composition to be drawn on. If null, a new composition will be created.
|
||||||
|
* @param cursor Specifies the current position within the composition structure. Defaults to the root of the given composition cast as a `GroupNode`.
|
||||||
|
* @param drawFunction The actual drawing logic that will be executed in the drawing context of the `CompositionDrawer`.
|
||||||
|
* @return The resulting `Composition` after applying the drawing function.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
fun drawComposition(
|
fun drawComposition(
|
||||||
documentBounds: CompositionDimensions = defaultCompositionDimensions,
|
documentBounds: CompositionDimensions = defaultCompositionDimensions,
|
||||||
composition: Composition? = null,
|
composition: Composition? = null,
|
||||||
cursor: GroupNode? = composition?.root as? GroupNode,
|
cursor: GroupNode? = composition?.root as? GroupNode,
|
||||||
drawFunction: CompositionDrawer.() -> Unit
|
drawFunction: CompositionDrawer.() -> Unit
|
||||||
): Composition = CompositionDrawer(documentBounds, composition, cursor).apply { drawFunction() }.composition
|
): Composition {
|
||||||
|
contract {
|
||||||
|
callsInPlace(drawFunction, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
return CompositionDrawer(documentBounds, composition, cursor).apply { drawFunction() }.composition
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw into an existing [Composition].
|
* Draws the content of an existing composition using the provided drawing function.
|
||||||
|
*
|
||||||
|
* @param drawFunction the drawing logic to be executed using a [CompositionDrawer].
|
||||||
|
* This function allows defining how the composition should be rendered visually.
|
||||||
|
* @param cursor an optional [GroupNode] that serves as the starting point for drawing.
|
||||||
|
* Defaults to the root of the composition if not provided.
|
||||||
*/
|
*/
|
||||||
fun Composition.draw(drawFunction: CompositionDrawer.() -> Unit) {
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
fun Composition.draw(drawFunction: CompositionDrawer.() -> Unit, cursor: GroupNode? = this.root as? GroupNode) {
|
||||||
|
contract {
|
||||||
|
callsInPlace(drawFunction, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
drawComposition(composition = this, drawFunction = drawFunction)
|
drawComposition(composition = this, drawFunction = drawFunction)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,16 @@ import kotlin.contracts.ExperimentalContracts
|
|||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
// Derives Composition dimensions from current Drawer
|
/**
|
||||||
|
* Draws a composition within the specified document bounds or an existing composition.
|
||||||
|
* This function utilizes a customizable draw function to define the drawing behavior.
|
||||||
|
*
|
||||||
|
* @param documentBounds Specifies the dimensions for the drawing area. Defaults to the full drawable area of the program.
|
||||||
|
* @param composition An optional existing composition to draw onto. If not provided, a new composition is created.
|
||||||
|
* @param cursor An optional cursor representing the current position in the composition hierarchy. Defaults to the root of the provided composition.
|
||||||
|
* @param drawFunction A lambda function defining the drawing operations to be performed using the `CompositionDrawer`.
|
||||||
|
* @return The resulting composition after applying the draw function.
|
||||||
|
*/
|
||||||
@OptIn(ExperimentalContracts::class)
|
@OptIn(ExperimentalContracts::class)
|
||||||
fun Program.drawComposition(
|
fun Program.drawComposition(
|
||||||
documentBounds: CompositionDimensions = CompositionDimensions(0.0.pixels, 0.0.pixels, this.drawer.width.toDouble().pixels, this.drawer.height.toDouble().pixels),
|
documentBounds: CompositionDimensions = CompositionDimensions(0.0.pixels, 0.0.pixels, this.drawer.width.toDouble().pixels, this.drawer.height.toDouble().pixels),
|
||||||
@@ -20,6 +29,16 @@ fun Program.drawComposition(
|
|||||||
return CompositionDrawer(documentBounds, composition, cursor).apply { drawFunction() }.composition
|
return CompositionDrawer(documentBounds, composition, cursor).apply { drawFunction() }.composition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a composition using the specified document bounds and drawing logic.
|
||||||
|
* Optionally, an existing composition and cursor can be passed to update or build upon them.
|
||||||
|
*
|
||||||
|
* @param documentBounds The bounding rectangle representing the area to be drawn.
|
||||||
|
* @param composition An optional existing composition to update. If null, a new composition will be created.
|
||||||
|
* @param cursor An optional cursor `GroupNode` used as the starting position for appending new elements. Defaults to the root of the provided composition if available.
|
||||||
|
* @param drawFunction A lambda function containing the drawing operations to be applied.
|
||||||
|
* @return The resulting `Composition` object after performing the drawing operations.
|
||||||
|
*/
|
||||||
@OptIn(ExperimentalContracts::class)
|
@OptIn(ExperimentalContracts::class)
|
||||||
fun Program.drawComposition(
|
fun Program.drawComposition(
|
||||||
documentBounds: Rectangle,
|
documentBounds: Rectangle,
|
||||||
@@ -32,5 +51,3 @@ fun Program.drawComposition(
|
|||||||
}
|
}
|
||||||
return CompositionDrawer(CompositionDimensions(documentBounds), composition, cursor).apply { drawFunction() }.composition
|
return CompositionDrawer(CompositionDimensions(documentBounds), composition, cursor).apply { drawFunction() }.composition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user