From 7412ae4595ec236c256159281ab7c7aeb4953b17 Mon Sep 17 00:00:00 2001 From: Abe Pazos Date: Tue, 21 Mar 2023 09:57:26 +0100 Subject: [PATCH] Fixes to orx-color and orx-shapes (#296) --- .../src/jvmDemo/kotlin/DemoColorRange03.kt | 4 +- orx-shapes/src/commonMain/kotlin/Net.kt | 64 ++++++++++++++----- .../src/commonMain/kotlin/RegularPolygon.kt | 16 +++++ .../src/commonMain/kotlin/RoundedRectangle.kt | 10 ++- orx-shapes/src/jvmDemo/kotlin/DemoNet01.kt | 32 ++++++++++ 5 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 orx-shapes/src/jvmDemo/kotlin/DemoNet01.kt diff --git a/orx-color/src/jvmDemo/kotlin/DemoColorRange03.kt b/orx-color/src/jvmDemo/kotlin/DemoColorRange03.kt index 0b7c0fd7..f6c9e715 100644 --- a/orx-color/src/jvmDemo/kotlin/DemoColorRange03.kt +++ b/orx-color/src/jvmDemo/kotlin/DemoColorRange03.kt @@ -1,12 +1,14 @@ import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.draw.loadFont -import org.openrndr.extensions.SingleScreenshot import org.openrndr.extra.color.spaces.* import org.openrndr.extra.color.palettes.rangeTo fun main() { application { + configure { + height = 30 + 50 * 11 // row count + } program { extend { drawer.clear(ColorRGBa.WHITE) diff --git a/orx-shapes/src/commonMain/kotlin/Net.kt b/orx-shapes/src/commonMain/kotlin/Net.kt index ad08dd52..b0ccd137 100644 --- a/orx-shapes/src/commonMain/kotlin/Net.kt +++ b/orx-shapes/src/commonMain/kotlin/Net.kt @@ -7,27 +7,61 @@ import org.openrndr.shape.Circle import org.openrndr.shape.LineSegment import org.openrndr.shape.ShapeContour -class Net(val point0: Vector2, val point1: Vector2, val circle: Circle) : LinearType { - override fun div(scale: Double) = Net(point0 / scale, point1 / scale, circle / scale) +class Net(val point0: Vector2, val point1: Vector2, val circle: Circle) : + LinearType { + override fun div(scale: Double) = + Net(point0 / scale, point1 / scale, circle / scale) - override fun times(scale: Double) = Net(point0 * scale, point1 * scale, circle * scale) + override fun times(scale: Double) = + Net(point0 * scale, point1 * scale, circle * scale) - override fun plus(right: Net) = Net(point0 + right.point0, point1 + right.point1, circle + right.circle) + override fun plus(right: Net) = + Net(point0 + right.point0, point1 + right.point1, circle + right.circle) - override fun minus(right: Net) = Net(point0 - right.point0, point1 - right.point1, circle - right.circle) + override fun minus(right: Net) = + Net(point0 - right.point0, point1 - right.point1, circle - right.circle) + /** + * Creates a [ShapeContour] with three segments: two [LineSegment] and one [Arc]. + * These three components form a contour that resemble a string starting + * at [point0], wrapping around the [circle] and ending at [point1]. + * If one of the points is inside the circle only a line segment tangent + * to the circle that starts at the other point is returned. If both + * points are inside the circle an empty contour is returned. + */ val contour: ShapeContour get() { - val tangents0 = circle.tangents(point0) - val tangents1 = circle.tangents(point1) - var k = LineSegment(point0, tangents0.first).contour - run { - val th0 = Polar.fromVector(tangents0.first - circle.center).theta - var th1 = Polar.fromVector(tangents1.second - circle.center).theta - if (th1 < th0) th1 += 360.0 - k += Arc(circle.center, circle.radius, th0, th1).contour + val p0Inside = circle.contains(point0) + val p1Inside = circle.contains(point1) + + return when { + !p0Inside && !p1Inside -> { + val tangents0 = circle.tangents(point0) + val tangents1 = circle.tangents(point1) + + val th0 = + Polar.fromVector(tangents0.first - circle.center).theta + val th1 = + Polar.fromVector(tangents1.second - circle.center).theta + + LineSegment(point0, tangents0.first).contour + + Arc( + circle.center, + circle.radius, + th0, + if (th1 < th0) th1 + 360.0 else th1 + ).contour + + LineSegment(tangents1.second, point1).contour + } + + p0Inside -> + LineSegment(circle.tangents(point1).second, point1).contour + + p1Inside -> + LineSegment(circle.tangents(point0).first, point0).contour + + else -> + ShapeContour.EMPTY } - k += LineSegment(tangents1.second, point1).contour - return k } } \ No newline at end of file diff --git a/orx-shapes/src/commonMain/kotlin/RegularPolygon.kt b/orx-shapes/src/commonMain/kotlin/RegularPolygon.kt index 335adf2a..be60f8d8 100644 --- a/orx-shapes/src/commonMain/kotlin/RegularPolygon.kt +++ b/orx-shapes/src/commonMain/kotlin/RegularPolygon.kt @@ -8,6 +8,10 @@ import kotlin.math.PI import kotlin.math.cos import kotlin.math.sin +/** + * Creates a regular polygon at [center] with the given [sides] and [radius]. + * Specify a [phase] in degrees to rotate it. + */ fun regularPolygon(sides: Int, center: Vector2 = Vector2.ZERO, radius: Double = 100.0, phase: Double = 0.0): ShapeContour { val c = contour { val phi = phase.asRadians @@ -22,6 +26,11 @@ fun regularPolygon(sides: Int, center: Vector2 = Vector2.ZERO, radius: Double = return c } +/** + * Creates a rounded polygon at [center] with the given [sides] and [radius]. + * Specify a [phase] in degrees to rotate it. + * [roundFactor] 0.0 = no rounding, 0.5 = default, 1.0 = full rounding. + */ fun regularPolygonRounded(sides: Int, roundFactor: Double = 0.5, center: Vector2 = Vector2.ZERO, radius: Double = 100.0, phase: Double = 0.0): ShapeContour { val c = contour { val phi = phase.asRadians @@ -60,6 +69,13 @@ fun regularPolygonRounded(sides: Int, roundFactor: Double = 0.5, center: Vector2 return c } +/** + * Creates a beveled polygon at [center] with the given [sides] and [radius]. + * Specify a [phase] in degrees to rotate it. + * If 0.0 < [bevelFactor] < 1.0 the number of [sides] is doubled. + * Using 0.5 all sides have equal length. With other values [bevelFactor] + * determines the length ratio between even and odd sides. + */ fun regularPolygonBeveled(sides: Int, bevelFactor: Double = 0.5, center: Vector2 = Vector2.ZERO, radius: Double = 100.0, phase: Double = 0.0): ShapeContour { val c = contour { val phi = phase.asRadians diff --git a/orx-shapes/src/commonMain/kotlin/RoundedRectangle.kt b/orx-shapes/src/commonMain/kotlin/RoundedRectangle.kt index 69cce0f8..4540ff2c 100644 --- a/orx-shapes/src/commonMain/kotlin/RoundedRectangle.kt +++ b/orx-shapes/src/commonMain/kotlin/RoundedRectangle.kt @@ -2,11 +2,14 @@ package org.openrndr.extra.shapes import org.openrndr.draw.Drawer import org.openrndr.math.Vector2 +import org.openrndr.shape.Rectangle import org.openrndr.shape.contour +import org.openrndr.shape.ShapeContour import kotlin.math.min class RoundedRectangle(val corner: Vector2, val width: Double, val height: Double, val radius: Double) { constructor(x: Double, y: Double, width: Double, height: Double, radius: Double) : this(Vector2(x, y), width, height, radius) + constructor(rectangle: Rectangle, radius: Double) : this(rectangle.corner, rectangle.width, rectangle.height, radius) /** the center of the rounded rectangle */ val center: Vector2 @@ -36,6 +39,9 @@ class RoundedRectangle(val corner: Vector2, val width: Double, val height: Doubl curveTo(Vector2(x, y), Vector2(x + r, y)) close() } + + val shape + get() = contour.shape } fun Drawer.roundedRectangle(x: Double, y: Double, width: Double, height: Double, radius: Double) = @@ -45,4 +51,6 @@ fun Drawer.roundedRectangle(position: Vector2, width: Double, height: Double, ra contour(RoundedRectangle(position, width, height, radius).contour) fun Drawer.roundedRectangle(roundedRectangle: RoundedRectangle) = - contour(roundedRectangle.contour) \ No newline at end of file + contour(roundedRectangle.contour) + +fun Rectangle.toRounded(radius: Double) = RoundedRectangle(this, radius) \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoNet01.kt b/orx-shapes/src/jvmDemo/kotlin/DemoNet01.kt new file mode 100644 index 00000000..04e21964 --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/DemoNet01.kt @@ -0,0 +1,32 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.shapes.Net +import org.openrndr.shape.Circle +import kotlin.math.sin + +fun main() { + application { + program { + extend { + drawer.clear(ColorRGBa.BLACK) + drawer.stroke = ColorRGBa.WHITE + drawer.fill = ColorRGBa.PINK + val a = drawer.bounds.position(0.7, 0.5) + val b = drawer.bounds.position(0.3, 0.5) + val c = Circle( + drawer.bounds.position( + sin(seconds) * 0.35 + 0.5, + sin(seconds * 2) * 0.25 + 0.5 + ), 50.0 + ) + val net = Net(a, b, c) + drawer.circle(a, 10.0) + drawer.circle(b, 10.0) + drawer.circle(c) + + drawer.strokeWeight = 2.0 + drawer.contour(net.contour) + } + } + } +} \ No newline at end of file