tangentsToCircle fixed

This commit is contained in:
Artyom Degtyarev 2023-02-15 14:36:58 +03:00
parent 50579f4712
commit bef317677c
5 changed files with 116 additions and 68 deletions

View File

@ -6,7 +6,8 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.math.PI import space.kscience.kmath.geometry.Euclidean2DSpace.distanceTo
import kotlin.math.*
/** /**
* A circle in 2D space * A circle in 2D space
@ -17,4 +18,40 @@ public data class Circle2D(
public val radius: Double 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 public val Circle2D.circumference: Double get() = radius * 2 * PI

View File

@ -27,5 +27,15 @@ public fun <V : Vector> LineSegment<V>.line(algebra: GeometrySpace<V>): Line<V>
Line(begin, end - begin) 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 LineSegment2D = LineSegment<DoubleVector2D>
public typealias LineSegment3D = LineSegment<DoubleVector3D> public typealias LineSegment3D = LineSegment<DoubleVector3D>

View File

@ -5,9 +5,6 @@
package space.kscience.kmath.geometry 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.absoluteValue
import kotlin.math.atan2 import kotlin.math.atan2
import kotlin.math.pow import kotlin.math.pow
@ -15,63 +12,64 @@ import kotlin.math.sign
import kotlin.math.sin import kotlin.math.sin
import kotlin.math.cos import kotlin.math.cos
import space.kscience.kmath.geometry.Euclidean2DSpace.vector 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.scale
import space.kscience.kmath.geometry.Euclidean2DSpace.add import space.kscience.kmath.geometry.Euclidean2DSpace.add
import space.kscience.kmath.geometry.Euclidean2DSpace.distanceTo
public class Segment( //public class Segment(
public val startPoint: DoubleVector2D, // public val startPoint: DoubleVector2D,
public val terminalPoint: DoubleVector2D, // public val terminalPoint: DoubleVector2D,
public val length: Double = startPoint.distanceTo(terminalPoint) // public val length: Double = startPoint.distanceTo(terminalPoint)
) { //) {
public override operator fun equals(other: Any?): Boolean { // public override operator fun equals(other: Any?): Boolean {
return if (other is Segment) { // return if (other is Segment) {
startPoint.x.equalFloat(other.startPoint.x) && startPoint.y.equalFloat(other.startPoint.y) && // startPoint.x.equalFloat(other.startPoint.x) && startPoint.y.equalFloat(other.startPoint.y) &&
terminalPoint.x.equalFloat(other.terminalPoint.x) && terminalPoint.y.equalFloat(other.terminalPoint.y) // terminalPoint.x.equalFloat(other.terminalPoint.x) && terminalPoint.y.equalFloat(other.terminalPoint.y)
} else { // } else {
false // false
} // }
} // }
} //}
public const val maxFloatDelta: Double = 0.000001 //public const val maxFloatDelta: Double = 0.000001
public fun Double.equalFloat(other: Double): Boolean = kotlin.math.abs(this - other) < maxFloatDelta //public fun Double.equalFloat(other: Double): Boolean = kotlin.math.abs(this - other) < maxFloatDelta
public fun tangentsToCircles( //public fun tangentsToCircles(
startCircle: Circle2D, // startCircle: Circle2D,
terminalCircle: Circle2D // terminalCircle: Circle2D
): kotlin.collections.MutableMap<String, Segment> { //): kotlin.collections.MutableMap<String, LineSegment<DoubleVector2D>> {
val R1 = startCircle.radius // val R1 = startCircle.radius
val R2 = terminalCircle.radius // val R2 = terminalCircle.radius
val d = Segment(startCircle.center, terminalCircle.center).length // val line = LineSegment(startCircle.center, terminalCircle.center)
val angle1 = atan2(terminalCircle.center.x - startCircle.center.x, terminalCircle.center.y - startCircle.center.y) // val d = line.begin.distanceTo(line.end)
var r: Double // val angle1 = atan2(terminalCircle.center.x - startCircle.center.x, terminalCircle.center.y - startCircle.center.y)
var angle2: Double // var r: Double
val routes = mapOf("RSR" to Pair(R1, R2), // var angle2: Double
"RSL" to Pair(R1, -R2), // val routes = mapOf("RSR" to Pair(R1, R2),
"LSR" to Pair(-R1, R2), // "RSL" to Pair(R1, -R2),
"LSL" to Pair(-R1, -R2)) // "LSR" to Pair(-R1, R2),
val segments = mutableMapOf<String, Segment>() // "LSL" to Pair(-R1, -R2))
for ((route, r1r2) in routes) { // val segments = mutableMapOf<String, LineSegment<DoubleVector2D>>()
val r1 = r1r2.first // for ((route, r1r2) in routes) {
val r2 = r1r2.second // val r1 = r1r2.first
r = if (r1.sign == r2.sign) { // val r2 = r1r2.second
r1.absoluteValue - r2.absoluteValue // r = if (r1.sign == r2.sign) {
} else { // r1.absoluteValue - r2.absoluteValue
r1.absoluteValue + r2.absoluteValue // } else {
} // r1.absoluteValue + r2.absoluteValue
val L = (d * d - r * r).pow(0.5) // }
angle2 = if (r1.absoluteValue > r2.absoluteValue) { // val L = (d * d - r * r).pow(0.5)
angle1 + r1.sign * atan2(r.absoluteValue, L) // angle2 = if (r1.absoluteValue > r2.absoluteValue) {
} else { // angle1 + r1.sign * atan2(r.absoluteValue, L)
angle1 - r2.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)), // val W = vector(-cos(angle2), sin(angle2))
add(terminalCircle.center, scale(W, r2))) // segments[route] = LineSegment(add(startCircle.center, scale(W, r1)),
} // add(terminalCircle.center, scale(W, r2)))
return segments // }
} // return segments
//}

View File

@ -8,6 +8,7 @@ package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.Euclidean2DSpace.vector import space.kscience.kmath.geometry.Euclidean2DSpace.vector
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue
class TangentTest { class TangentTest {
@Test @Test
@ -15,25 +16,24 @@ class TangentTest {
val c1 = Circle2D(vector(0.0, 0.0), 1.0) val c1 = Circle2D(vector(0.0, 0.0), 1.0)
val c2 = Circle2D(vector(4.0, 0.0), 1.0) val c2 = Circle2D(vector(4.0, 0.0), 1.0)
val routes = arrayListOf<String>("RSR", "RSL", "LSR", "LSL") val routes = arrayListOf<String>("RSR", "RSL", "LSR", "LSL")
val segments = arrayListOf<Segment>( val segments = arrayListOf<LineSegment<DoubleVector2D>>(
Segment(startPoint = vector(0.0, 1.0), LineSegment<DoubleVector2D>(begin = vector(0.0, 1.0),
terminalPoint = vector(4.0, 1.0)), end = vector(4.0, 1.0)),
Segment(startPoint = vector(0.5, 0.8660254), LineSegment<DoubleVector2D>(begin = vector(0.5, 0.8660254),
terminalPoint = vector(3.5, -0.8660254)), end = vector(3.5, -0.8660254)),
Segment(startPoint = vector(0.5, -0.8660254), LineSegment<DoubleVector2D>(begin = vector(0.5, -0.8660254),
terminalPoint = vector(3.5, 0.8660254)), end = vector(3.5, 0.8660254)),
Segment(startPoint = vector(0.0, -1.0), LineSegment<DoubleVector2D>(begin = vector(0.0, -1.0),
terminalPoint = vector(4.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 tangentMapKeys = tangentMap.keys.toList()
val tangentMapValues = tangentMap.values.toList() val tangentMapValues = tangentMap.values.toList()
assertEquals(routes, tangentMapKeys) assertEquals(routes, tangentMapKeys)
for (i in segments.indices) { for (i in segments.indices) {
assertEquals(segments[i], tangentMapValues[i]) assertTrue(equalLineSegments(segments[i], tangentMapValues[i]))
} }
// assertEquals(segments, tangentMapValues)
} }
} }

View File

@ -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 = fun <V : Vector> GeometrySpace<V>.isOrthogonal(a: V, b: V, absoluteTolerance: Double = 1e-6): Boolean =
abs(a dot b) < absoluteTolerance abs(a dot b) < absoluteTolerance
fun Double.equalFloat(other: Double, maxFloatDelta: Double = 0.000001):
Boolean = kotlin.math.abs(this - other) < maxFloatDelta