diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt index dc05f6648..3df8dba7b 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt @@ -85,13 +85,6 @@ public object Euclidean2DSpace : GeometrySpace, 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, line2: LineSegment): 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) } diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt index adc3a8b85..ab322ddca 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt @@ -27,8 +27,5 @@ public fun LineSegment.line(algebra: GeometrySpace): Line 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 public typealias LineSegment3D = LineSegment diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/floatPrecision.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/floatPrecision.kt new file mode 100644 index 000000000..023355a0f --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/floatPrecision.kt @@ -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.equalsVector( + space: GeometrySpace, + 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 LineSegment.equalsLine( + space: GeometrySpace, + other: LineSegment, + precision: Double = defaultPrecision, +): Boolean = begin.equalsVector(space, other.begin, precision) && end.equalsVector(space, other.end, precision) \ No newline at end of file diff --git a/kmath-trajectory/src/commonMain/kotlin/space/kscience/kmath/trajectory/Tangent.kt b/kmath-trajectory/src/commonMain/kotlin/space/kscience/kmath/trajectory/Tangent.kt deleted file mode 100644 index 9546a8d64..000000000 --- a/kmath-trajectory/src/commonMain/kotlin/space/kscience/kmath/trajectory/Tangent.kt +++ /dev/null @@ -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> { - 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>() - 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 -} \ No newline at end of file diff --git a/kmath-trajectory/src/commonMain/kotlin/space/kscience/kmath/trajectory/tangent.kt b/kmath-trajectory/src/commonMain/kotlin/space/kscience/kmath/trajectory/tangent.kt new file mode 100644 index 000000000..70a1ac696 --- /dev/null +++ b/kmath-trajectory/src/commonMain/kotlin/space/kscience/kmath/trajectory/tangent.kt @@ -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> = 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 + ) + ) + } + } +} \ No newline at end of file diff --git a/kmath-trajectory/src/commonTest/kotlin/space/kscience/kmath/trajectory/TangentTest.kt b/kmath-trajectory/src/commonTest/kotlin/space/kscience/kmath/trajectory/TangentTest.kt new file mode 100644 index 000000000..c1cf2f800 --- /dev/null +++ b/kmath-trajectory/src/commonTest/kotlin/space/kscience/kmath/trajectory/TangentTest.kt @@ -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])) + } + } +} \ No newline at end of file diff --git a/kmath-trajectory/src/commonTest/kotlin/space/kscience/kmath/trajectory/segments/TangentTest.kt b/kmath-trajectory/src/commonTest/kotlin/space/kscience/kmath/trajectory/segments/TangentTest.kt deleted file mode 100644 index cfa2fa52b..000000000 --- a/kmath-trajectory/src/commonTest/kotlin/space/kscience/kmath/trajectory/segments/TangentTest.kt +++ /dev/null @@ -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.RSR, - DubinsRoutes.RSL, - DubinsRoutes.LSR, - DubinsRoutes.LSL - ) - val segments = arrayListOf>( - 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(equalLineSegments(segments[i], tangentMapValues[i])) - } - } -} \ No newline at end of file