forked from kscience/kmath
Cleanup after circle tangent changes
This commit is contained in:
parent
8998a394b3
commit
7d897ad8cb
@ -85,13 +85,6 @@ public object Euclidean2DSpace : GeometrySpace<DoubleVector2D>,
|
||||
override fun scale(a: DoubleVector2D, value: Double): DoubleVector2D = vector(a.x * value, a.y * value)
|
||||
override fun DoubleVector2D.dot(other: DoubleVector2D): Double = x * other.x + y * other.y
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
public val xAxis: DoubleVector2D = vector(1.0, 0.0)
|
||||
public val yAxis: DoubleVector2D = vector(0.0, 1.0)
|
||||
}
|
||||
|
@ -27,8 +27,5 @@ public fun <V : Vector> LineSegment<V>.line(algebra: GeometrySpace<V>): Line<V>
|
||||
Line(begin, end - begin)
|
||||
}
|
||||
|
||||
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>
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2018-2023 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.geometry
|
||||
|
||||
internal const val defaultPrecision = 1e-6
|
||||
|
||||
public fun Double.equalsFloat(other: Double, precision: Double = defaultPrecision): Boolean =
|
||||
kotlin.math.abs(this - other) < precision
|
||||
|
||||
public fun Double.equalsFloat(other: Float, precision: Double = defaultPrecision): Boolean =
|
||||
kotlin.math.abs(this - other) < precision
|
||||
|
||||
public fun <V : Vector> V.equalsVector(
|
||||
space: GeometrySpace<V>,
|
||||
other: V,
|
||||
precision: Double = defaultPrecision,
|
||||
): Boolean = with(space) {
|
||||
norm(this@equalsVector - other) < precision
|
||||
}
|
||||
|
||||
public fun Float64Vector2D.equalsVector(
|
||||
other: Float64Vector2D,
|
||||
precision: Double = defaultPrecision,
|
||||
): Boolean = equalsVector(Euclidean2DSpace, other, precision)
|
||||
|
||||
public fun Float64Vector3D.equalsVector(
|
||||
other: Float64Vector3D,
|
||||
precision: Double = defaultPrecision,
|
||||
): Boolean = equalsVector(Euclidean3DSpace, other, precision)
|
||||
|
||||
public fun <V : Vector> LineSegment<V>.equalsLine(
|
||||
space: GeometrySpace<V>,
|
||||
other: LineSegment<V>,
|
||||
precision: Double = defaultPrecision,
|
||||
): Boolean = begin.equalsVector(space, other.begin, precision) && end.equalsVector(space, other.end, precision)
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2023 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.trajectory
|
||||
|
||||
import space.kscience.kmath.geometry.Circle2D
|
||||
import space.kscience.kmath.geometry.DoubleVector2D
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.distanceTo
|
||||
import space.kscience.kmath.geometry.LineSegment
|
||||
import kotlin.math.*
|
||||
|
||||
public enum class DubinsRoutes {
|
||||
RSR, RSL, LSR, LSL
|
||||
}
|
||||
|
||||
public fun Circle2D.tangentsToCircle(other: Circle2D): Map<DubinsRoutes, 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(
|
||||
DubinsRoutes.RSR to Pair(R1, R2),
|
||||
DubinsRoutes.RSL to Pair(R1, -R2),
|
||||
DubinsRoutes.LSR to Pair(-R1, R2),
|
||||
DubinsRoutes.LSL to Pair(-R1, -R2)
|
||||
)
|
||||
val segments = mutableMapOf<DubinsRoutes, 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
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2018-2023 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.trajectory
|
||||
|
||||
import space.kscience.kmath.geometry.Circle2D
|
||||
import space.kscience.kmath.geometry.DoubleVector2D
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace
|
||||
import space.kscience.kmath.geometry.LineSegment
|
||||
import kotlin.math.*
|
||||
|
||||
public fun Circle2D.tangentsToCircle(
|
||||
other: Circle2D,
|
||||
): Map<DubinsPath.Type, LineSegment<DoubleVector2D>> = with(Euclidean2DSpace) {
|
||||
val line = LineSegment(center, other.center)
|
||||
val d = line.begin.distanceTo(line.end)
|
||||
val angle1 = atan2(other.center.x - center.x, other.center.y - center.y)
|
||||
var r: Double
|
||||
var angle2: Double
|
||||
val routes = mapOf(
|
||||
DubinsPath.Type.RSR to Pair(radius, other.radius),
|
||||
DubinsPath.Type.RSL to Pair(radius, -other.radius),
|
||||
DubinsPath.Type.LSR to Pair(-radius, other.radius),
|
||||
DubinsPath.Type.LSL to Pair(-radius, -other.radius)
|
||||
)
|
||||
return buildMap {
|
||||
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))
|
||||
put(
|
||||
route,
|
||||
LineSegment(
|
||||
center + w * r1,
|
||||
other.center + w * r2
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2018-2023 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.trajectory
|
||||
|
||||
import space.kscience.kmath.geometry.Circle2D
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.vector
|
||||
import space.kscience.kmath.geometry.LineSegment
|
||||
import space.kscience.kmath.geometry.equalsLine
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TangentTest {
|
||||
@Test
|
||||
fun tangent() {
|
||||
val c1 = Circle2D(vector(0.0, 0.0), 1.0)
|
||||
val c2 = Circle2D(vector(4.0, 0.0), 1.0)
|
||||
val routes = listOf(
|
||||
DubinsPath.Type.RSR,
|
||||
DubinsPath.Type.RSL,
|
||||
DubinsPath.Type.LSR,
|
||||
DubinsPath.Type.LSL
|
||||
)
|
||||
val segments = listOf(
|
||||
LineSegment(
|
||||
begin = vector(0.0, 1.0),
|
||||
end = vector(4.0, 1.0)
|
||||
),
|
||||
LineSegment(
|
||||
begin = vector(0.5, 0.8660254),
|
||||
end = vector(3.5, -0.8660254)
|
||||
),
|
||||
LineSegment(
|
||||
begin = vector(0.5, -0.8660254),
|
||||
end = vector(3.5, 0.8660254)
|
||||
),
|
||||
LineSegment(
|
||||
begin = vector(0.0, -1.0),
|
||||
end = vector(4.0, -1.0)
|
||||
)
|
||||
)
|
||||
|
||||
val tangentMap = c1.tangentsToCircle(c2)
|
||||
val tangentMapKeys = tangentMap.keys.toList()
|
||||
val tangentMapValues = tangentMap.values.toList()
|
||||
|
||||
assertEquals(routes, tangentMapKeys)
|
||||
for (i in segments.indices) {
|
||||
assertTrue(segments[i].equalsLine(Euclidean2DSpace, tangentMapValues[i]))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2023 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.trajectory
|
||||
|
||||
import space.kscience.kmath.geometry.Circle2D
|
||||
import space.kscience.kmath.geometry.DoubleVector2D
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.equalLineSegments
|
||||
import space.kscience.kmath.geometry.Euclidean2DSpace.vector
|
||||
import space.kscience.kmath.geometry.LineSegment
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TangentTest {
|
||||
@Test
|
||||
fun tangent() {
|
||||
val c1 = Circle2D(vector(0.0, 0.0), 1.0)
|
||||
val c2 = Circle2D(vector(4.0, 0.0), 1.0)
|
||||
val routes = arrayListOf<DubinsRoutes>(
|
||||
DubinsRoutes.RSR,
|
||||
DubinsRoutes.RSL,
|
||||
DubinsRoutes.LSR,
|
||||
DubinsRoutes.LSL
|
||||
)
|
||||
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 = c1.tangentsToCircle(c2)
|
||||
val tangentMapKeys = tangentMap.keys.toList()
|
||||
val tangentMapValues = tangentMap.values.toList()
|
||||
|
||||
assertEquals(routes, tangentMapKeys)
|
||||
for (i in segments.indices) {
|
||||
assertTrue(equalLineSegments(segments[i], tangentMapValues[i]))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user