Type safe angles

This commit is contained in:
Alexander Nozik 2023-02-06 17:19:51 +03:00
parent a23b9954cd
commit 50ccfeab70
15 changed files with 65 additions and 46 deletions

View File

@ -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"

View File

@ -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(

View File

@ -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")

View File

@ -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<Gmc> {
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<Gmc>.center: GeodeticMapCoordinates

View File

@ -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<Gmc>.coordinatesOf(pair: Pair<Number, Number>) =
@ -77,8 +76,8 @@ public fun FeatureGroup<Gmc>.arc(
ArcFeature(
space,
oval = space.Rectangle(coordinatesOf(center), radius, radius),
startAngle = startAngle.radians.toFloat(),
arcLength = arcLength.radians.toFloat(),
startAngle = startAngle,
arcLength = arcLength,
)
)

View File

@ -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()

View File

@ -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)
}
}

View File

@ -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<Float>
@ -185,7 +186,7 @@ public data class LineFeature<T : Any>(
override fun getBoundingBox(zoom: Float): Rectangle<T> =
space.Rectangle(a, b)
private val clickRadius get() = attributes[ClickRadius] ?: 20f
private val clickRadius get() = attributes[ClickRadius] ?: 10f
override fun contains(viewPoint: ViewPoint<T>): Boolean = with(space) {
viewPoint.focus in getBoundingBox(viewPoint.zoom) && viewPoint.focus.distanceToLine(
@ -206,8 +207,8 @@ public data class LineFeature<T : Any>(
public data class ArcFeature<T : Any>(
override val space: CoordinateSpace<T>,
public val oval: Rectangle<T>,
public val startAngle: Float,
public val arcLength: Float,
public val startAngle: Angle,
public val arcLength: Angle,
override val attributes: Attributes = Attributes.EMPTY,
) : DraggableFeature<T> {
override fun getBoundingBox(zoom: Float): Rectangle<T> = oval

View File

@ -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<out F : Feature<*>>(public val id: String)
@ -188,8 +189,8 @@ public fun <T : Any> FeatureGroup<T>.line(
public fun <T : Any> FeatureGroup<T>.arc(
oval: Rectangle<T>,
startAngle: Float,
arcLength: Float,
startAngle: Angle,
arcLength: Angle,
id: String? = null,
): FeatureRef<T, ArcFeature<T>> = feature(
id,

View File

@ -11,17 +11,18 @@ public fun <T : Any> FeatureGroup<T>.draggableLine(
var lineId: FeatureRef<T, LineFeature<T>>? = null
fun drawLine(): FeatureRef<T, LineFeature<T>> {
//save attributes before update
val attributes: Attributes? = lineId?.attributes
val currentId = line(
val currentId = feature(
lineId?.id ?: id,
LineFeature(
space,
aId.resolve().center,
bId.resolve().center,
lineId?.id ?: id
)
currentId.modifyAttributes {
Attributes {
ZAttribute(-10f)
if (attributes != null) from(attributes)
lineId?.attributes?.let { from(it) }
}
)
)
lineId = currentId
return currentId
}

View File

@ -57,8 +57,6 @@ public fun <T : Any, F : Feature<T>, V> FeatureRef<T, F>.modifyAttribute(key: A
* Add drag to this feature
*
* @param constraint optional drag constraint
*
* TODO use context receiver for that
*/
@Suppress("UNCHECKED_CAST")
public fun <T: Any, F : DraggableFeature<T>> FeatureRef<T, F>.draggable(

View File

@ -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 <T : Any> Modifier.mapControls(
state: CoordinateViewScope<T>,
features: FeatureGroup<T>,
@ -55,7 +53,7 @@ public fun <T : Any> 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 {

View File

@ -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 <T : Any> 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,

View File

@ -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<Number, Number>.toCoordinates(): XY = XY(first.toFloat(), second.toFloat())
@ -56,8 +57,8 @@ fun FeatureGroup<XY>.line(
public fun FeatureGroup<XY>.arc(
center: Pair<Double, Double>,
radius: Float,
startAngle: Float,
arcLength: Float,
startAngle: Angle,
arcLength: Angle,
id: String? = null,
): FeatureRef<XY, ArcFeature<XY>> = arc(
oval = XYCoordinateSpace.Rectangle(center.toCoordinates(), radius, radius),

View File

@ -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
@ -66,10 +66,16 @@ fun FeatureStateSnapshot<XY>.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<XY>.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<XY>.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,