[orx-shapes] Add tangent calculation utilities for Circle
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package org.openrndr.extra.shapes.primitives
|
||||
|
||||
import org.openrndr.math.Polar
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.asDegrees
|
||||
import org.openrndr.shape.Circle
|
||||
import org.openrndr.shape.Circle.Companion.INVALID
|
||||
import kotlin.math.acos
|
||||
import kotlin.math.sqrt
|
||||
|
||||
/**
|
||||
* Calculates the tangent lines between this circle and another circle.
|
||||
*
|
||||
* Depending on the value of [isInner], the method computes either the inner
|
||||
* tangents or outer tangents between the two circles.
|
||||
*
|
||||
* @param other The other circle with which to calculate tangents.
|
||||
* @param isInner If true, computes inner tangents where the tangents go
|
||||
* through the region between the two circles. If false (default), computes
|
||||
* outer tangents.
|
||||
* @return A list of pairs of points where each pair represents a tangent line.
|
||||
* Each pair contains one point on this circle and the corresponding point on the other circle.
|
||||
* Returns an empty list if no valid tangent lines exist between the circles.
|
||||
*/
|
||||
fun Circle.tangents(other: Circle, isInner: Boolean = false): List<Pair<Vector2, Vector2>> {
|
||||
if (this == INVALID || other == INVALID) {
|
||||
return listOf()
|
||||
}
|
||||
|
||||
val distSq = center.squaredDistanceTo(other.center)
|
||||
|
||||
if (isInner) {
|
||||
if (sqrt(distSq) <= radius + other.radius) {
|
||||
return listOf() // circles too close
|
||||
}
|
||||
} else {
|
||||
val rDiff = radius - other.radius
|
||||
if (distSq <= rDiff * rDiff) {
|
||||
return listOf() // nested circles
|
||||
}
|
||||
}
|
||||
|
||||
val otherRadiusSigned = if (isInner) -other.radius else other.radius
|
||||
val hyp = other.center - center // hypotenuse
|
||||
val adj = radius - otherRadiusSigned // adjacent
|
||||
val a = hyp * adj
|
||||
val b = hyp.perpendicular() * sqrt(distSq - adj * adj)
|
||||
val v1 = (a - b) / distSq
|
||||
val v2 = (a + b) / distSq
|
||||
|
||||
return listOf(
|
||||
Pair(center + v1 * radius, other.center + v1 * otherRadiusSigned),
|
||||
Pair(center + v2 * radius, other.center + v2 * otherRadiusSigned)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the tangent points from a given external point to a circle.
|
||||
*
|
||||
* If the circle is invalid (e.g., has an undefined radius or center),
|
||||
* the function returns a pair of infinite vectors.
|
||||
*
|
||||
* @param point The external point from which tangents to the circle are calculated.
|
||||
* @return A pair of [Vector2] representing the two points on the circle
|
||||
* where the tangents from the given external point touch the circle.
|
||||
*/
|
||||
fun Circle.tangents(point: Vector2): Pair<Vector2, Vector2> {
|
||||
if (this == INVALID) {
|
||||
return Pair(Vector2.INFINITY, Vector2.INFINITY)
|
||||
}
|
||||
val v = Polar.fromVector(point - center)
|
||||
val b = v.radius
|
||||
val theta = (acos(radius / b)).asDegrees
|
||||
val d1 = v.theta + theta
|
||||
val d2 = v.theta - theta
|
||||
|
||||
val tp = center + Polar(d1, radius).cartesian
|
||||
val tp2 = center + Polar(d2, radius).cartesian
|
||||
|
||||
return Pair(tp, tp2)
|
||||
}
|
||||
Reference in New Issue
Block a user