[orx-shapes] Refactor package layout
This commit is contained in:
33
orx-shapes/src/commonMain/kotlin/primitives/Arc.kt
Normal file
33
orx-shapes/src/commonMain/kotlin/primitives/Arc.kt
Normal file
@@ -0,0 +1,33 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
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<Arc> {
|
||||
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)
|
||||
}
|
||||
38
orx-shapes/src/commonMain/kotlin/primitives/Circle.kt
Normal file
38
orx-shapes/src/commonMain/kotlin/primitives/Circle.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
import org.openrndr.math.Polar
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.transforms.buildTransform
|
||||
import org.openrndr.shape.Circle
|
||||
import org.openrndr.shape.Ellipse
|
||||
import org.openrndr.shape.ShapeContour
|
||||
import org.openrndr.shape.contour as buildContour
|
||||
|
||||
/**
|
||||
* Convert [Circle] to [ShapeContour] with a number of segments equal to [segments]
|
||||
* @param segments the number of segments, at least 4
|
||||
*/
|
||||
fun Circle.contour(segments: Int): ShapeContour {
|
||||
return buildContour {
|
||||
val p = Polar(0.0, radius)
|
||||
moveTo(center + p.cartesian)
|
||||
for (i in 1 until segments+1) {
|
||||
val lp = Polar(i * 360.0/segments, radius).cartesian + center
|
||||
arcTo(radius, radius, 360.0/segments, false, true, lp.x, lp.y)
|
||||
}
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert [Ellipse] to [ShapeContour] with a number of segments equal to [segments]
|
||||
* @param segments the number of segments, at least 4
|
||||
*/
|
||||
fun Ellipse.contour(segments: Int): ShapeContour {
|
||||
return Circle(Vector2.ZERO, xRadius).contour(segments).transform(
|
||||
buildTransform {
|
||||
translate(center)
|
||||
scale(1.0, yRadius/xRadius)
|
||||
}
|
||||
)
|
||||
}
|
||||
67
orx-shapes/src/commonMain/kotlin/primitives/Net.kt
Normal file
67
orx-shapes/src/commonMain/kotlin/primitives/Net.kt
Normal file
@@ -0,0 +1,67 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
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<Net> {
|
||||
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)
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
}
|
||||
}
|
||||
}
|
||||
50
orx-shapes/src/commonMain/kotlin/primitives/Pulley.kt
Normal file
50
orx-shapes/src/commonMain/kotlin/primitives/Pulley.kt
Normal file
@@ -0,0 +1,50 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
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<Pulley> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
80
orx-shapes/src/commonMain/kotlin/primitives/RectangleGrid.kt
Normal file
80
orx-shapes/src/commonMain/kotlin/primitives/RectangleGrid.kt
Normal file
@@ -0,0 +1,80 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
import org.openrndr.shape.Rectangle
|
||||
import kotlin.math.round
|
||||
|
||||
/**
|
||||
* Splits [Rectangle] into a grid of [Rectangle]s
|
||||
* @param columns the number of columns in the resulting grid
|
||||
* @param rows the number of rows in the resulting grid
|
||||
* @param marginX the unitless margin width
|
||||
* @param marginY the unitless margin height
|
||||
* @param gutterX the unitless gutter width, the horizontal space between grid cells
|
||||
* @param gutterY the unitless gutter height, the vertical space between grid cells
|
||||
*/
|
||||
fun Rectangle.grid(
|
||||
columns: Int,
|
||||
rows: Int,
|
||||
marginX: Double = 0.0,
|
||||
marginY: Double = 0.0,
|
||||
gutterX: Double = 0.0,
|
||||
gutterY: Double = 0.0
|
||||
) = grid(
|
||||
(width - marginX * 2 - gutterX * (columns - 1)) / columns,
|
||||
(height - marginY * 2 - gutterY * (rows - 1)) / rows,
|
||||
marginX, marginY,
|
||||
gutterX, gutterY
|
||||
)
|
||||
|
||||
/**
|
||||
* Splits [Rectangle] into a grid of [Rectangle]s
|
||||
* @param cellWidth the unitless width of a cell
|
||||
* @param cellHeight the unitless height of a cell
|
||||
* @param minMarginX the unitless minimum margin width (may increase to produce
|
||||
* the desired cell aspect ratio)
|
||||
* @param minMarginY the unitless minimum margin height (may increase to produce
|
||||
* the desired cell aspect ratio)
|
||||
* @param gutterX the unitless gutter width, the horizontal space between grid cells
|
||||
* @param gutterY the unitless gutter height, the vertical space between grid cells
|
||||
*/
|
||||
fun Rectangle.grid(
|
||||
cellWidth: Double,
|
||||
cellHeight: Double,
|
||||
minMarginX: Double = 0.0,
|
||||
minMarginY: Double = 0.0,
|
||||
gutterX: Double = 0.0,
|
||||
gutterY: Double = 0.0
|
||||
): List<List<Rectangle>> {
|
||||
|
||||
val availableWidth = (width - minMarginX * 2).coerceAtLeast(0.0)
|
||||
val availableHeight = (height - minMarginY * 2).coerceAtLeast(0.0)
|
||||
|
||||
val cellSpaceX = cellWidth + gutterX
|
||||
val cellSpaceY = cellHeight + gutterY
|
||||
|
||||
val columns = round((availableWidth + gutterX) / cellSpaceX).toInt()
|
||||
val rows = round((availableHeight + gutterY) / cellSpaceY).toInt()
|
||||
|
||||
if (columns == 0 || rows == 0) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val totalGutterWidth = gutterX * (columns - 1).coerceAtLeast(0)
|
||||
val totalGutterHeight = gutterY * (rows - 1).coerceAtLeast(0)
|
||||
|
||||
val totalWidth = cellWidth * columns + totalGutterWidth
|
||||
val totalHeight = cellHeight * rows + totalGutterHeight
|
||||
|
||||
val x0 = x + (width - totalWidth) / 2
|
||||
val y0 = y + (height - totalHeight) / 2
|
||||
|
||||
return (0 until rows).map { row ->
|
||||
(0 until columns).map { column ->
|
||||
Rectangle(
|
||||
x0 + column * cellSpaceX,
|
||||
y0 + row * cellSpaceY,
|
||||
cellWidth, cellHeight
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
115
orx-shapes/src/commonMain/kotlin/primitives/RegularPolygon.kt
Normal file
115
orx-shapes/src/commonMain/kotlin/primitives/RegularPolygon.kt
Normal file
@@ -0,0 +1,115 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.asRadians
|
||||
import org.openrndr.shape.ShapeContour
|
||||
import org.openrndr.shape.contour
|
||||
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
|
||||
for (i in 0 until sides) {
|
||||
val x = center.x + radius * cos(i.toDouble() / sides * PI * 2 + phi)
|
||||
val y = center.y + radius * sin(i.toDouble() / sides * PI * 2 + phi)
|
||||
|
||||
moveOrLineTo(x, y)
|
||||
}
|
||||
close()
|
||||
}
|
||||
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
|
||||
for (i in 0 until sides) {
|
||||
val x0 = center.x + radius * cos(i.toDouble() / sides * PI * 2 + phi)
|
||||
val y0 = center.y + radius * sin(i.toDouble() / sides * PI * 2 + phi)
|
||||
|
||||
val x1 = center.x + radius * cos((i + 1.0) / sides * PI * 2 + phi)
|
||||
val y1 = center.y + radius * sin((i + 1.0) / sides * PI * 2 + phi)
|
||||
|
||||
val x2 = center.x + radius * cos((i + 2.0) / sides * PI * 2 + phi)
|
||||
val y2 = center.y + radius * sin((i + 2.0) / sides * PI * 2 + phi)
|
||||
|
||||
val f = roundFactor / 2.0
|
||||
|
||||
val dx10 = x1 - x0
|
||||
val dy10 = y1 - y0
|
||||
val dx21 = x2 - x1
|
||||
val dy21 = y2 - y1
|
||||
|
||||
val x3 = x0 + dx10 * f
|
||||
val y3 = y0 + dy10 * f
|
||||
|
||||
val x4 = x1 - dx10 * f
|
||||
val y4 = y1 - dy10 * f
|
||||
|
||||
val x5 = x1 + dx21 * f
|
||||
val y5 = y1 + dy21 * f
|
||||
|
||||
moveOrLineTo(x3, y3)
|
||||
lineTo(x4, y4)
|
||||
curveTo(x1, y1, x5, y5)
|
||||
}
|
||||
close()
|
||||
}
|
||||
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
|
||||
for (i in 0 until sides) {
|
||||
val x0 = center.x + radius * cos(i.toDouble() / sides * PI * 2 + phi)
|
||||
val y0 = center.y + radius * sin(i.toDouble() / sides * PI * 2 + phi)
|
||||
|
||||
val x1 = center.x + radius * cos((i + 1.0) / sides * PI * 2 + phi)
|
||||
val y1 = center.y + radius * sin((i + 1.0) / sides * PI * 2 + phi)
|
||||
|
||||
val x2 = center.x + radius * cos((i + 2.0) / sides * PI * 2 + phi)
|
||||
val y2 = center.y + radius * sin((i + 2.0) / sides * PI * 2 + phi)
|
||||
|
||||
val f = bevelFactor / 2.0
|
||||
|
||||
val dx10 = x1 - x0
|
||||
val dy10 = y1 - y0
|
||||
val dx21 = x2 - x1
|
||||
val dy21 = y2 - y1
|
||||
|
||||
val x3 = x0 + dx10 * f
|
||||
val y3 = y0 + dy10 * f
|
||||
|
||||
val x4 = x1 - dx10 * f
|
||||
val y4 = y1 - dy10 * f
|
||||
|
||||
val x5 = x1 + dx21 * f
|
||||
val y5 = y1 + dy21 * f
|
||||
|
||||
moveOrLineTo(x3, y3)
|
||||
lineTo(x4, y4)
|
||||
lineTo(x5, y5)
|
||||
}
|
||||
close()
|
||||
}
|
||||
return c
|
||||
}
|
||||
83
orx-shapes/src/commonMain/kotlin/primitives/RegularStar.kt
Normal file
83
orx-shapes/src/commonMain/kotlin/primitives/RegularStar.kt
Normal file
@@ -0,0 +1,83 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.asRadians
|
||||
import org.openrndr.shape.ShapeContour
|
||||
import org.openrndr.shape.contour
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
fun regularStar(points: Int, innerRadius: Double, outerRadius: Double, center: Vector2 = Vector2.ZERO, phase: Double = 0.0): ShapeContour {
|
||||
return contour {
|
||||
val theta = phase.asRadians
|
||||
val phi = PI * 2.0 / (points * 2)
|
||||
for (i in 0 until points * 2 step 2) {
|
||||
val outerPoint = Vector2(cos(i * phi + theta), sin(i * phi + theta)) * outerRadius + center
|
||||
val innerPoint = Vector2(cos((i + 1) * phi + theta), sin((i + 1) * phi + theta)) * innerRadius + center
|
||||
moveOrLineTo(outerPoint)
|
||||
lineTo(innerPoint)
|
||||
}
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
fun regularStarRounded(points: Int, innerRadius: Double, outerRadius: Double,
|
||||
innerFactor: Double, outerFactor: Double,
|
||||
center: Vector2 = Vector2.ZERO,
|
||||
phase: Double = 0.0): ShapeContour {
|
||||
return contour {
|
||||
val theta = phase.asRadians
|
||||
val phi = PI * 2.0 / (points * 2)
|
||||
for (i in 0 until points * 2 step 2) {
|
||||
val outerPoint0 = Vector2(cos(i * phi + theta), sin(i * phi + theta)) * outerRadius + center
|
||||
val innerPoint = Vector2(cos((i + 1) * phi + theta), sin((i + 1) * phi + theta)) * innerRadius + center
|
||||
val outerPoint1 = Vector2(cos((i + 2) * phi + theta), sin((i + 2) * phi + theta)) * outerRadius + center
|
||||
val innerPoint1 = Vector2(cos((i + 3) * phi + theta), sin((i + 3) * phi + theta)) * innerRadius + center
|
||||
|
||||
val fo = (outerFactor * 0.5)
|
||||
val fi = (innerFactor * 0.5)
|
||||
|
||||
val p0 = innerPoint - (innerPoint - outerPoint0) * fi
|
||||
val p1 = innerPoint + (outerPoint1 - innerPoint) * fi
|
||||
val p2 = outerPoint1 - (outerPoint1 - innerPoint) * fo
|
||||
val p3 = outerPoint1 + (innerPoint1 - outerPoint1) * fo
|
||||
|
||||
moveOrLineTo(p0)
|
||||
curveTo(innerPoint, p1)
|
||||
lineTo(p2)
|
||||
curveTo(outerPoint1, p3)
|
||||
}
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
fun regularStarBeveled(points: Int, innerRadius: Double, outerRadius: Double,
|
||||
innerFactor: Double, outerFactor: Double,
|
||||
center: Vector2 = Vector2.ZERO,
|
||||
phase: Double = 0.0): ShapeContour {
|
||||
return contour {
|
||||
val theta = phase.asRadians
|
||||
val phi = PI * 2.0 / (points * 2)
|
||||
for (i in 0 until points * 2 step 2) {
|
||||
val outerPoint0 = Vector2(cos(i * phi + theta), sin(i * phi + theta)) * outerRadius + center
|
||||
val innerPoint = Vector2(cos((i + 1) * phi + theta), sin((i + 1) * phi + theta)) * innerRadius + center
|
||||
val outerPoint1 = Vector2(cos((i + 2) * phi + theta), sin((i + 2) * phi + theta)) * outerRadius + center
|
||||
val innerPoint1 = Vector2(cos((i + 3) * phi + theta), sin((i + 3) * phi + theta)) * innerRadius + center
|
||||
|
||||
val fo = (outerFactor * 0.5)
|
||||
val fi = (innerFactor * 0.5)
|
||||
|
||||
val p0 = innerPoint - (innerPoint - outerPoint0) * fi
|
||||
val p1 = innerPoint + (outerPoint1 - innerPoint) * fi
|
||||
val p2 = outerPoint1 - (outerPoint1 - innerPoint) * fo
|
||||
val p3 = outerPoint1 + (innerPoint1 - outerPoint1) * fo
|
||||
|
||||
moveOrLineTo(p0)
|
||||
lineTo(p1)
|
||||
lineTo(p2)
|
||||
lineTo(p3)
|
||||
}
|
||||
close()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
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
|
||||
get() = corner + Vector2(width / 2, height / 2)
|
||||
|
||||
val x: Double get() = corner.x
|
||||
val y: Double get() = corner.y
|
||||
|
||||
/** [ShapeContour] representation of the rounded rectangle */
|
||||
val contour
|
||||
get() = contour {
|
||||
// A higher radius than half the width/height makes it go weird
|
||||
val r = min(min(radius, width / 2), height / 2)
|
||||
|
||||
moveTo(x + r, y)
|
||||
lineTo(x + width - r, y)
|
||||
|
||||
arcTo(r, r, 90.0, false, true, Vector2(x + width, y + r))
|
||||
lineTo(x + width, y + height - r)
|
||||
|
||||
arcTo(r, r, 90.0, false, true, Vector2(x + width - r, y + height))
|
||||
lineTo(x + r, y + height)
|
||||
|
||||
arcTo(r, r, 90.0, false, true, Vector2(x, y + height - r))
|
||||
lineTo(x, y + r)
|
||||
|
||||
arcTo(r, r, 90.0, false, true, Vector2(x + r, y))
|
||||
close()
|
||||
}
|
||||
|
||||
val shape
|
||||
get() = contour.shape
|
||||
}
|
||||
|
||||
fun Drawer.roundedRectangle(x: Double, y: Double, width: Double, height: Double, radius: Double) =
|
||||
contour(RoundedRectangle(x, y, width, height, radius).contour)
|
||||
|
||||
fun Drawer.roundedRectangle(position: Vector2, width: Double, height: Double, radius: Double) =
|
||||
contour(RoundedRectangle(position, width, height, radius).contour)
|
||||
|
||||
fun Drawer.roundedRectangle(roundedRectangle: RoundedRectangle) =
|
||||
contour(roundedRectangle.contour)
|
||||
|
||||
fun Rectangle.toRounded(radius: Double) = RoundedRectangle(this, radius)
|
||||
32
orx-shapes/src/commonMain/kotlin/primitives/Tear.kt
Normal file
32
orx-shapes/src/commonMain/kotlin/primitives/Tear.kt
Normal file
@@ -0,0 +1,32 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
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<Tear> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user