0.3.1-dev-11 #510
@ -6,7 +6,8 @@
|
||||
package space.kscience.kmath.geometry
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.math.PI
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.distanceTo
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
* A circle in 2D space
|
||||
@ -17,4 +18,40 @@ public data class Circle2D(
|
||||
public val radius: Double
|
||||
)
|
||||
|
||||
public fun Circle2D.tangentsToCircle(other: Circle2D): kotlin.collections.MutableMap<String, LineSegment<DoubleVector2D>> {
|
||||
val R1 = this.radius
|
||||
val R2 = other.radius
|
||||
val line = LineSegment(this.center, other.center)
|
||||
val d = line.begin.distanceTo(line.end)
|
||||
val angle1 = atan2(other.center.x - this.center.x, other.center.y - this.center.y)
|
||||
var r: Double
|
||||
var angle2: Double
|
||||
val routes = mapOf("RSR" to Pair(R1, R2),
|
||||
"RSL" to Pair(R1, -R2),
|
||||
"LSR" to Pair(-R1, R2),
|
||||
"LSL" to Pair(-R1, -R2))
|
||||
val segments = mutableMapOf<String, LineSegment<DoubleVector2D>>()
|
||||
for ((route, r1r2) in routes) {
|
||||
val r1 = r1r2.first
|
||||
val r2 = r1r2.second
|
||||
r = if (r1.sign == r2.sign) {
|
||||
r1.absoluteValue - r2.absoluteValue
|
||||
} else {
|
||||
r1.absoluteValue + r2.absoluteValue
|
||||
}
|
||||
val L = (d * d - r * r).pow(0.5)
|
||||
angle2 = if (r1.absoluteValue > r2.absoluteValue) {
|
||||
angle1 + r1.sign * atan2(r.absoluteValue, L)
|
||||
} else {
|
||||
angle1 - r2.sign * atan2(r.absoluteValue, L)
|
||||
}
|
||||
val W = Euclidean2DSpace.vector(-cos(angle2), sin(angle2))
|
||||
segments[route] = LineSegment(
|
||||
Euclidean2DSpace.add(this.center, Euclidean2DSpace.scale(W, r1)),
|
||||
Euclidean2DSpace.add(other.center, Euclidean2DSpace.scale(W, r2))
|
||||
)
|
||||
}
|
||||
return segments
|
||||
}
|
||||
|
||||
public val Circle2D.circumference: Double get() = radius * 2 * PI
|
||||
|
@ -27,5 +27,15 @@ public fun <V : Vector> LineSegment<V>.line(algebra: GeometrySpace<V>): Line<V>
|
||||
Line(begin, end - begin)
|
||||
}
|
||||
|
||||
public fun equalLineSegments(line1: LineSegment<DoubleVector2D>, line2: LineSegment<DoubleVector2D>): Boolean {
|
||||
val maxFloatDelta = 0.000001
|
||||
return line1.begin.x.equalFloat(line2.begin.x) && line1.begin.y.equalFloat(line2.begin.y) &&
|
||||
line1.end.x.equalFloat(line2.end.x) && line1.end.y.equalFloat(line2.end.y)
|
||||
// return line1.begin == line2.begin && line1.end == line2.end
|
||||
}
|
||||
|
||||
public fun Double.equalFloat(other: Double, maxFloatDelta: Double = 0.000001):
|
||||
Boolean = kotlin.math.abs(this - other) < maxFloatDelta
|
||||
|
||||
public typealias LineSegment2D = LineSegment<DoubleVector2D>
|
||||
public typealias LineSegment3D = LineSegment<DoubleVector3D>
|
||||
|
@ -5,9 +5,6 @@
|
||||
|
||||
package space.kscience.kmath.geometry
|
||||
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.distanceTo
|
||||
import space.kscience.kmath.linear.DoubleLinearSpace.plus
|
||||
import space.kscience.kmath.linear.Point
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.pow
|
||||
@ -15,63 +12,64 @@ import kotlin.math.sign
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.cos
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.vector
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.dot
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.scale
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.add
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.distanceTo
|
||||
|
||||
|
||||
public class Segment(
|
||||
public val startPoint: DoubleVector2D,
|
||||
public val terminalPoint: DoubleVector2D,
|
||||
public val length: Double = startPoint.distanceTo(terminalPoint)
|
||||
) {
|
||||
public override operator fun equals(other: Any?): Boolean {
|
||||
return if (other is Segment) {
|
||||
startPoint.x.equalFloat(other.startPoint.x) && startPoint.y.equalFloat(other.startPoint.y) &&
|
||||
terminalPoint.x.equalFloat(other.terminalPoint.x) && terminalPoint.y.equalFloat(other.terminalPoint.y)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
//public class Segment(
|
||||
// public val startPoint: DoubleVector2D,
|
||||
// public val terminalPoint: DoubleVector2D,
|
||||
// public val length: Double = startPoint.distanceTo(terminalPoint)
|
||||
//) {
|
||||
// public override operator fun equals(other: Any?): Boolean {
|
||||
// return if (other is Segment) {
|
||||
// startPoint.x.equalFloat(other.startPoint.x) && startPoint.y.equalFloat(other.startPoint.y) &&
|
||||
// terminalPoint.x.equalFloat(other.terminalPoint.x) && terminalPoint.y.equalFloat(other.terminalPoint.y)
|
||||
// } else {
|
||||
// false
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
public const val maxFloatDelta: Double = 0.000001
|
||||
public fun Double.equalFloat(other: Double): Boolean = kotlin.math.abs(this - other) < maxFloatDelta
|
||||
//public const val maxFloatDelta: Double = 0.000001
|
||||
//public fun Double.equalFloat(other: Double): Boolean = kotlin.math.abs(this - other) < maxFloatDelta
|
||||
|
||||
|
||||
|
||||
public fun tangentsToCircles(
|
||||
startCircle: Circle2D,
|
||||
terminalCircle: Circle2D
|
||||
): kotlin.collections.MutableMap<String, Segment> {
|
||||
val R1 = startCircle.radius
|
||||
val R2 = terminalCircle.radius
|
||||
val d = Segment(startCircle.center, terminalCircle.center).length
|
||||
val angle1 = atan2(terminalCircle.center.x - startCircle.center.x, terminalCircle.center.y - startCircle.center.y)
|
||||
var r: Double
|
||||
var angle2: Double
|
||||
val routes = mapOf("RSR" to Pair(R1, R2),
|
||||
"RSL" to Pair(R1, -R2),
|
||||
"LSR" to Pair(-R1, R2),
|
||||
"LSL" to Pair(-R1, -R2))
|
||||
val segments = mutableMapOf<String, Segment>()
|
||||
for ((route, r1r2) in routes) {
|
||||
val r1 = r1r2.first
|
||||
val r2 = r1r2.second
|
||||
r = if (r1.sign == r2.sign) {
|
||||
r1.absoluteValue - r2.absoluteValue
|
||||
} else {
|
||||
r1.absoluteValue + r2.absoluteValue
|
||||
}
|
||||
val L = (d * d - r * r).pow(0.5)
|
||||
angle2 = if (r1.absoluteValue > r2.absoluteValue) {
|
||||
angle1 + r1.sign * atan2(r.absoluteValue, L)
|
||||
} else {
|
||||
angle1 - r2.sign * atan2(r.absoluteValue, L)
|
||||
}
|
||||
val W = vector(-cos(angle2), sin(angle2))
|
||||
segments[route] = Segment(add(startCircle.center, scale(W, r1)),
|
||||
add(terminalCircle.center, scale(W, r2)))
|
||||
}
|
||||
return segments
|
||||
}
|
||||
//public fun tangentsToCircles(
|
||||
// startCircle: Circle2D,
|
||||
// terminalCircle: Circle2D
|
||||
//): kotlin.collections.MutableMap<String, LineSegment<DoubleVector2D>> {
|
||||
// val R1 = startCircle.radius
|
||||
// val R2 = terminalCircle.radius
|
||||
// val line = LineSegment(startCircle.center, terminalCircle.center)
|
||||
// val d = line.begin.distanceTo(line.end)
|
||||
// val angle1 = atan2(terminalCircle.center.x - startCircle.center.x, terminalCircle.center.y - startCircle.center.y)
|
||||
// var r: Double
|
||||
// var angle2: Double
|
||||
// val routes = mapOf("RSR" to Pair(R1, R2),
|
||||
// "RSL" to Pair(R1, -R2),
|
||||
// "LSR" to Pair(-R1, R2),
|
||||
// "LSL" to Pair(-R1, -R2))
|
||||
// val segments = mutableMapOf<String, LineSegment<DoubleVector2D>>()
|
||||
// for ((route, r1r2) in routes) {
|
||||
// val r1 = r1r2.first
|
||||
// val r2 = r1r2.second
|
||||
// r = if (r1.sign == r2.sign) {
|
||||
// r1.absoluteValue - r2.absoluteValue
|
||||
// } else {
|
||||
// r1.absoluteValue + r2.absoluteValue
|
||||
// }
|
||||
// val L = (d * d - r * r).pow(0.5)
|
||||
// angle2 = if (r1.absoluteValue > r2.absoluteValue) {
|
||||
// angle1 + r1.sign * atan2(r.absoluteValue, L)
|
||||
// } else {
|
||||
// angle1 - r2.sign * atan2(r.absoluteValue, L)
|
||||
// }
|
||||
// val W = vector(-cos(angle2), sin(angle2))
|
||||
// segments[route] = LineSegment(add(startCircle.center, scale(W, r1)),
|
||||
// add(terminalCircle.center, scale(W, r2)))
|
||||
// }
|
||||
// return segments
|
||||
//}
|
@ -8,6 +8,7 @@ package space.kscience.kmath.geometry
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.vector
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TangentTest {
|
||||
@Test
|
||||
@ -15,25 +16,24 @@ class TangentTest {
|
||||
val c1 = Circle2D(vector(0.0, 0.0), 1.0)
|
||||
val c2 = Circle2D(vector(4.0, 0.0), 1.0)
|
||||
val routes = arrayListOf<String>("RSR", "RSL", "LSR", "LSL")
|
||||
val segments = arrayListOf<Segment>(
|
||||
Segment(startPoint = vector(0.0, 1.0),
|
||||
terminalPoint = vector(4.0, 1.0)),
|
||||
Segment(startPoint = vector(0.5, 0.8660254),
|
||||
terminalPoint = vector(3.5, -0.8660254)),
|
||||
Segment(startPoint = vector(0.5, -0.8660254),
|
||||
terminalPoint = vector(3.5, 0.8660254)),
|
||||
Segment(startPoint = vector(0.0, -1.0),
|
||||
terminalPoint = vector(4.0, -1.0))
|
||||
val segments = arrayListOf<LineSegment<DoubleVector2D>>(
|
||||
LineSegment<DoubleVector2D>(begin = vector(0.0, 1.0),
|
||||
end = vector(4.0, 1.0)),
|
||||
LineSegment<DoubleVector2D>(begin = vector(0.5, 0.8660254),
|
||||
end = vector(3.5, -0.8660254)),
|
||||
LineSegment<DoubleVector2D>(begin = vector(0.5, -0.8660254),
|
||||
end = vector(3.5, 0.8660254)),
|
||||
LineSegment<DoubleVector2D>(begin = vector(0.0, -1.0),
|
||||
end = vector(4.0, -1.0))
|
||||
)
|
||||
|
||||
val tangentMap = tangentsToCircles(c1, c2)
|
||||
val tangentMap = c1.tangentsToCircle(c2)
|
||||
val tangentMapKeys = tangentMap.keys.toList()
|
||||
val tangentMapValues = tangentMap.values.toList()
|
||||
|
||||
assertEquals(routes, tangentMapKeys)
|
||||
for (i in segments.indices) {
|
||||
assertEquals(segments[i], tangentMapValues[i])
|
||||
}
|
||||
// assertEquals(segments, tangentMapValues)
|
||||
assertTrue(equalLineSegments(segments[i], tangentMapValues[i]))
|
||||
}
|
||||
}
|
||||
}
|
@ -44,3 +44,6 @@ fun <V : Vector> GeometrySpace<V>.isCollinear(a: V, b: V, absoluteTolerance: Dou
|
||||
|
||||
fun <V : Vector> GeometrySpace<V>.isOrthogonal(a: V, b: V, absoluteTolerance: Double = 1e-6): Boolean =
|
||||
abs(a dot b) < absoluteTolerance
|
||||
|
||||
fun Double.equalFloat(other: Double, maxFloatDelta: Double = 0.000001):
|
||||
Boolean = kotlin.math.abs(this - other) < maxFloatDelta
|
Loading…
Reference in New Issue
Block a user