diff --git a/orx-shapes/src/commonMain/kotlin/Arc.kt b/orx-shapes/src/commonMain/kotlin/Arc.kt new file mode 100644 index 00000000..c74eace8 --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/Arc.kt @@ -0,0 +1,33 @@ +package org.openrndr.extra.shapes + +import org.openrndr.math.* +import org.openrndr.shape.ShapeContour +import org.openrndr.shape.contour + +/** + * A circular arc + */ +class Arc(val center: Vector2, val radius: Double, val angle0: Double, val angle1: Double) : LinearType { + fun position(t: Double): Vector2 { + val angle = mix(angle0, angle1, t.clamp(0.0, 1.0)) + return Polar(angle, radius).cartesian + center + } + + val contour: ShapeContour + get() { + return contour { + moveTo(position(0.0)) + circularArcTo(position(0.5), position(1.0)) + } + } + + override fun div(scale: Double) = Arc(center / scale, radius / scale, angle0 / scale, angle1 / scale) + + override fun times(scale: Double) = Arc(center * scale, radius * scale, angle0 * scale, angle1 * scale) + + override fun plus(right: Arc) = + Arc(center + right.center, radius + right.radius, angle0 + right.angle0, angle1 + right.angle1) + + override fun minus(right: Arc) = + Arc(center - right.center, radius - right.radius, angle0 - right.angle0, angle1 - right.angle1) +} diff --git a/orx-shapes/src/commonMain/kotlin/Net.kt b/orx-shapes/src/commonMain/kotlin/Net.kt new file mode 100644 index 00000000..ad08dd52 --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/Net.kt @@ -0,0 +1,33 @@ +package org.openrndr.extra.shapes + +import org.openrndr.math.LinearType +import org.openrndr.math.Polar +import org.openrndr.math.Vector2 +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) + + 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 minus(right: Net) = Net(point0 - right.point0, point1 - right.point1, circle - right.circle) + + 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 + } + k += LineSegment(tangents1.second, point1).contour + return k + } +} \ No newline at end of file diff --git a/orx-shapes/src/commonMain/kotlin/Pulley.kt b/orx-shapes/src/commonMain/kotlin/Pulley.kt new file mode 100644 index 00000000..f47161b9 --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/Pulley.kt @@ -0,0 +1,50 @@ +package org.openrndr.extra.shapes + +import org.openrndr.math.LinearType +import org.openrndr.math.Polar +import org.openrndr.shape.Circle +import org.openrndr.shape.LineSegment +import org.openrndr.shape.ShapeContour + +class Pulley(val circle0: Circle, val circle1: Circle) : LinearType { + override fun div(scale: Double): Pulley { + return Pulley(circle0 / scale, circle1 / scale) + } + + override fun times(scale: Double): Pulley { + return Pulley(circle0 * scale, circle1 * scale) + } + + override fun plus(right: Pulley): Pulley { + return Pulley(circle0 + right.circle0, circle1 + right.circle1) + } + + override fun minus(right: Pulley): Pulley { + return Pulley(circle0 - right.circle0, circle1 - right.circle1) + } + + val contour: ShapeContour + get() { + val tangents = circle0.tangents(circle1) + if (tangents.isEmpty()) { + return ShapeContour.EMPTY + } else { + var k = LineSegment(tangents[0].first, tangents[0].second).contour + + run { + var th0 = Polar.fromVector(tangents[0].second - circle1.center).theta + val th1 = Polar.fromVector(tangents[1].second - circle1.center).theta + if (th0 < th1) th0 += 360.0 + k += Arc(circle1.center, circle1.radius, th0, th1).contour + } + k += LineSegment(tangents[1].first, tangents[1].second).contour.reversed + run { + val th0 = Polar.fromVector(tangents[0].first - circle0.center).theta + var th1 = Polar.fromVector(tangents[1].first - circle0.center).theta + if (th0 > th1) th1 += 360.0 + k += Arc(circle0.center, circle0.radius, th0, th1).contour.reversed + } + return k.close() + } + } +} \ No newline at end of file diff --git a/orx-shapes/src/commonMain/kotlin/Tear.kt b/orx-shapes/src/commonMain/kotlin/Tear.kt new file mode 100644 index 00000000..872378dc --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/Tear.kt @@ -0,0 +1,32 @@ +package org.openrndr.extra.shapes + +import org.openrndr.math.LinearType +import org.openrndr.math.Polar +import org.openrndr.math.Vector2 +import org.openrndr.shape.Circle +import org.openrndr.shape.LineSegment +import org.openrndr.shape.ShapeContour + +class Tear(val point: Vector2, val circle: Circle) : LinearType { + override fun div(scale: Double) = Tear(point / scale, circle / scale) + + override fun times(scale: Double) = Tear(point * scale, circle * scale) + + override fun plus(right: Tear) = Tear(point + right.point, circle + right.circle) + + override fun minus(right: Tear) = Tear(point - right.point, circle - right.circle) + + val contour: ShapeContour + get() { + val tangents = circle.tangents(point) + var k = LineSegment(point, tangents.first).contour + run { + val th0 = Polar.fromVector(tangents.first - circle.center).theta + var th1 = Polar.fromVector(tangents.second - circle.center).theta + if (th1 < th0) th1 += 360.0 + k += Arc(circle.center, circle.radius, th0, th1).contour + } + k += LineSegment(tangents.second, point).contour + return k.close() + } +} \ No newline at end of file diff --git a/orx-shapes/src/commonMain/kotlin/bounds/Bounds.kt b/orx-shapes/src/commonMain/kotlin/bounds/Bounds.kt new file mode 100644 index 00000000..af335b26 --- /dev/null +++ b/orx-shapes/src/commonMain/kotlin/bounds/Bounds.kt @@ -0,0 +1,33 @@ +package org.openrndr.extra.shapes.bounds + +import org.openrndr.shape.* +import kotlin.jvm.JvmName + +/** + * Evaluates the bounds around all [ShapeContour] instances in the [Iterable] + */ + +val Iterable.bounds : Rectangle + @JvmName("shapeContourBounds") + get() = map { + it.bounds + }.bounds + +/** + * Evaluates the bounds around all [Shape] instances in the [Iterable] + */ +val Iterable.bounds : Rectangle + @JvmName("shapeBounds") + get() = map { + it.bounds + }.bounds + + +/** + * Evaluates the bounds around all [Segment] instances in the [Iterable] + */ +val Iterable.bounds : Rectangle + @JvmName("segmentBounds") + get() = map { + it.bounds + }.bounds \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoArc01.kt b/orx-shapes/src/jvmDemo/kotlin/DemoArc01.kt new file mode 100644 index 00000000..0aa25c89 --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/DemoArc01.kt @@ -0,0 +1,22 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.shapes.Arc + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + extend { + val a = Arc(drawer.bounds.center, 100.0, 0.0 + seconds * 36.0, -180.0 + seconds * 36.0) + drawer.clear(ColorRGBa.PINK) + drawer.contour(a.contour) + drawer.circle(a.position(0.0), 5.0) + drawer.circle(a.position(0.5), 5.0) + drawer.circle(a.position(1.0), 5.0) + } + } + } +} \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoPulley01.kt b/orx-shapes/src/jvmDemo/kotlin/DemoPulley01.kt new file mode 100644 index 00000000..c43961cf --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/DemoPulley01.kt @@ -0,0 +1,26 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.shapes.Pulley +import org.openrndr.math.Vector2 +import org.openrndr.shape.Circle + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + extend { + drawer.clear(ColorRGBa.BLACK) + drawer.stroke = ColorRGBa.WHITE + drawer.fill = ColorRGBa.PINK + val pulley = Pulley( + Circle(drawer.bounds.center - Vector2(100.0, 100.0), 150.0), + Circle(drawer.bounds.center + Vector2(150.0, 150.0), 75.0) + ) + drawer.contour(pulley.contour) + } + } + } +} \ No newline at end of file diff --git a/orx-shapes/src/jvmDemo/kotlin/DemoTear01.kt b/orx-shapes/src/jvmDemo/kotlin/DemoTear01.kt new file mode 100644 index 00000000..5cd15903 --- /dev/null +++ b/orx-shapes/src/jvmDemo/kotlin/DemoTear01.kt @@ -0,0 +1,29 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.noise.scatter +import org.openrndr.extra.shapes.Tear +import org.openrndr.math.Vector2 +import org.openrndr.shape.Circle +import kotlin.random.Random + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + val points = drawer.bounds.scatter(40.0, distanceToEdge = 150.0, random = Random(0)) + val tears = points.map { + Tear(it - Vector2(0.0, 20.0), Circle(it + Vector2(0.0, 20.0), 20.0)) + } + + extend { + drawer.clear(ColorRGBa.BLACK) + drawer.fill = ColorRGBa.PINK + drawer.stroke = ColorRGBa.WHITE + drawer.contours(tears.map { it.contour }) + } + } + } +} \ No newline at end of file