diff --git a/demo/maps/src/jvmMain/kotlin/Main.kt b/demo/maps/src/jvmMain/kotlin/Main.kt index 9ef45a5..235bcac 100644 --- a/demo/maps/src/jvmMain/kotlin/Main.kt +++ b/demo/maps/src/jvmMain/kotlin/Main.kt @@ -74,7 +74,7 @@ fun App() { .modifyAttribute(ColorAttribute, Color.Blue) .modifyAttribute(AlphaAttribute, 0.4f) - image(pointOne, Icons.Filled.Home) + icon(pointOne, Icons.Filled.Home) val marker1 = rectangle(55.744 to 38.614, size = DpSize(10.dp, 10.dp)).color(Color.Magenta) val marker2 = rectangle(55.8 to 38.5, size = DpSize(10.dp, 10.dp)).color(Color.Magenta) diff --git a/maps-kt-compose/build.gradle.kts b/maps-kt-compose/build.gradle.kts index 0d821a7..8185226 100644 --- a/maps-kt-compose/build.gradle.kts +++ b/maps-kt-compose/build.gradle.kts @@ -1,13 +1,14 @@ plugins { - kotlin("multiplatform") + id("space.kscience.gradle.mpp") id("org.jetbrains.compose") `maven-publish` } -kotlin { - explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Warning - jvmToolchain(11) +kscience{ jvm() +} + +kotlin { sourceSets { commonMain { dependencies { @@ -19,8 +20,6 @@ kotlin { api("io.github.microutils:kotlin-logging:2.1.23") } } - val jvmMain by getting { - } val jvmTest by getting { dependencies { implementation("io.ktor:ktor-client-cio") @@ -28,18 +27,11 @@ kotlin { implementation(spclibs.kotlinx.coroutines.test) implementation(spclibs.logback.classic) - - implementation(kotlin("test-junit5")) - implementation("org.junit.jupiter:junit-jupiter:${spclibs.versions.junit.get()}") } } } } -tasks.withType { - useJUnitPlatform() -} - readme { description = "Compose-multiplaform implementation for web-mercator tiled maps" maturity = space.kscience.gradle.Maturity.EXPERIMENTAL diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/mapFeatures.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/mapFeatures.kt index d8cd0dd..fa17d6c 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/mapFeatures.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/mapFeatures.kt @@ -92,14 +92,14 @@ public fun FeatureGroup.multiLine( id: String? = null, ): FeatureRef> = feature(id, MultiLineFeature(space, points.map(::coordinatesOf))) -public fun FeatureGroup.image( +public fun FeatureGroup.icon( position: Pair, image: ImageVector, size: DpSize = DpSize(20.dp, 20.dp), id: String? = null, -): FeatureRef> = feature( +): FeatureRef> = feature( id, - VectorImageFeature( + VectorIconFeature( space, coordinatesOf(position), size, diff --git a/maps-kt-core/src/commonMain/kotlin/center/sciprog/maps/coordinates/GeodeticMapCoordinates.kt b/maps-kt-core/src/commonMain/kotlin/center/sciprog/maps/coordinates/GeodeticMapCoordinates.kt index 5eaea24..8484b3b 100644 --- a/maps-kt-core/src/commonMain/kotlin/center/sciprog/maps/coordinates/GeodeticMapCoordinates.kt +++ b/maps-kt-core/src/commonMain/kotlin/center/sciprog/maps/coordinates/GeodeticMapCoordinates.kt @@ -1,10 +1,7 @@ package center.sciprog.maps.coordinates import kotlinx.serialization.Serializable -import space.kscience.kmath.geometry.Angle -import space.kscience.kmath.geometry.degrees -import space.kscience.kmath.geometry.normalized -import space.kscience.kmath.geometry.radians +import space.kscience.kmath.geometry.* /** * Geodetic coordinated @@ -16,7 +13,7 @@ public class GeodeticMapCoordinates( public val latitude: Angle, public val longitude: Angle, public val elevation: Distance? = null, -) { +) : Vector2D { init { require(latitude in (-Angle.piDiv2)..(Angle.piDiv2)) { "Latitude $latitude is not in (-PI/2)..(PI/2)" @@ -26,16 +23,17 @@ public class GeodeticMapCoordinates( } } + override val x: Angle get() = longitude + + override val y: Angle get() = latitude + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false other as GeodeticMapCoordinates - if (latitude != other.latitude) return false - if (longitude != other.longitude) return false - - return true + return latitude == other.latitude && longitude == other.longitude } override fun hashCode(): Int { diff --git a/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/Feature.kt b/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/Feature.kt index 24f9a99..110a3a1 100644 --- a/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/Feature.kt +++ b/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/Feature.kt @@ -267,8 +267,11 @@ public data class DrawFeature( override fun withAttributes(modify: (Attributes) -> Attributes): Feature = copy(attributes = modify(attributes)) } +/** + * Fixed size bitmap icon + */ @Stable -public data class BitmapImageFeature( +public data class BitmapIconFeature( override val space: CoordinateSpace, override val center: T, public val size: DpSize, @@ -282,8 +285,11 @@ public data class BitmapImageFeature( override fun withAttributes(modify: (Attributes) -> Attributes): Feature = copy(attributes = modify(attributes)) } +/** + * Fixed size vector icon + */ @Stable -public data class VectorImageFeature( +public data class VectorIconFeature( override val space: CoordinateSpace, override val center: T, public val size: DpSize, @@ -301,7 +307,7 @@ public data class VectorImageFeature( } /** - * An image that is bound to coordinates and is scaled together with them + * An image that is bound to coordinates and is scaled (and possibly warped) together with them * * @param rectangle the size of background in scheme size units. The screen units to scheme units ratio equals scale. */ diff --git a/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/FeatureGroup.kt b/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/FeatureGroup.kt index 2d44da7..8cbb6ff 100644 --- a/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/FeatureGroup.kt +++ b/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/FeatureGroup.kt @@ -235,16 +235,16 @@ public fun FeatureGroup.polygon( PolygonFeature(space, points, attributes) ) -public fun FeatureGroup.image( +public fun FeatureGroup.icon( position: T, image: ImageVector, size: DpSize = DpSize(image.defaultWidth, image.defaultHeight), attributes: Attributes = Attributes.EMPTY, id: String? = null, -): FeatureRef> = +): FeatureRef> = feature( id, - VectorImageFeature( + VectorIconFeature( space, position, size, diff --git a/maps-kt-features/src/jvmMain/kotlin/center/sciprog/maps/features/drawFeature.kt b/maps-kt-features/src/jvmMain/kotlin/center/sciprog/maps/features/drawFeature.kt index 89e69f6..8948f79 100644 --- a/maps-kt-features/src/jvmMain/kotlin/center/sciprog/maps/features/drawFeature.kt +++ b/maps-kt-features/src/jvmMain/kotlin/center/sciprog/maps/features/drawFeature.kt @@ -3,10 +3,7 @@ package center.sciprog.maps.features import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.* -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas -import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.graphics.drawscope.* import androidx.compose.ui.graphics.painter.Painter import center.sciprog.attributes.plus import org.jetbrains.skia.Font @@ -72,9 +69,9 @@ public fun DrawScope.drawFeature( } - is BitmapImageFeature -> drawImage(feature.image, feature.center.toOffset()) + is BitmapIconFeature -> drawImage(feature.image, feature.center.toOffset()) - is VectorImageFeature -> { + is VectorIconFeature -> { val offset = feature.center.toOffset() val size = feature.size.toSize() translate(offset.x - size.width / 2, offset.y - size.height / 2) { diff --git a/maps-kt-geotiff/build.gradle.kts b/maps-kt-geotiff/build.gradle.kts new file mode 100644 index 0000000..bcce48f --- /dev/null +++ b/maps-kt-geotiff/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("space.kscience.gradle.jvm") + `maven-publish` +} + +repositories { + maven("https://repo.osgeo.org/repository/release/") +} + +dependencies { + api("org.geotools:gt-geotiff:27.2") { + exclude(group = "javax.media", module = "jai_core") + } + + api(projects.mapsKtCore) + api(projects.mapsKtFeatures) +} diff --git a/maps-kt-geotiff/src/main/kotlin/center/sciprog/maps/geotiff/geotiff.kt b/maps-kt-geotiff/src/main/kotlin/center/sciprog/maps/geotiff/geotiff.kt new file mode 100644 index 0000000..e77ba3f --- /dev/null +++ b/maps-kt-geotiff/src/main/kotlin/center/sciprog/maps/geotiff/geotiff.kt @@ -0,0 +1,26 @@ +package center.sciprog.maps.geotiff + +import center.sciprog.maps.coordinates.Gmc +import center.sciprog.maps.features.Feature +import center.sciprog.maps.features.FeatureGroup +import center.sciprog.maps.features.FeatureRef +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject +import org.geotools.gce.geotiff.GeoTiffReader +import org.geotools.util.factory.Hints +import java.io.File +import java.net.URL + + +public fun FeatureGroup.geoJson( + geoTiffUrl: URL, + id: String? = null, +): FeatureRef> { + val reader = GeoTiffReader + val jsonString = geoJsonUrl.readText() + val json = Json.parseToJsonElement(jsonString).jsonObject + val geoJson = GeoJson(json) + + return geoJson(geoJson, id) +} + diff --git a/maps-kt-scheme/build.gradle.kts b/maps-kt-scheme/build.gradle.kts index 49ca678..48c27b8 100644 --- a/maps-kt-scheme/build.gradle.kts +++ b/maps-kt-scheme/build.gradle.kts @@ -1,11 +1,14 @@ plugins { - kotlin("multiplatform") + id("space.kscience.gradle.mpp") id("org.jetbrains.compose") `maven-publish` } -kotlin { +kscience{ jvm() +} + +kotlin { sourceSets { commonMain { dependencies { diff --git a/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XY.kt b/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XY.kt index 298d60a..8895cbd 100644 --- a/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XY.kt +++ b/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XY.kt @@ -5,11 +5,12 @@ import androidx.compose.ui.unit.dp import center.sciprog.maps.features.CoordinateSpace import center.sciprog.maps.features.Rectangle import center.sciprog.maps.features.ViewPoint +import space.kscience.kmath.geometry.Vector2D import kotlin.math.abs import kotlin.math.max import kotlin.math.min -data class XY(val x: Float, val y: Float) +public data class XY(override val x: Float, override val y: Float): Vector2D internal data class XYRectangle( override val a: XY, @@ -28,21 +29,21 @@ internal data class XYRectangle( // } } -val Rectangle.top get() = max(a.y, b.y) -val Rectangle.bottom get() = min(a.y, b.y) +public val Rectangle.top: Float get() = max(a.y, b.y) +public val Rectangle.bottom: Float get() = min(a.y, b.y) -val Rectangle.right get() = max(a.x, b.x) -val Rectangle.left get() = min(a.x, b.x) +public val Rectangle.right: Float get() = max(a.x, b.x) +public val Rectangle.left: Float get() = min(a.x, b.x) -val Rectangle.width: Float get() = abs(a.x - b.x) -val Rectangle.height: Float get() = abs(a.y - b.y) +public val Rectangle.width: Float get() = abs(a.x - b.x) +public val Rectangle.height: Float get() = abs(a.y - b.y) public val Rectangle.leftTop: XY get() = XY(left, top) public val Rectangle.rightBottom: XY get() = XY(right, bottom) internal val defaultCanvasSize = DpSize(512.dp, 512.dp) -data class XYViewPoint( +public data class XYViewPoint( override val focus: XY, override val zoom: Float = 1f, ) : ViewPoint diff --git a/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XYCoordinateSpace.kt b/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XYCoordinateSpace.kt index 74e1c6f..7aced67 100644 --- a/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XYCoordinateSpace.kt +++ b/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XYCoordinateSpace.kt @@ -9,7 +9,7 @@ import center.sciprog.maps.features.ViewPoint import kotlin.math.abs import kotlin.math.pow -object XYCoordinateSpace : CoordinateSpace { +public object XYCoordinateSpace : CoordinateSpace { override fun Rectangle(first: XY, second: XY): Rectangle = XYRectangle(first, second) diff --git a/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XYViewScope.kt b/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XYViewScope.kt index 6454b8a..550a66a 100644 --- a/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XYViewScope.kt +++ b/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/XYViewScope.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.unit.dp import center.sciprog.maps.features.* import kotlin.math.min -class XYViewScope( +public class XYViewScope( config: ViewConfig, ) : CoordinateViewScope(config) { override val space: CoordinateSpace @@ -45,7 +45,7 @@ class XYViewScope( return DpRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y) } - companion object{ + public companion object{ @Composable public fun remember( config: ViewConfig = ViewConfig(), diff --git a/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/schemeFeatures.kt b/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/schemeFeatures.kt index 7548576..be229a8 100644 --- a/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/schemeFeatures.kt +++ b/maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme/schemeFeatures.kt @@ -15,7 +15,7 @@ import kotlin.math.ceil internal fun Pair.toCoordinates(): XY = XY(first.toFloat(), second.toFloat()) -fun FeatureGroup.background( +public fun FeatureGroup.background( width: Float, height: Float, offset: XY = XY(0f, 0f), @@ -37,19 +37,19 @@ fun FeatureGroup.background( ) } -fun FeatureGroup.circle( +public fun FeatureGroup.circle( centerCoordinates: Pair, size: Dp = 5.dp, id: String? = null, ): FeatureRef> = circle(centerCoordinates.toCoordinates(), size, id = id) -fun FeatureGroup.draw( +public fun FeatureGroup.draw( position: Pair, id: String? = null, draw: DrawScope.() -> Unit, ): FeatureRef> = draw(position.toCoordinates(), id = id, draw = draw) -fun FeatureGroup.line( +public fun FeatureGroup.line( aCoordinates: Pair, bCoordinates: Pair, id: String? = null, @@ -69,15 +69,15 @@ public fun FeatureGroup.arc( id = id ) -fun FeatureGroup.image( +public fun FeatureGroup.image( position: Pair, image: ImageVector, size: DpSize = DpSize(image.defaultWidth, image.defaultHeight), id: String? = null, -): FeatureRef> = - image(position.toCoordinates(), image, size = size, id = id) +): FeatureRef> = + icon(position.toCoordinates(), image, size = size, id = id) -fun FeatureGroup.text( +public fun FeatureGroup.text( position: Pair, text: String, id: String? = null, diff --git a/maps-kt-scheme/src/jvmMain/kotlin/center/sciprog/maps/svg/exportToSvg.kt b/maps-kt-scheme/src/jvmMain/kotlin/center/sciprog/maps/svg/exportToSvg.kt index edac4b3..0dfb04e 100644 --- a/maps-kt-scheme/src/jvmMain/kotlin/center/sciprog/maps/svg/exportToSvg.kt +++ b/maps-kt-scheme/src/jvmMain/kotlin/center/sciprog/maps/svg/exportToSvg.kt @@ -120,9 +120,9 @@ fun FeatureStateSnapshot.generateSvg( ) } - is BitmapImageFeature -> drawImage(feature.image, feature.center.toOffset()) + is BitmapIconFeature -> drawImage(feature.image, feature.center.toOffset()) - is VectorImageFeature -> { + is VectorIconFeature -> { val offset = feature.center.toOffset() val imageSize = feature.size.toSize() translate(offset.x - imageSize.width / 2, offset.y - imageSize.height / 2) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 88a50fd..7ce183f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -48,6 +48,7 @@ include( ":trajectory-kt", ":maps-kt-core", ":maps-kt-geojson", +// ":maps-kt-geotiff", ":maps-kt-features", ":maps-kt-compose", ":maps-kt-scheme", diff --git a/trajectory-kt/src/commonMain/kotlin/space/kscience/kmath/trajectory/Obstacle.kt b/trajectory-kt/src/commonMain/kotlin/space/kscience/kmath/trajectory/Obstacle.kt index 32061ef..18c8cf7 100644 --- a/trajectory-kt/src/commonMain/kotlin/space/kscience/kmath/trajectory/Obstacle.kt +++ b/trajectory-kt/src/commonMain/kotlin/space/kscience/kmath/trajectory/Obstacle.kt @@ -35,113 +35,113 @@ private fun TangentPath(vararg tangents: Tangent) = TangentPath(listOf(*tangents * Create inner and outer tangents between two circles. * This method returns a map of segments using [DubinsPath] connection type notation. */ -internal fun Circle2D.tangentsToCircle( - other: Circle2D, +internal fun tangentsToCircle( + first: Circle2D, + second: Circle2D, ): Map = with(Euclidean2DSpace) { //return empty map for concentric circles - if (center.equalsVector(other.center)) return emptyMap() + if (first.center.equalsVector(second.center)) return emptyMap() // A line connecting centers - val line = LineSegment(center, other.center) + val line = LineSegment(first.center, second.center) // Distance between centers val distance = line.begin.distanceTo(line.end) - val angle1 = atan2(other.center.x - center.x, other.center.y - center.y) + val angle1 = atan2(second.center.x - first.center.x, second.center.y - first.center.y) 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 - val r = if (r1.sign == r2.sign) { - r1.absoluteValue - r2.absoluteValue - } else { - r1.absoluteValue + r2.absoluteValue - } - if (distance * distance >= r * r) { - val l = sqrt(distance * distance - r * r) - 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 - ) - ) - } else { - throw Exception("Circles should not intersect") - } + return listOf( + DubinsPath.Type.RSR, + DubinsPath.Type.RSL, + DubinsPath.Type.LSR, + DubinsPath.Type.LSL + ).associateWith { route -> + val r1 = when (route.first) { + Trajectory2D.L -> -first.radius + Trajectory2D.R -> first.radius } - } -} + val r2 = when (route.third) { + Trajectory2D.L -> -second.radius + Trajectory2D.R -> second.radius + } + val r = if (r1.sign == r2.sign) { + r1.absoluteValue - r2.absoluteValue + } else { + r1.absoluteValue + r2.absoluteValue + } + if (distance * distance < r * r) error("Circles should not intersect") -private fun dubinsTangentsToCircles( - firstCircle: Circle2D, - secondCircle: Circle2D, - firstObstacle: Obstacle, - secondObstacle: Obstacle, -): Map = with(Euclidean2DSpace) { - val line = LineSegment(firstCircle.center, secondCircle.center) - val distance = line.begin.distanceTo(line.end) - val angle1 = atan2( - secondCircle.center.x - firstCircle.center.x, - secondCircle.center.y - firstCircle.center.y - ) - var r: Double - var angle2: Double - val routes = mapOf( - DubinsPath.Type.RSR to Pair(firstCircle.radius, secondCircle.radius), - DubinsPath.Type.RSL to Pair(firstCircle.radius, -secondCircle.radius), - DubinsPath.Type.LSR to Pair(-firstCircle.radius, secondCircle.radius), - DubinsPath.Type.LSL to Pair(-firstCircle.radius, -secondCircle.radius) - ) - return buildMap { - for ((route: DubinsPath.Type, 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 - } - if (distance * distance >= r * r) { - val l = sqrt(distance * distance - r * r) - 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, - Tangent( - startCircle = Circle2D(firstCircle.center, firstCircle.radius), - endCircle = secondCircle, - startObstacle = firstObstacle, - endObstacle = secondObstacle, - lineSegment = LineSegment( - firstCircle.center + w * r1, - secondCircle.center + w * r2 - ), - startDirection = route.first, - endDirection = route.third - ) - ) - } else { - throw Exception("Circles should not intersect") - } + val l = sqrt(distance * distance - r * r) + 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)) + + LineSegment( + first.center + w * r1, + second.center + w * r2 + ) } } +// +//private fun dubinsTangentsToCircles( +// firstCircle: Circle2D, +// secondCircle: Circle2D, +// firstObstacle: Obstacle, +// secondObstacle: Obstacle, +//): Map = with(Euclidean2DSpace) { +// val line = LineSegment(firstCircle.center, secondCircle.center) +// val distance = line.begin.distanceTo(line.end) +// val angle1 = atan2( +// secondCircle.center.x - firstCircle.center.x, +// secondCircle.center.y - firstCircle.center.y +// ) +// var r: Double +// var angle2: Double +// val routes = mapOf( +// DubinsPath.Type.RSR to Pair(firstCircle.radius, secondCircle.radius), +// DubinsPath.Type.RSL to Pair(firstCircle.radius, -secondCircle.radius), +// DubinsPath.Type.LSR to Pair(-firstCircle.radius, secondCircle.radius), +// DubinsPath.Type.LSL to Pair(-firstCircle.radius, -secondCircle.radius) +// ) +// return buildMap { +// for ((route: DubinsPath.Type, 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 +// } +// if (distance * distance >= r * r) { +// val l = sqrt(distance * distance - r * r) +// 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, +// Tangent( +// startCircle = Circle2D(firstCircle.center, firstCircle.radius), +// endCircle = secondCircle, +// startObstacle = firstObstacle, +// endObstacle = secondObstacle, +// lineSegment = LineSegment( +// firstCircle.center + w * r1, +// secondCircle.center + w * r2 +// ), +// startDirection = route.first, +// endDirection = route.third +// ) +// ) +// } else { +// throw Exception("Circles should not intersect") +// } +// } +// } +//} internal class Obstacle( public val circles: List, @@ -335,13 +335,23 @@ private fun Tangent.intersectObstacle(obstacle: Obstacle): Boolean { private fun outerTangents(first: Obstacle, second: Obstacle): Map = buildMap { for (circle1 in first.circles) { for (circle2 in second.circles) { - for (tangent in dubinsTangentsToCircles(circle1, circle2, first, second)) { - if (!(tangent.value.intersectObstacle(first)) - and !(tangent.value.intersectObstacle(second)) + for ((pathType, segment) in tangentsToCircle(circle1, circle2)) { + val tangent = Tangent( + circle1, + circle2, + first, + second, + segment, + pathType.first, + pathType.third + ) + + if (!(tangent.intersectObstacle(first)) + and !(tangent.intersectObstacle(second)) ) { put( - tangent.key, - tangent.value + pathType, + tangent ) } } @@ -506,7 +516,7 @@ internal fun findAllPaths( val currentObstacle = tangentPath.last().endObstacle var nextObstacle: Obstacle? = null if (currentObstacle != finalObstacle) { - val tangentToFinal = outerTangents(currentObstacle, finalObstacle)[DubinsPath.Type( + val tangentToFinal: Tangent? = outerTangents(currentObstacle, finalObstacle)[DubinsPath.Type( currentDirection, Trajectory2D.S, j diff --git a/trajectory-kt/src/commonTest/kotlin/space/kscience/kmath/trajectory/TangentTest.kt b/trajectory-kt/src/commonTest/kotlin/space/kscience/kmath/trajectory/TangentTest.kt index 6fc00fb..28db6b9 100644 --- a/trajectory-kt/src/commonTest/kotlin/space/kscience/kmath/trajectory/TangentTest.kt +++ b/trajectory-kt/src/commonTest/kotlin/space/kscience/kmath/trajectory/TangentTest.kt @@ -44,7 +44,7 @@ class TangentTest { ) ) - val tangentMap = c1.tangentsToCircle(c2) + val tangentMap = tangentsToCircle(c1, c2) val tangentMapKeys = tangentMap.keys.toList() val tangentMapValues = tangentMap.values.toList() @@ -58,6 +58,6 @@ class TangentTest { fun concentric(){ val c1 = Circle2D(vector(0.0, 0.0), 10.0) val c2 = Circle2D(vector(0.0, 0.0), 1.0) - assertEquals(emptyMap(), c1.tangentsToCircle(c2)) + assertEquals(emptyMap(), tangentsToCircle(c1, c2)) } } \ No newline at end of file