Type safe angles
This commit is contained in:
parent
a23b9954cd
commit
50ccfeab70
@ -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"
|
||||
|
@ -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(
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
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
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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<T : Any>(
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun <T: Any> FeatureGroup<T>.snapshot(): FeatureStateSnapshot<T> = FeatureStateSnapshot(
|
||||
fun <T : Any> FeatureGroup<T>.snapshot(): FeatureStateSnapshot<T> = FeatureStateSnapshot(
|
||||
featureMap,
|
||||
features.filterIsInstance<PainterFeature<T>>().associateWith { it.getPainter() }
|
||||
)
|
||||
@ -44,7 +44,7 @@ fun FeatureStateSnapshot<XY>.generateSvg(
|
||||
fun SvgDrawScope.drawFeature(scale: Float, feature: Feature<XY>) {
|
||||
|
||||
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<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,
|
||||
|
Loading…
Reference in New Issue
Block a user