From 50ccfeab70e28cc160d4c019413e5e00da973439 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 6 Feb 2023 17:19:51 +0300 Subject: [PATCH] Type safe angles --- build.gradle.kts | 2 ++ demo/maps/src/jvmMain/kotlin/Main.kt | 14 +++++++--- demo/scheme/src/jvmMain/kotlin/Main.kt | 4 +-- .../sciprog/maps/compose/GmcRectangle.kt | 5 ++-- .../sciprog/maps/compose/mapFeatures.kt | 5 ++-- maps-kt-core/build.gradle.kts | 2 +- maps-kt-features/build.gradle.kts | 3 +++ .../center/sciprog/maps/features/Feature.kt | 7 ++--- .../sciprog/maps/features/FeatureGroup.kt | 5 ++-- .../maps/features/compositeFeatures.kt | 21 ++++++++------- .../maps/features/mapFeatureAttributes.kt | 2 -- .../sciprog/maps/compose/mapControls.kt | 4 +-- .../sciprog/maps/features/drawFeature.kt | 5 ++-- .../sciprog/maps/scheme/schemeFeatures.kt | 5 ++-- .../center/sciprog/maps/svg/exportToSvg.kt | 27 ++++++++++++------- 15 files changed, 65 insertions(+), 46 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 58e19c0..e303c6a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,8 @@ plugins { id("space.kscience.gradle.project") } +val kmathVersion: String by extra("0.3.1-dev-10") + allprojects { group = "center.sciprog" version = "0.2.2-dev-1" diff --git a/demo/maps/src/jvmMain/kotlin/Main.kt b/demo/maps/src/jvmMain/kotlin/Main.kt index 89be627..ba4da06 100644 --- a/demo/maps/src/jvmMain/kotlin/Main.kt +++ b/demo/maps/src/jvmMain/kotlin/Main.kt @@ -59,7 +59,7 @@ fun App() { val pointOne = 55.568548 to 37.568604 val pointTwo = 55.929444 to 37.518434 - val pointThree = 60.929444 to 37.518434 +// val pointThree = 60.929444 to 37.518434 MapView( mapTileProvider = mapTileProvider, @@ -81,9 +81,15 @@ fun App() { val marker2 = rectangle(55.8 to 38.5, size = DpSize(10.dp, 10.dp)).color(Color.Magenta) val marker3 = rectangle(56.0 to 38.5, size = DpSize(10.dp, 10.dp)).color(Color.Magenta) - draggableLine(marker1, marker2).color(Color.Blue) - draggableLine(marker2, marker3).color(Color.Blue) - draggableLine(marker3, marker1).color(Color.Blue) + draggableLine(marker1, marker2, id = "line 1").color(Color.Red).onClick { + println("line 1 clicked") + } + draggableLine(marker2, marker3, id = "line 2").color(Color.DarkGray).onClick { + println("line 2 clicked") + } + draggableLine(marker3, marker1, id = "line 3").color(Color.Blue).onClick { + println("line 3 clicked") + } points( points = listOf( diff --git a/demo/scheme/src/jvmMain/kotlin/Main.kt b/demo/scheme/src/jvmMain/kotlin/Main.kt index ec4acde..991d6e5 100644 --- a/demo/scheme/src/jvmMain/kotlin/Main.kt +++ b/demo/scheme/src/jvmMain/kotlin/Main.kt @@ -19,9 +19,9 @@ import center.sciprog.maps.svg.snapshot import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import space.kscience.kmath.geometry.Angle import java.awt.Desktop import java.nio.file.Files -import kotlin.math.PI @Composable @Preview @@ -35,7 +35,7 @@ fun App() { text(410.52737 to 868.7676, "Shire").color(Color.Blue) circle(1132.0881 to 394.99127).color(Color.Red) text(1132.0881 to 394.99127, "Ordruin").color(Color.Red) - arc(center = 1132.0881 to 394.99127, radius = 20f, startAngle = 0f, 2 * PI.toFloat()) + arc(center = 1132.0881 to 394.99127, radius = 20f, startAngle = Angle.zero, Angle.piTimes2) //circle(410.52737 to 868.7676, id = "hobbit") diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/GmcRectangle.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/GmcRectangle.kt index e276a66..5401340 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/GmcRectangle.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/GmcRectangle.kt @@ -6,6 +6,8 @@ import center.sciprog.maps.features.Rectangle import space.kscience.kmath.geometry.Angle import space.kscience.kmath.geometry.abs +internal fun Angle.isBetween(a: Angle, b: Angle) = this in a..b || this in b..a + /** * A section of the map between two parallels and two meridians. The figure represents a square in a Mercator projection. * Params are two opposing "corners" of quasi-square. @@ -18,8 +20,7 @@ internal data class GmcRectangle( ) : Rectangle { override fun contains(point: Gmc): Boolean = - point.latitude in a.latitude..b.latitude - && point.longitude in a.longitude..b.longitude + point.latitude.isBetween(a.latitude, b.latitude) && point.longitude.isBetween(a.longitude, b.longitude) } public val Rectangle.center: GeodeticMapCoordinates 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 c28bda7..204e98b 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 @@ -12,7 +12,6 @@ import center.sciprog.maps.coordinates.Gmc import center.sciprog.maps.coordinates.GmcCurve import center.sciprog.maps.features.* import space.kscience.kmath.geometry.Angle -import space.kscience.kmath.geometry.radians internal fun FeatureGroup.coordinatesOf(pair: Pair) = @@ -77,8 +76,8 @@ public fun FeatureGroup.arc( ArcFeature( space, oval = space.Rectangle(coordinatesOf(center), radius, radius), - startAngle = startAngle.radians.toFloat(), - arcLength = arcLength.radians.toFloat(), + startAngle = startAngle, + arcLength = arcLength, ) ) diff --git a/maps-kt-core/build.gradle.kts b/maps-kt-core/build.gradle.kts index 888c522..933d68e 100644 --- a/maps-kt-core/build.gradle.kts +++ b/maps-kt-core/build.gradle.kts @@ -3,7 +3,7 @@ plugins { `maven-publish` } -val kmathVersion: String by rootProject.extra("0.3.1-dev-10") +val kmathVersion: String by rootProject.extra kscience{ useSerialization() diff --git a/maps-kt-features/build.gradle.kts b/maps-kt-features/build.gradle.kts index f5887cd..c0e7700 100644 --- a/maps-kt-features/build.gradle.kts +++ b/maps-kt-features/build.gradle.kts @@ -4,10 +4,13 @@ plugins { `maven-publish` } +val kmathVersion: String by rootProject.extra + kotlin { sourceSets { commonMain { dependencies { + api("space.kscience:kmath-trajectory:$kmathVersion") api(compose.foundation) } } 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 673d20d..2746609 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 @@ -16,6 +16,7 @@ import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import center.sciprog.attributes.Attributes import center.sciprog.attributes.NameAttribute +import space.kscience.kmath.geometry.Angle public typealias FloatRange = ClosedFloatingPointRange @@ -185,7 +186,7 @@ public data class LineFeature( override fun getBoundingBox(zoom: Float): Rectangle = space.Rectangle(a, b) - private val clickRadius get() = attributes[ClickRadius] ?: 20f + private val clickRadius get() = attributes[ClickRadius] ?: 10f override fun contains(viewPoint: ViewPoint): Boolean = with(space) { viewPoint.focus in getBoundingBox(viewPoint.zoom) && viewPoint.focus.distanceToLine( @@ -206,8 +207,8 @@ public data class LineFeature( public data class ArcFeature( override val space: CoordinateSpace, public val oval: Rectangle, - public val startAngle: Float, - public val arcLength: Float, + public val startAngle: Angle, + public val arcLength: Angle, override val attributes: Attributes = Attributes.EMPTY, ) : DraggableFeature { override fun getBoundingBox(zoom: Float): Rectangle = oval 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 e09f1f3..b7f07bc 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 @@ -12,6 +12,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import center.sciprog.attributes.* +import space.kscience.kmath.geometry.Angle //@JvmInline //public value class FeatureId>(public val id: String) @@ -188,8 +189,8 @@ public fun FeatureGroup.line( public fun FeatureGroup.arc( oval: Rectangle, - startAngle: Float, - arcLength: Float, + startAngle: Angle, + arcLength: Angle, id: String? = null, ): FeatureRef> = feature( id, diff --git a/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/compositeFeatures.kt b/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/compositeFeatures.kt index 7a30a74..8177114 100644 --- a/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/compositeFeatures.kt +++ b/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/compositeFeatures.kt @@ -11,17 +11,18 @@ public fun FeatureGroup.draggableLine( var lineId: FeatureRef>? = null fun drawLine(): FeatureRef> { - //save attributes before update - val attributes: Attributes? = lineId?.attributes - val currentId = line( - aId.resolve().center, - bId.resolve().center, - lineId?.id ?: id + val currentId = feature( + lineId?.id ?: id, + LineFeature( + space, + aId.resolve().center, + bId.resolve().center, + Attributes { + ZAttribute(-10f) + lineId?.attributes?.let { from(it) } + } + ) ) - currentId.modifyAttributes { - ZAttribute(-10f) - if (attributes != null) from(attributes) - } lineId = currentId return currentId } diff --git a/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/mapFeatureAttributes.kt b/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/mapFeatureAttributes.kt index 0284b67..e57d86f 100644 --- a/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/mapFeatureAttributes.kt +++ b/maps-kt-features/src/commonMain/kotlin/center/sciprog/maps/features/mapFeatureAttributes.kt @@ -57,8 +57,6 @@ public fun , V> FeatureRef.modifyAttribute(key: A * Add drag to this feature * * @param constraint optional drag constraint - * - * TODO use context receiver for that */ @Suppress("UNCHECKED_CAST") public fun > FeatureRef.draggable( diff --git a/maps-kt-features/src/jvmMain/kotlin/center/sciprog/maps/compose/mapControls.kt b/maps-kt-features/src/jvmMain/kotlin/center/sciprog/maps/compose/mapControls.kt index 41718c4..bc7bf76 100644 --- a/maps-kt-features/src/jvmMain/kotlin/center/sciprog/maps/compose/mapControls.kt +++ b/maps-kt-features/src/jvmMain/kotlin/center/sciprog/maps/compose/mapControls.kt @@ -1,6 +1,5 @@ package center.sciprog.maps.compose -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.gestures.drag import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -17,7 +16,6 @@ import kotlin.math.min * Create a modifier for Map/Scheme canvas controls on desktop * @param features a collection of features to be rendered in descending [ZAttribute] order */ -@OptIn(ExperimentalFoundationApi::class) public fun Modifier.mapControls( state: CoordinateViewScope, features: FeatureGroup, @@ -55,7 +53,7 @@ public fun Modifier.mapControls( point ) features.forEachWithAttributeUntil(ClickListenerAttribute) { _, feature, listeners -> - if (point in feature as DomainFeature) { + if (point in (feature as DomainFeature)) { listeners.forEach { it.handle(event, point) } false } else { 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 c75e2db..ea6d2c6 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 @@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.toArgb import center.sciprog.attributes.plus import org.jetbrains.skia.Font import org.jetbrains.skia.Paint +import space.kscience.kmath.geometry.degrees import kotlin.math.PI @@ -56,8 +57,8 @@ public fun DrawScope.drawFeature( drawArc( color = color, - startAngle = feature.startAngle / PI.toFloat() * 180f, - sweepAngle = feature.arcLength / PI.toFloat() * 180f, + startAngle = (feature.startAngle.degrees / PI * 180).toFloat(), + sweepAngle = (feature.arcLength.degrees / PI * 180).toFloat(), useCenter = false, topLeft = dpRect.topLeft, size = size, 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 2f0af99..cbb547d 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 @@ -9,6 +9,7 @@ import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import center.sciprog.attributes.Attributes import center.sciprog.maps.features.* +import space.kscience.kmath.geometry.Angle internal fun Pair.toCoordinates(): XY = XY(first.toFloat(), second.toFloat()) @@ -56,8 +57,8 @@ fun FeatureGroup.line( public fun FeatureGroup.arc( center: Pair, radius: Float, - startAngle: Float, - arcLength: Float, + startAngle: Angle, + arcLength: Angle, id: String? = null, ): FeatureRef> = arc( oval = XYCoordinateSpace.Rectangle(center.toCoordinates(), radius, radius), 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 54d041f..d1b9119 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 @@ -12,8 +12,8 @@ import center.sciprog.maps.features.* import center.sciprog.maps.scheme.* import org.jfree.svg.SVGGraphics2D import org.jfree.svg.SVGUtils +import space.kscience.kmath.geometry.degrees import java.awt.Font.PLAIN -import kotlin.math.PI import kotlin.math.abs @@ -23,7 +23,7 @@ class FeatureStateSnapshot( ) @Composable -fun FeatureGroup.snapshot(): FeatureStateSnapshot = FeatureStateSnapshot( +fun FeatureGroup.snapshot(): FeatureStateSnapshot = FeatureStateSnapshot( featureMap, features.filterIsInstance>().associateWith { it.getPainter() } ) @@ -44,7 +44,7 @@ fun FeatureStateSnapshot.generateSvg( fun SvgDrawScope.drawFeature(scale: Float, feature: Feature) { val color = feature.color ?: Color.Red - val alpha = feature.attributes[AlphaAttribute]?:1f + val alpha = feature.attributes[AlphaAttribute] ?: 1f when (feature) { is ScalableImageFeature -> { @@ -66,10 +66,16 @@ fun FeatureStateSnapshot.generateSvg( is CircleFeature -> drawCircle( color, feature.size.toPx(), - center = feature.center.toOffset() + center = feature.center.toOffset(), + alpha = alpha ) - is LineFeature -> drawLine(color, feature.a.toOffset(), feature.b.toOffset()) + is LineFeature -> drawLine( + color, + feature.a.toOffset(), + feature.b.toOffset(), + alpha = alpha + ) is ArcFeature -> { val topLeft = feature.oval.leftTop.toOffset() @@ -79,12 +85,13 @@ fun FeatureStateSnapshot.generateSvg( drawArc( color = color, - startAngle = (feature.startAngle * 180 / PI).toFloat(), - sweepAngle = (feature.arcLength * 180 / PI).toFloat(), + startAngle = feature.startAngle.degrees.toFloat(), + sweepAngle = feature.arcLength.degrees.toFloat(), useCenter = false, topLeft = topLeft, size = size, - style = Stroke() + style = Stroke(), + alpha = alpha ) } @@ -95,12 +102,12 @@ fun FeatureStateSnapshot.generateSvg( val imageSize = feature.size.toSize() translate(offset.x - imageSize.width / 2, offset.y - imageSize.height / 2) { with(painterCache[feature]!!) { - draw(imageSize) + draw(imageSize, alpha = alpha) } } } - is TextFeature -> drawIntoCanvas { canvas -> + is TextFeature -> drawIntoCanvas { _ -> val offset = feature.position.toOffset() drawText( feature.text,