diff --git a/trajectory-kt/src/commonMain/kotlin/space/kscience/kmath/geometry/geometryExtensions.kt b/trajectory-kt/src/commonMain/kotlin/space/kscience/kmath/geometry/geometryExtensions.kt index cde9a43..b8b4686 100644 --- a/trajectory-kt/src/commonMain/kotlin/space/kscience/kmath/geometry/geometryExtensions.kt +++ b/trajectory-kt/src/commonMain/kotlin/space/kscience/kmath/geometry/geometryExtensions.kt @@ -1,9 +1,10 @@ package space.kscience.kmath.geometry -import space.kscience.kmath.operations.DoubleField.pow import space.kscience.trajectory.* import kotlin.math.abs +import kotlin.math.pow import kotlin.math.sign +import kotlin.math.sqrt public fun Euclidean2DSpace.circle(x: Number, y: Number, radius: Number): Circle2D = Circle2D(vector(x, y), radius = radius.toDouble()) @@ -23,17 +24,25 @@ public fun Euclidean2DSpace.intersectsOrInside(circle1: Circle2D, circle2: Circl * https://mathworld.wolfram.com/Circle-LineIntersection.html */ public fun Euclidean2DSpace.intersects(segment: LineSegment2D, circle: Circle2D): Boolean { - val begin = segment.begin - val end = segment.end - val d = begin.distanceTo(end) - val det = (begin.x - circle.center.x) * (end.y - circle.center.y) - - (end.x - circle.center.x) * (begin.y - circle.center.y) + val direction = segment.end - segment.begin + val radiusVector = segment.begin - circle.center - val incidence = circle.radius.pow(2) * d.pow(2) - det.pow(2) + val a = direction dot direction + val b = 2 * (radiusVector dot direction) + val c = (radiusVector dot radiusVector) - circle.radius.pow(2) - return incidence >= 0 + val discriminantSquared = b * b - 4 * a * c + if (discriminantSquared < 0) return false + + val discriminant = sqrt(discriminantSquared) + + val t1 = (-b - discriminant) / (2 * a) // first intersection point in relative coordinates + val t2 = (-b + discriminant) / (2 * a) //second intersection point in relative coordinates + + return t1.sign != t2.sign || (t1-1.0).sign != (t2-1).sign } + public fun Euclidean2DSpace.intersects(circle: Circle2D, segment: LineSegment2D): Boolean = intersects(segment, circle) diff --git a/trajectory-kt/src/commonMain/kotlin/space/kscience/trajectory/Obstacles.kt b/trajectory-kt/src/commonMain/kotlin/space/kscience/trajectory/Obstacles.kt index 2daba4c..3bc03c8 100644 --- a/trajectory-kt/src/commonMain/kotlin/space/kscience/trajectory/Obstacles.kt +++ b/trajectory-kt/src/commonMain/kotlin/space/kscience/trajectory/Obstacles.kt @@ -149,7 +149,7 @@ public class Obstacles(public val obstacles: List) { //cutting first and last arcs to accommodate connection points val first = circumvention.first() as CircleTrajectory2D val last = circumvention.last() as CircleTrajectory2D - //arc between end of the nangent and end of previous arc (begin of the the next one) + //arc between end of the tangent and end of previous arc (begin of the next one) circumvention[0] = CircleTrajectory2D( first.circle, tangent1.tangentTrajectory.endPose, @@ -182,7 +182,7 @@ public class Obstacles(public val obstacles: List) { private fun avoiding( dubinsPath: CompositeTrajectory2D, ): Collection = with(Euclidean2DSpace) { - //fast return if no obstacles intersect direct path + //fast return if no obstacles intersect the direct path if (obstacles.none { it.intersects(dubinsPath) }) return listOf(dubinsPath) val beginArc = dubinsPath.segments.first() as CircleTrajectory2D @@ -208,7 +208,8 @@ public class Obstacles(public val obstacles: List) { endArc ) ?: return emptySet() - if (remainingObstacleIndices.none { obstacles[it].intersects(tangentToEnd.tangentTrajectory) }) return setOf( + // if no intersections, finish + if (obstacles.indices.none { obstacles[it].intersects(tangentToEnd.tangentTrajectory) }) return setOf( TangentPath(tangents + tangentToEnd) ) diff --git a/trajectory-kt/src/commonTest/kotlin/space/kscience/trajectory/ObstacleTest.kt b/trajectory-kt/src/commonTest/kotlin/space/kscience/trajectory/ObstacleTest.kt index 14ba762..5d0d06b 100644 --- a/trajectory-kt/src/commonTest/kotlin/space/kscience/trajectory/ObstacleTest.kt +++ b/trajectory-kt/src/commonTest/kotlin/space/kscience/trajectory/ObstacleTest.kt @@ -89,13 +89,14 @@ class ObstacleTest { ) val paths: List = Obstacles.avoidObstacles( - Pose2D(-0.9, -0.9, Angle.pi), - Pose2D(-0.9, -0.9, Angle.piDiv2), + Pose2D(-1, -1, Angle.pi), + Pose2D(-1, -1, Angle.piDiv2), 1.0, obstacle ) assertTrue { paths.isNotEmpty() } - assertEquals(12.0, paths.minOf { it.length }, 2.0) + assertEquals(9.5, paths.minOf { it.length }, 1.0) + assertEquals(12.5, paths.maxOf { it.length }, 1.0) } @Test