Immutable attributes
This commit is contained in:
parent
7643968d39
commit
26c3e589da
@ -109,6 +109,15 @@ fun App() {
|
|||||||
text(position = it, it.toShortString(), id = "text", color = Color.Blue)
|
text(position = it, it.toShortString(), id = "text", color = Color.Blue)
|
||||||
}
|
}
|
||||||
}.launchIn(scope)
|
}.launchIn(scope)
|
||||||
|
|
||||||
|
features.forEach { (id, feature) ->
|
||||||
|
if (feature is PolygonFeature) {
|
||||||
|
(id as FeatureId<PolygonFeature<Gmc>>).onHover {
|
||||||
|
println("Hover on $id")
|
||||||
|
points(feature.points, color = Color.Blue, id = "selected", attributes = Attributes(ZAttribute, 10f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import center.sciprog.maps.coordinates.*
|
|||||||
import center.sciprog.maps.features.CoordinateSpace
|
import center.sciprog.maps.features.CoordinateSpace
|
||||||
import center.sciprog.maps.features.Rectangle
|
import center.sciprog.maps.features.Rectangle
|
||||||
import center.sciprog.maps.features.ViewPoint
|
import center.sciprog.maps.features.ViewPoint
|
||||||
|
import kotlin.math.abs
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
@ -82,6 +83,13 @@ public object WebMercatorSpace : CoordinateSpace<Gmc> {
|
|||||||
(mercatorA.y - mercatorB.y).dp * tileScale
|
(mercatorA.y - mercatorB.y).dp * tileScale
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun Gmc.isInsidePolygon(points: List<Gmc>): Boolean = points.zipWithNext().count { (left, right) ->
|
||||||
|
val dist = right.latitude - left.latitude
|
||||||
|
val intersection = left.latitude * abs((right.longitude - longitude) / dist) +
|
||||||
|
right.latitude * abs((longitude - left.longitude) / dist)
|
||||||
|
longitude in left.longitude..right.longitude && intersection >= latitude
|
||||||
|
} % 2 == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,56 +18,56 @@ public typealias MapFeature = Feature<Gmc>
|
|||||||
|
|
||||||
public fun FeatureBuilder<Gmc>.circle(
|
public fun FeatureBuilder<Gmc>.circle(
|
||||||
centerCoordinates: Pair<Number, Number>,
|
centerCoordinates: Pair<Number, Number>,
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
size: Dp = 5.dp,
|
size: Dp = 5.dp,
|
||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<CircleFeature<Gmc>> = feature(
|
): FeatureId<CircleFeature<Gmc>> = feature(
|
||||||
id, CircleFeature(coordinateSpace, coordinatesOf(centerCoordinates), zoomRange, size, color)
|
id, CircleFeature(space, coordinatesOf(centerCoordinates), zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun FeatureBuilder<Gmc>.rectangle(
|
public fun FeatureBuilder<Gmc>.rectangle(
|
||||||
centerCoordinates: Pair<Number, Number>,
|
centerCoordinates: Pair<Number, Number>,
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
size: DpSize = DpSize(5.dp, 5.dp),
|
size: DpSize = DpSize(5.dp, 5.dp),
|
||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<RectangleFeature<Gmc>> = feature(
|
): FeatureId<RectangleFeature<Gmc>> = feature(
|
||||||
id, RectangleFeature(coordinateSpace, coordinatesOf(centerCoordinates), zoomRange, size, color)
|
id, RectangleFeature(space, coordinatesOf(centerCoordinates), zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
public fun FeatureBuilder<Gmc>.draw(
|
public fun FeatureBuilder<Gmc>.draw(
|
||||||
position: Pair<Number, Number>,
|
position: Pair<Number, Number>,
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
draw: DrawScope.() -> Unit,
|
draw: DrawScope.() -> Unit,
|
||||||
): FeatureId<DrawFeature<Gmc>> = feature(
|
): FeatureId<DrawFeature<Gmc>> = feature(
|
||||||
id,
|
id,
|
||||||
DrawFeature(coordinateSpace, coordinatesOf(position), zoomRange, drawFeature = draw)
|
DrawFeature(space, coordinatesOf(position), zoomRange, drawFeature = draw)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
public fun FeatureBuilder<Gmc>.line(
|
public fun FeatureBuilder<Gmc>.line(
|
||||||
curve: GmcCurve,
|
curve: GmcCurve,
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<LineFeature<Gmc>> = feature(
|
): FeatureId<LineFeature<Gmc>> = feature(
|
||||||
id,
|
id,
|
||||||
LineFeature(coordinateSpace, curve.forward.coordinates, curve.backward.coordinates, zoomRange, color)
|
LineFeature(space, curve.forward.coordinates, curve.backward.coordinates, zoomRange, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
public fun FeatureBuilder<Gmc>.line(
|
public fun FeatureBuilder<Gmc>.line(
|
||||||
aCoordinates: Pair<Double, Double>,
|
aCoordinates: Pair<Double, Double>,
|
||||||
bCoordinates: Pair<Double, Double>,
|
bCoordinates: Pair<Double, Double>,
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<LineFeature<Gmc>> = feature(
|
): FeatureId<LineFeature<Gmc>> = feature(
|
||||||
id,
|
id,
|
||||||
LineFeature(coordinateSpace, coordinatesOf(aCoordinates), coordinatesOf(bCoordinates), zoomRange, color)
|
LineFeature(space, coordinatesOf(aCoordinates), coordinatesOf(bCoordinates), zoomRange, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -76,14 +76,14 @@ public fun FeatureBuilder<Gmc>.arc(
|
|||||||
radius: Distance,
|
radius: Distance,
|
||||||
startAngle: Angle,
|
startAngle: Angle,
|
||||||
arcLength: Angle,
|
arcLength: Angle,
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<ArcFeature<Gmc>> = feature(
|
): FeatureId<ArcFeature<Gmc>> = feature(
|
||||||
id,
|
id,
|
||||||
ArcFeature(
|
ArcFeature(
|
||||||
coordinateSpace,
|
space,
|
||||||
oval = coordinateSpace.Rectangle(coordinatesOf(center), radius, radius),
|
oval = space.Rectangle(coordinatesOf(center), radius, radius),
|
||||||
startAngle = startAngle.radians.toFloat(),
|
startAngle = startAngle.radians.toFloat(),
|
||||||
arcLength = arcLength.radians.toFloat(),
|
arcLength = arcLength.radians.toFloat(),
|
||||||
zoomRange = zoomRange,
|
zoomRange = zoomRange,
|
||||||
@ -93,24 +93,24 @@ public fun FeatureBuilder<Gmc>.arc(
|
|||||||
|
|
||||||
public fun FeatureBuilder<Gmc>.points(
|
public fun FeatureBuilder<Gmc>.points(
|
||||||
points: List<Pair<Double, Double>>,
|
points: List<Pair<Double, Double>>,
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
stroke: Float = 2f,
|
stroke: Float = 2f,
|
||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
pointMode: PointMode = PointMode.Points,
|
pointMode: PointMode = PointMode.Points,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<PointsFeature<Gmc>> =
|
): FeatureId<PointsFeature<Gmc>> =
|
||||||
feature(id, PointsFeature(coordinateSpace, points.map(::coordinatesOf), zoomRange, stroke, color, pointMode))
|
feature(id, PointsFeature(space, points.map(::coordinatesOf), zoomRange, stroke, color, pointMode))
|
||||||
|
|
||||||
public fun FeatureBuilder<Gmc>.image(
|
public fun FeatureBuilder<Gmc>.image(
|
||||||
position: Pair<Double, Double>,
|
position: Pair<Double, Double>,
|
||||||
image: ImageVector,
|
image: ImageVector,
|
||||||
size: DpSize = DpSize(20.dp, 20.dp),
|
size: DpSize = DpSize(20.dp, 20.dp),
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<VectorImageFeature<Gmc>> = feature(
|
): FeatureId<VectorImageFeature<Gmc>> = feature(
|
||||||
id,
|
id,
|
||||||
VectorImageFeature(
|
VectorImageFeature(
|
||||||
coordinateSpace,
|
space,
|
||||||
coordinatesOf(position),
|
coordinatesOf(position),
|
||||||
size,
|
size,
|
||||||
image,
|
image,
|
||||||
@ -121,11 +121,11 @@ public fun FeatureBuilder<Gmc>.image(
|
|||||||
public fun FeatureBuilder<Gmc>.text(
|
public fun FeatureBuilder<Gmc>.text(
|
||||||
position: Pair<Double, Double>,
|
position: Pair<Double, Double>,
|
||||||
text: String,
|
text: String,
|
||||||
zoomRange: DoubleRange = defaultZoomRange,
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
font: FeatureFont.() -> Unit = { size = 16f },
|
font: FeatureFont.() -> Unit = { size = 16f },
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<TextFeature<Gmc>> = feature(
|
): FeatureId<TextFeature<Gmc>> = feature(
|
||||||
id,
|
id,
|
||||||
TextFeature(coordinateSpace, coordinatesOf(position), text, zoomRange, color, fontConfig = font)
|
TextFeature(space, coordinatesOf(position), text, zoomRange, color, fontConfig = font)
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package center.sciprog.maps.features
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
public interface Attribute<T>
|
||||||
|
|
||||||
|
public object ZAttribute : Attribute<Float>
|
||||||
|
|
||||||
|
public object DraggableAttribute : Attribute<DragHandle<Any>>
|
||||||
|
|
||||||
|
public object DragListenerAttribute : Attribute<Set<DragListener<Any>>>
|
||||||
|
|
||||||
|
public object ClickListenerAttribute : Attribute<Set<MouseListener<Any>>>
|
||||||
|
|
||||||
|
public object HoverListenerAttribute : Attribute<Set<MouseListener<Any>>>
|
||||||
|
|
||||||
|
public object VisibleAttribute : Attribute<Boolean>
|
||||||
|
|
||||||
|
public object ColorAttribute : Attribute<Color>
|
||||||
|
|
||||||
|
public object AlphaAttribute : Attribute<Float>
|
@ -1,55 +0,0 @@
|
|||||||
package center.sciprog.maps.features
|
|
||||||
|
|
||||||
import androidx.compose.runtime.mutableStateMapOf
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
|
|
||||||
public object ZAttribute : Feature.Attribute<Float>
|
|
||||||
|
|
||||||
public object DraggableAttribute : Feature.Attribute<DragHandle<Any>>
|
|
||||||
|
|
||||||
public object DragListenerAttribute : Feature.Attribute<Set<DragListener<Any>>>
|
|
||||||
|
|
||||||
public object ClickableListenerAttribute : Feature.Attribute<Set<ClickListener<Any>>>
|
|
||||||
|
|
||||||
public object VisibleAttribute : Feature.Attribute<Boolean>
|
|
||||||
|
|
||||||
public object ColorAttribute : Feature.Attribute<Color>
|
|
||||||
|
|
||||||
public class AttributeMap {
|
|
||||||
public val map: MutableMap<Feature.Attribute<*>, Any> = mutableStateMapOf()
|
|
||||||
|
|
||||||
public operator fun <T, A : Feature.Attribute<T>> set(
|
|
||||||
attribute: A,
|
|
||||||
attrValue: T?,
|
|
||||||
) {
|
|
||||||
if (attrValue == null) {
|
|
||||||
map.remove(attribute)
|
|
||||||
} else {
|
|
||||||
map[attribute] = attrValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
public operator fun <T> get(attribute: Feature.Attribute<T>): T? = map[attribute] as? T
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other == null || this::class != other::class) return false
|
|
||||||
|
|
||||||
other as AttributeMap
|
|
||||||
|
|
||||||
if (map != other.map) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = map.hashCode()
|
|
||||||
|
|
||||||
override fun toString(): String = "AttributeMap(value=${map.entries})"
|
|
||||||
}
|
|
||||||
|
|
||||||
public var Feature<*>.z: Float
|
|
||||||
get() = attributes[ZAttribute] ?: 0f
|
|
||||||
set(value) {
|
|
||||||
attributes[ZAttribute] = value
|
|
||||||
}
|
|
@ -0,0 +1,43 @@
|
|||||||
|
package center.sciprog.maps.features
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
@JvmInline
|
||||||
|
public value class Attributes internal constructor(internal val map: Map<Attribute<*>, Any>) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public operator fun <T> get(attribute: Attribute<T>): T? = map[attribute] as? T
|
||||||
|
|
||||||
|
public fun <T> Attribute<T>.invoke(value: T?): Attributes = withAttribute(this, value)
|
||||||
|
|
||||||
|
override fun toString(): String = "AttributeMap(value=${map.entries})"
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val EMPTY: Attributes = Attributes(emptyMap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T, A : Attribute<T>> Attributes.withAttribute(
|
||||||
|
attribute: A,
|
||||||
|
attrValue: T?,
|
||||||
|
): Attributes = Attributes(
|
||||||
|
if (attrValue == null) {
|
||||||
|
map - attribute
|
||||||
|
} else {
|
||||||
|
map + (attribute to attrValue)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
public fun <T : Any, A : Attribute<T>> Attributes(
|
||||||
|
attribute: A,
|
||||||
|
attrValue: T,
|
||||||
|
): Attributes = Attributes(mapOf(attribute to attrValue))
|
||||||
|
|
||||||
|
public operator fun Attributes.plus(other: Attributes): Attributes = Attributes(map + other.map)
|
||||||
|
|
||||||
|
public val Feature<*>.z: Float
|
||||||
|
get() = attributes[ZAttribute] ?: 0f
|
||||||
|
// set(value) {
|
||||||
|
// attributes[ZAttribute] = value
|
||||||
|
// }
|
@ -76,6 +76,8 @@ public interface CoordinateSpace<T : Any> {
|
|||||||
|
|
||||||
return distanceVale.dp
|
return distanceVale.dp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun T.isInsidePolygon(points: List<T>): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T : Any> CoordinateSpace<T>.Rectangle(viewPoint: ViewPoint<T>, size: DpSize): Rectangle<T> =
|
public fun <T : Any> CoordinateSpace<T>.Rectangle(viewPoint: ViewPoint<T>, size: DpSize): Rectangle<T> =
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package center.sciprog.maps.features
|
package center.sciprog.maps.features
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.ui.geometry.Rect
|
import androidx.compose.ui.geometry.Rect
|
||||||
import androidx.compose.ui.graphics.*
|
import androidx.compose.ui.graphics.*
|
||||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
@ -14,19 +15,19 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
public typealias DoubleRange = FloatRange
|
|
||||||
public typealias FloatRange = ClosedFloatingPointRange<Float>
|
public typealias FloatRange = ClosedFloatingPointRange<Float>
|
||||||
|
|
||||||
public interface Feature<T : Any> {
|
public interface Feature<T : Any> {
|
||||||
public interface Attribute<T>
|
|
||||||
|
|
||||||
public val space: CoordinateSpace<T>
|
public val space: CoordinateSpace<T>
|
||||||
|
|
||||||
public val zoomRange: FloatRange
|
public val zoomRange: FloatRange
|
||||||
|
|
||||||
public val attributes: AttributeMap
|
public val attributes: Attributes
|
||||||
|
|
||||||
public fun getBoundingBox(zoom: Float): Rectangle<T>?
|
public fun getBoundingBox(zoom: Float): Rectangle<T>?
|
||||||
|
|
||||||
|
public fun withAttributes(modify: Attributes.() -> Attributes): Feature<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface PainterFeature<T : Any> : Feature<T> {
|
public interface PainterFeature<T : Any> : Feature<T> {
|
||||||
@ -34,13 +35,13 @@ public interface PainterFeature<T : Any> : Feature<T> {
|
|||||||
public fun getPainter(): Painter
|
public fun getPainter(): Painter
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ClickableFeature<T : Any> : Feature<T> {
|
public interface DomainFeature<T : Any> : Feature<T> {
|
||||||
public operator fun contains(viewPoint: ViewPoint<T>): Boolean = getBoundingBox(viewPoint.zoom)?.let {
|
public operator fun contains(viewPoint: ViewPoint<T>): Boolean = getBoundingBox(viewPoint.zoom)?.let {
|
||||||
viewPoint.focus in it
|
viewPoint.focus in it
|
||||||
} ?: false
|
} ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface DraggableFeature<T : Any> : ClickableFeature<T> {
|
public interface DraggableFeature<T : Any> : DomainFeature<T> {
|
||||||
public fun withCoordinates(newCoordinates: T): Feature<T>
|
public fun withCoordinates(newCoordinates: T): Feature<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,24 +59,24 @@ public fun <T : Any> Iterable<Feature<T>>.computeBoundingBox(
|
|||||||
mapNotNull { it.getBoundingBox(zoom) }.wrapRectangles()
|
mapNotNull { it.getBoundingBox(zoom) }.wrapRectangles()
|
||||||
}
|
}
|
||||||
|
|
||||||
//public fun Pair<Number, Number>.toCoordinates(): GeodeticMapCoordinates =
|
|
||||||
// GeodeticMapCoordinates.ofDegrees(first.toDouble(), second.toDouble())
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A feature that decides what to show depending on the zoom value (it could change size of shape)
|
* A feature that decides what to show depending on the zoom value (it could change size of shape)
|
||||||
*/
|
*/
|
||||||
public class FeatureSelector<T : Any>(
|
@Stable
|
||||||
|
public data class FeatureSelector<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
public val selector: (zoom: Float) -> Feature<T>,
|
public val selector: (zoom: Float) -> Feature<T>,
|
||||||
) : Feature<T> {
|
) : Feature<T> {
|
||||||
|
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T>? = selector(zoom).getBoundingBox(zoom)
|
override fun getBoundingBox(zoom: Float): Rectangle<T>? = selector(zoom).getBoundingBox(zoom)
|
||||||
|
|
||||||
|
override fun withAttributes(modify: Attributes.() -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PathFeature<T : Any>(
|
@Stable
|
||||||
|
public data class PathFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
public val rectangle: Rectangle<T>,
|
public val rectangle: Rectangle<T>,
|
||||||
public val path: Path,
|
public val path: Path,
|
||||||
@ -83,7 +84,7 @@ public class PathFeature<T : Any>(
|
|||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
public val style: DrawStyle = Fill,
|
public val style: DrawStyle = Fill,
|
||||||
public val targetRect: Rect = path.getBounds(),
|
public val targetRect: Rect = path.getBounds(),
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : DraggableFeature<T> {
|
) : DraggableFeature<T> {
|
||||||
override fun withCoordinates(newCoordinates: T): Feature<T> = with(space) {
|
override fun withCoordinates(newCoordinates: T): Feature<T> = with(space) {
|
||||||
PathFeature(
|
PathFeature(
|
||||||
@ -98,126 +99,172 @@ public class PathFeature<T : Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> = rectangle
|
override fun getBoundingBox(zoom: Float): Rectangle<T> = rectangle
|
||||||
|
override fun withAttributes(modify: Attributes.() -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PointsFeature<T : Any>(
|
@Stable
|
||||||
|
public data class PointsFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
public val points: List<T>,
|
public val points: List<T>,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
public val stroke: Float = 2f,
|
public val stroke: Float = 2f,
|
||||||
public val color: Color = Color.Red,
|
public val color: Color = Color.Red,
|
||||||
public val pointMode: PointMode = PointMode.Points,
|
public val pointMode: PointMode = PointMode.Points,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : Feature<T> {
|
) : Feature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T>? = with(space) {
|
|
||||||
points.wrapPoints()
|
private val boundingBox by lazy {
|
||||||
|
with(space) { points.wrapPoints() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getBoundingBox(zoom: Float): Rectangle<T>? = boundingBox
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
public data class PolygonFeature<T : Any>(
|
||||||
|
override val space: CoordinateSpace<T>,
|
||||||
|
public val points: List<T>,
|
||||||
|
override val zoomRange: FloatRange,
|
||||||
|
public val color: Color = Color.Red,
|
||||||
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
|
) : DomainFeature<T> {
|
||||||
|
|
||||||
|
private val boundingBox: Rectangle<T>? by lazy {
|
||||||
|
with(space) { points.wrapPoints() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBoundingBox(zoom: Float): Rectangle<T>? = boundingBox
|
||||||
|
|
||||||
|
override fun contains(viewPoint: ViewPoint<T>): Boolean = viewPoint.focus in boundingBox!!//with(space) { viewPoint.focus.isInsidePolygon(points) }
|
||||||
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
public data class CircleFeature<T : Any>(
|
public data class CircleFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
override val center: T,
|
override val center: T,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
public val size: Dp = 5.dp,
|
public val size: Dp = 5.dp,
|
||||||
public val color: Color = Color.Red,
|
public val color: Color = Color.Red,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : MarkerFeature<T> {
|
) : MarkerFeature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> =
|
override fun getBoundingBox(zoom: Float): Rectangle<T> =
|
||||||
space.Rectangle(center, zoom, DpSize(size, size))
|
space.Rectangle(center, zoom, DpSize(size, size))
|
||||||
|
|
||||||
override fun withCoordinates(newCoordinates: T): Feature<T> =
|
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(center = newCoordinates)
|
||||||
CircleFeature(space, newCoordinates, zoomRange, size, color, attributes)
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RectangleFeature<T : Any>(
|
@Stable
|
||||||
|
public data class RectangleFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
override val center: T,
|
override val center: T,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
public val size: DpSize = DpSize(5.dp, 5.dp),
|
public val size: DpSize = DpSize(5.dp, 5.dp),
|
||||||
public val color: Color = Color.Red,
|
public val color: Color = Color.Red,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : MarkerFeature<T> {
|
) : MarkerFeature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> =
|
override fun getBoundingBox(zoom: Float): Rectangle<T> =
|
||||||
space.Rectangle(center, zoom, size)
|
space.Rectangle(center, zoom, size)
|
||||||
|
|
||||||
override fun withCoordinates(newCoordinates: T): Feature<T> =
|
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(center = newCoordinates)
|
||||||
RectangleFeature(space, newCoordinates, zoomRange, size, color, attributes)
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LineFeature<T : Any>(
|
@Stable
|
||||||
|
public data class LineFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
public val a: T,
|
public val a: T,
|
||||||
public val b: T,
|
public val b: T,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
public val color: Color = Color.Red,
|
public val color: Color = Color.Red,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : ClickableFeature<T> {
|
) : DomainFeature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> =
|
override fun getBoundingBox(zoom: Float): Rectangle<T> =
|
||||||
space.Rectangle(a, b)
|
space.Rectangle(a, b)
|
||||||
|
|
||||||
override fun contains(viewPoint: ViewPoint<T>): Boolean = with(space) {
|
override fun contains(viewPoint: ViewPoint<T>): Boolean = with(space) {
|
||||||
viewPoint.focus in space.Rectangle(a, b) && viewPoint.focus.distanceToLine(a, b, viewPoint.zoom).value < 5f
|
viewPoint.focus in getBoundingBox(viewPoint.zoom) && viewPoint.focus.distanceToLine(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
viewPoint.zoom
|
||||||
|
).value < 5f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param startAngle the angle from 3 o'clock downwards for the start of the arc in radians
|
* @param startAngle the angle from 3 o'clock downwards for the start of the arc in radians
|
||||||
* @param arcLength arc length in radians
|
* @param arcLength arc length in radians
|
||||||
*/
|
*/
|
||||||
public class ArcFeature<T : Any>(
|
@Stable
|
||||||
|
public data class ArcFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
public val oval: Rectangle<T>,
|
public val oval: Rectangle<T>,
|
||||||
public val startAngle: Float,
|
public val startAngle: Float,
|
||||||
public val arcLength: Float,
|
public val arcLength: Float,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
public val color: Color = Color.Red,
|
public val color: Color = Color.Red,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : DraggableFeature<T> {
|
) : DraggableFeature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> = oval
|
override fun getBoundingBox(zoom: Float): Rectangle<T> = oval
|
||||||
|
|
||||||
override fun withCoordinates(newCoordinates: T): Feature<T> = with(space) {
|
override fun withCoordinates(newCoordinates: T): Feature<T> =
|
||||||
ArcFeature(space, oval.withCenter(newCoordinates), startAngle, arcLength, zoomRange, color, attributes)
|
copy(oval = with(space) { oval.withCenter(newCoordinates) })
|
||||||
}
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
public data class DrawFeature<T : Any>(
|
public data class DrawFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
public val position: T,
|
public val position: T,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
public val drawFeature: DrawScope.() -> Unit,
|
public val drawFeature: DrawScope.() -> Unit,
|
||||||
) : DraggableFeature<T> {
|
) : DraggableFeature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> = space.Rectangle(position, position)
|
override fun getBoundingBox(zoom: Float): Rectangle<T> = space.Rectangle(position, position)
|
||||||
|
|
||||||
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(position = newCoordinates)
|
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(position = newCoordinates)
|
||||||
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
public data class BitmapImageFeature<T : Any>(
|
public data class BitmapImageFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
override val center: T,
|
override val center: T,
|
||||||
public val size: DpSize,
|
public val size: DpSize,
|
||||||
public val image: ImageBitmap,
|
public val image: ImageBitmap,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : MarkerFeature<T> {
|
) : MarkerFeature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> = space.Rectangle(center, zoom, size)
|
override fun getBoundingBox(zoom: Float): Rectangle<T> = space.Rectangle(center, zoom, size)
|
||||||
|
|
||||||
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(center = newCoordinates)
|
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(center = newCoordinates)
|
||||||
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
public data class VectorImageFeature<T : Any>(
|
public data class VectorImageFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
override val center: T,
|
override val center: T,
|
||||||
public val size: DpSize,
|
public val size: DpSize,
|
||||||
public val image: ImageVector,
|
public val image: ImageVector,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : MarkerFeature<T>, PainterFeature<T> {
|
) : MarkerFeature<T>, PainterFeature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> = space.Rectangle(center, zoom, size)
|
override fun getBoundingBox(zoom: Float): Rectangle<T> = space.Rectangle(center, zoom, size)
|
||||||
|
|
||||||
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(center = newCoordinates)
|
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(center = newCoordinates)
|
||||||
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun getPainter(): VectorPainter = rememberVectorPainter(image)
|
override fun getPainter(): VectorPainter = rememberVectorPainter(image)
|
||||||
}
|
}
|
||||||
@ -227,45 +274,50 @@ public data class VectorImageFeature<T : Any>(
|
|||||||
*
|
*
|
||||||
* @param rectangle the size of background in scheme size units. The screen units to scheme units ratio equals scale.
|
* @param rectangle the size of background in scheme size units. The screen units to scheme units ratio equals scale.
|
||||||
*/
|
*/
|
||||||
public class ScalableImageFeature<T : Any>(
|
public data class ScalableImageFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
public val rectangle: Rectangle<T>,
|
public val rectangle: Rectangle<T>,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
public val painter: @Composable () -> Painter,
|
public val painter: @Composable () -> Painter,
|
||||||
) : Feature<T>, PainterFeature<T> {
|
) : Feature<T>, PainterFeature<T> {
|
||||||
@Composable
|
@Composable
|
||||||
override fun getPainter(): Painter = painter.invoke()
|
override fun getPainter(): Painter = painter.invoke()
|
||||||
|
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> = rectangle
|
override fun getBoundingBox(zoom: Float): Rectangle<T> = rectangle
|
||||||
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A group of other features
|
* A group of other features
|
||||||
*/
|
*/
|
||||||
public class FeatureGroup<T : Any>(
|
public data class FeatureGroup<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
public val children: Map<FeatureId<*>, Feature<T>>,
|
public val children: Map<FeatureId<*>, Feature<T>>,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
) : Feature<T> {
|
) : Feature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T>? = with(space) {
|
override fun getBoundingBox(zoom: Float): Rectangle<T>? = with(space) {
|
||||||
children.values.mapNotNull { it.getBoundingBox(zoom) }.wrapRectangles()
|
children.values.mapNotNull { it.getBoundingBox(zoom) }.wrapRectangles()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun withAttributes(modify: Attributes.() -> Attributes): Feature<T> = copy(attributes = attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TextFeature<T : Any>(
|
public data class TextFeature<T : Any>(
|
||||||
override val space: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
public val position: T,
|
public val position: T,
|
||||||
public val text: String,
|
public val text: String,
|
||||||
override val zoomRange: FloatRange,
|
override val zoomRange: FloatRange,
|
||||||
public val color: Color = Color.Black,
|
public val color: Color = Color.Black,
|
||||||
override val attributes: AttributeMap = AttributeMap(),
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
public val fontConfig: FeatureFont.() -> Unit,
|
public val fontConfig: FeatureFont.() -> Unit,
|
||||||
) : DraggableFeature<T> {
|
) : DraggableFeature<T> {
|
||||||
override fun getBoundingBox(zoom: Float): Rectangle<T> = space.Rectangle(position, position)
|
override fun getBoundingBox(zoom: Float): Rectangle<T> = space.Rectangle(position, position)
|
||||||
|
|
||||||
override fun withCoordinates(newCoordinates: T): Feature<T> =
|
override fun withCoordinates(newCoordinates: T): Feature<T> = copy(position = newCoordinates)
|
||||||
TextFeature(space, newCoordinates, text, zoomRange, color, attributes, fontConfig)
|
|
||||||
|
override fun withAttributes(modify: (Attributes) -> Attributes): Feature<T> = copy(attributes = modify(attributes))
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package center.sciprog.maps.features
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateMapOf
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.PointMode
|
import androidx.compose.ui.graphics.PointMode
|
||||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
@ -23,11 +24,13 @@ public value class FeatureId<out F : Feature<*>>(public val id: String)
|
|||||||
|
|
||||||
public interface FeatureBuilder<T : Any> {
|
public interface FeatureBuilder<T : Any> {
|
||||||
|
|
||||||
public val coordinateSpace: CoordinateSpace<T>
|
public val space: CoordinateSpace<T>
|
||||||
|
|
||||||
|
public fun generateUID(feature: Feature<T>?): String
|
||||||
|
|
||||||
public fun <F : Feature<T>> feature(id: String?, feature: F): FeatureId<F>
|
public fun <F : Feature<T>> feature(id: String?, feature: F): FeatureId<F>
|
||||||
|
|
||||||
public fun <F : Feature<T>, V> setAttribute(id: FeatureId<F>, key: Feature.Attribute<V>, value: V?)
|
public fun <F : Feature<T>, V> FeatureId<F>.withAttribute(key: Attribute<V>, value: V?): FeatureId<F>
|
||||||
|
|
||||||
public val defaultColor: Color get() = Color.Red
|
public val defaultColor: Color get() = Color.Red
|
||||||
|
|
||||||
@ -38,11 +41,11 @@ public fun <T : Any, F : Feature<T>> FeatureBuilder<T>.feature(id: FeatureId<F>,
|
|||||||
feature(id.id, feature)
|
feature(id.id, feature)
|
||||||
|
|
||||||
public class FeatureCollection<T : Any>(
|
public class FeatureCollection<T : Any>(
|
||||||
override val coordinateSpace: CoordinateSpace<T>,
|
override val space: CoordinateSpace<T>,
|
||||||
) : CoordinateSpace<T> by coordinateSpace, FeatureBuilder<T> {
|
) : CoordinateSpace<T> by space, FeatureBuilder<T> {
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal val featureMap: MutableMap<String, Feature<T>> = mutableStateMapOf()
|
internal val featureMap: SnapshotStateMap<String, Feature<T>> = mutableStateMapOf()
|
||||||
|
|
||||||
public val features: Map<FeatureId<*>, Feature<T>>
|
public val features: Map<FeatureId<*>, Feature<T>>
|
||||||
get() = featureMap.mapKeys { FeatureId<Feature<T>>(it.key) }
|
get() = featureMap.mapKeys { FeatureId<Feature<T>>(it.key) }
|
||||||
@ -51,31 +54,47 @@ public class FeatureCollection<T : Any>(
|
|||||||
public operator fun <F : Feature<T>> get(id: FeatureId<F>): F =
|
public operator fun <F : Feature<T>> get(id: FeatureId<F>): F =
|
||||||
featureMap[id.id]?.let { it as F } ?: error("Feature with id=$id not found")
|
featureMap[id.id]?.let { it as F } ?: error("Feature with id=$id not found")
|
||||||
|
|
||||||
private fun generateID(feature: Feature<T>): String = "@feature[${feature.hashCode().toUInt()}]"
|
private var uidCounter = 0
|
||||||
|
|
||||||
|
override fun generateUID(feature: Feature<T>?): String = if(feature == null){
|
||||||
|
"@group[${uidCounter++}]"
|
||||||
|
} else {
|
||||||
|
"@${feature::class.simpleName}[${uidCounter++}]"
|
||||||
|
}
|
||||||
|
|
||||||
override fun <F : Feature<T>> feature(id: String?, feature: F): FeatureId<F> {
|
override fun <F : Feature<T>> feature(id: String?, feature: F): FeatureId<F> {
|
||||||
val safeId = id ?: generateID(feature)
|
val safeId = id ?: generateUID(feature)
|
||||||
featureMap[safeId] = feature
|
featureMap[safeId] = feature
|
||||||
return FeatureId(safeId)
|
return FeatureId(safeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <F : Feature<T>> feature(id: FeatureId<F>, feature: F): FeatureId<F> = feature(id.id, feature)
|
public fun <F : Feature<T>> feature(id: FeatureId<F>, feature: F): FeatureId<F> = feature(id.id, feature)
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun <A> getAttribute(id: FeatureId<Feature<T>>, key: Attribute<A>): A? =
|
||||||
|
get(id).attributes[key]
|
||||||
|
|
||||||
override fun <F : Feature<T>, V> setAttribute(id: FeatureId<F>, key: Feature.Attribute<V>, value: V?) {
|
/**
|
||||||
get(id).attributes[key] = value
|
* Process all features with a given attribute from the one with highest [z] to lowest
|
||||||
|
*/
|
||||||
|
public inline fun <A> forEachWithAttribute(
|
||||||
|
key: Attribute<A>,
|
||||||
|
block: (id: FeatureId<*>, feature: Feature<T>, attributeValue: A) -> Unit,
|
||||||
|
) {
|
||||||
|
featureMap.entries.sortedByDescending { it.value.z }.forEach { (id, feature) ->
|
||||||
|
feature.attributes[key]?.let {
|
||||||
|
block(FeatureId<Feature<T>>(id), feature, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
public fun <F : Feature<T>, V> FeatureId<F>.modifyAttributes(modify: Attributes.() -> Attributes) {
|
||||||
public fun FeatureId<DraggableFeature<T>>.onDrag(
|
feature(this, get(this).withAttributes(modify))
|
||||||
listener: PointerEvent.(from: ViewPoint<T>, to: ViewPoint<T>) -> Unit,
|
}
|
||||||
) {
|
|
||||||
with(get(this)) {
|
override fun <F : Feature<T>, V> FeatureId<F>.withAttribute(key: Attribute<V>, value: V?): FeatureId<F> {
|
||||||
attributes[DragListenerAttribute] =
|
feature(this, get(this).withAttributes { withAttribute(key, value) })
|
||||||
(attributes[DragListenerAttribute] ?: emptySet()) + DragListener { event, from, to ->
|
return this
|
||||||
event.listener(from as ViewPoint<T>, to as ViewPoint<T>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -89,7 +108,7 @@ public class FeatureCollection<T : Any>(
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
public fun FeatureId<DraggableFeature<T>>.draggable(
|
public fun FeatureId<DraggableFeature<T>>.draggable(
|
||||||
constraint: ((T) -> T)? = null,
|
constraint: ((T) -> T)? = null,
|
||||||
listener: (PointerEvent.(from: ViewPoint<T>, to: ViewPoint<T>) -> Unit)? = null
|
listener: (PointerEvent.(from: ViewPoint<T>, to: ViewPoint<T>) -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
if (getAttribute(this, DraggableAttribute) == null) {
|
if (getAttribute(this, DraggableAttribute) == null) {
|
||||||
val handle = DragHandle.withPrimaryButton<Any> { event, start, end ->
|
val handle = DragHandle.withPrimaryButton<Any> { event, start, end ->
|
||||||
@ -107,7 +126,7 @@ public class FeatureCollection<T : Any>(
|
|||||||
DragResult(end, true)
|
DragResult(end, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setAttribute(this, DraggableAttribute, handle)
|
this.withAttribute(DraggableAttribute, handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Apply callback
|
//Apply callback
|
||||||
@ -116,6 +135,42 @@ public class FeatureCollection<T : Any>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun FeatureId<DraggableFeature<T>>.onDrag(
|
||||||
|
listener: PointerEvent.(from: ViewPoint<T>, to: ViewPoint<T>) -> Unit,
|
||||||
|
) {
|
||||||
|
withAttribute(
|
||||||
|
DragListenerAttribute,
|
||||||
|
(getAttribute(this, DragListenerAttribute) ?: emptySet()) +
|
||||||
|
DragListener { event, from, to ->
|
||||||
|
event.listener(from as ViewPoint<T>, to as ViewPoint<T>)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun <F : DomainFeature<T>> FeatureId<F>.onClick(
|
||||||
|
onClick: PointerEvent.(click: ViewPoint<T>) -> Unit,
|
||||||
|
) {
|
||||||
|
withAttribute(
|
||||||
|
ClickListenerAttribute,
|
||||||
|
(getAttribute(this, ClickListenerAttribute) ?: emptySet()) +
|
||||||
|
MouseListener { event, point -> event.onClick(point as ViewPoint<T>) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun <F : DomainFeature<T>> FeatureId<F>.onHover(
|
||||||
|
onClick: PointerEvent.(move: ViewPoint<T>) -> Unit,
|
||||||
|
) {
|
||||||
|
withAttribute(
|
||||||
|
HoverListenerAttribute,
|
||||||
|
(getAttribute(this, HoverListenerAttribute) ?: emptySet()) +
|
||||||
|
MouseListener { event, point -> event.onClick(point as ViewPoint<T>) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cyclic update of a feature. Called infinitely until canceled.
|
* Cyclic update of a feature. Called infinitely until canceled.
|
||||||
*/
|
*/
|
||||||
@ -128,36 +183,6 @@ public class FeatureCollection<T : Any>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
public fun <F : ClickableFeature<T>> FeatureId<F>.onClick(
|
|
||||||
onClick: PointerEvent.(click: ViewPoint<T>) -> Unit,
|
|
||||||
) {
|
|
||||||
with(get(this)) {
|
|
||||||
attributes[ClickableListenerAttribute] =
|
|
||||||
(attributes[ClickableListenerAttribute] ?: emptySet()) + ClickListener { event, point ->
|
|
||||||
event.onClick(point as ViewPoint<T>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
public fun <A> getAttribute(id: FeatureId<Feature<T>>, key: Feature.Attribute<A>): A? =
|
|
||||||
get(id).attributes[key]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process all features with a given attribute from the one with highest [z] to lowest
|
|
||||||
*/
|
|
||||||
public inline fun <A> forEachWithAttribute(
|
|
||||||
key: Feature.Attribute<A>,
|
|
||||||
block: (id: FeatureId<*>, attributeValue: A) -> Unit,
|
|
||||||
) {
|
|
||||||
featureMap.entries.sortedByDescending { it.value.z }.forEach { (id, feature) ->
|
|
||||||
feature.attributes[key]?.let {
|
|
||||||
block(FeatureId<Feature<T>>(id), it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
|
||||||
@ -190,7 +215,7 @@ public fun <T : Any> FeatureBuilder<T>.circle(
|
|||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<CircleFeature<T>> = feature(
|
): FeatureId<CircleFeature<T>> = feature(
|
||||||
id, CircleFeature(coordinateSpace, center, zoomRange, size, color)
|
id, CircleFeature(space, center, zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun <T : Any> FeatureBuilder<T>.rectangle(
|
public fun <T : Any> FeatureBuilder<T>.rectangle(
|
||||||
@ -200,7 +225,7 @@ public fun <T : Any> FeatureBuilder<T>.rectangle(
|
|||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<RectangleFeature<T>> = feature(
|
): FeatureId<RectangleFeature<T>> = feature(
|
||||||
id, RectangleFeature(coordinateSpace, centerCoordinates, zoomRange, size, color)
|
id, RectangleFeature(space, centerCoordinates, zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun <T : Any> FeatureBuilder<T>.draw(
|
public fun <T : Any> FeatureBuilder<T>.draw(
|
||||||
@ -210,7 +235,7 @@ public fun <T : Any> FeatureBuilder<T>.draw(
|
|||||||
draw: DrawScope.() -> Unit,
|
draw: DrawScope.() -> Unit,
|
||||||
): FeatureId<DrawFeature<T>> = feature(
|
): FeatureId<DrawFeature<T>> = feature(
|
||||||
id,
|
id,
|
||||||
DrawFeature(coordinateSpace, position, zoomRange, drawFeature = draw)
|
DrawFeature(space, position, zoomRange, drawFeature = draw)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun <T : Any> FeatureBuilder<T>.line(
|
public fun <T : Any> FeatureBuilder<T>.line(
|
||||||
@ -221,7 +246,7 @@ public fun <T : Any> FeatureBuilder<T>.line(
|
|||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<LineFeature<T>> = feature(
|
): FeatureId<LineFeature<T>> = feature(
|
||||||
id,
|
id,
|
||||||
LineFeature(coordinateSpace, aCoordinates, bCoordinates, zoomRange, color)
|
LineFeature(space, aCoordinates, bCoordinates, zoomRange, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun <T : Any> FeatureBuilder<T>.arc(
|
public fun <T : Any> FeatureBuilder<T>.arc(
|
||||||
@ -233,7 +258,7 @@ public fun <T : Any> FeatureBuilder<T>.arc(
|
|||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<ArcFeature<T>> = feature(
|
): FeatureId<ArcFeature<T>> = feature(
|
||||||
id,
|
id,
|
||||||
ArcFeature(coordinateSpace, oval, startAngle, arcLength, zoomRange, color)
|
ArcFeature(space, oval, startAngle, arcLength, zoomRange, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun <T : Any> FeatureBuilder<T>.points(
|
public fun <T : Any> FeatureBuilder<T>.points(
|
||||||
@ -242,9 +267,22 @@ public fun <T : Any> FeatureBuilder<T>.points(
|
|||||||
stroke: Float = 2f,
|
stroke: Float = 2f,
|
||||||
color: Color = defaultColor,
|
color: Color = defaultColor,
|
||||||
pointMode: PointMode = PointMode.Points,
|
pointMode: PointMode = PointMode.Points,
|
||||||
|
attributes: Attributes = Attributes.EMPTY,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<PointsFeature<T>> =
|
): FeatureId<PointsFeature<T>> = feature(
|
||||||
feature(id, PointsFeature(coordinateSpace, points, zoomRange, stroke, color, pointMode))
|
id,
|
||||||
|
PointsFeature(space, points, zoomRange, stroke, color, pointMode, attributes)
|
||||||
|
)
|
||||||
|
|
||||||
|
public fun <T : Any> FeatureBuilder<T>.polygon(
|
||||||
|
points: List<T>,
|
||||||
|
zoomRange: FloatRange = defaultZoomRange,
|
||||||
|
color: Color = defaultColor,
|
||||||
|
id: String? = null,
|
||||||
|
): FeatureId<PolygonFeature<T>> = feature(
|
||||||
|
id,
|
||||||
|
PolygonFeature(space, points, zoomRange, color)
|
||||||
|
)
|
||||||
|
|
||||||
public fun <T : Any> FeatureBuilder<T>.image(
|
public fun <T : Any> FeatureBuilder<T>.image(
|
||||||
position: T,
|
position: T,
|
||||||
@ -256,7 +294,7 @@ public fun <T : Any> FeatureBuilder<T>.image(
|
|||||||
feature(
|
feature(
|
||||||
id,
|
id,
|
||||||
VectorImageFeature(
|
VectorImageFeature(
|
||||||
coordinateSpace,
|
space,
|
||||||
position,
|
position,
|
||||||
size,
|
size,
|
||||||
image,
|
image,
|
||||||
@ -269,8 +307,8 @@ public fun <T : Any> FeatureBuilder<T>.group(
|
|||||||
id: String? = null,
|
id: String? = null,
|
||||||
builder: FeatureCollection<T>.() -> Unit,
|
builder: FeatureCollection<T>.() -> Unit,
|
||||||
): FeatureId<FeatureGroup<T>> {
|
): FeatureId<FeatureGroup<T>> {
|
||||||
val map = FeatureCollection(coordinateSpace).apply(builder).features
|
val map = FeatureCollection(space).apply(builder).features
|
||||||
val feature = FeatureGroup(coordinateSpace, map, zoomRange)
|
val feature = FeatureGroup(space, map, zoomRange)
|
||||||
return feature(id, feature)
|
return feature(id, feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +319,7 @@ public fun <T : Any> FeatureBuilder<T>.scalableImage(
|
|||||||
painter: @Composable () -> Painter,
|
painter: @Composable () -> Painter,
|
||||||
): FeatureId<ScalableImageFeature<T>> = feature(
|
): FeatureId<ScalableImageFeature<T>> = feature(
|
||||||
id,
|
id,
|
||||||
ScalableImageFeature<T>(coordinateSpace, box, zoomRange, painter = painter)
|
ScalableImageFeature<T>(space, box, zoomRange, painter = painter)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun <T : Any> FeatureBuilder<T>.text(
|
public fun <T : Any> FeatureBuilder<T>.text(
|
||||||
@ -293,5 +331,5 @@ public fun <T : Any> FeatureBuilder<T>.text(
|
|||||||
id: String? = null,
|
id: String? = null,
|
||||||
): FeatureId<TextFeature<T>> = feature(
|
): FeatureId<TextFeature<T>> = feature(
|
||||||
id,
|
id,
|
||||||
TextFeature(coordinateSpace, position, text, zoomRange, color, fontConfig = font)
|
TextFeature(space, position, text, zoomRange, color, fontConfig = font)
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package center.sciprog.maps.features
|
||||||
|
|
||||||
|
///**
|
||||||
|
// * A group of other features
|
||||||
|
// */
|
||||||
|
//public data class FeatureGroup<T : Any>(
|
||||||
|
// val parentBuilder: FeatureBuilder<T>,
|
||||||
|
// private val groupId: String,
|
||||||
|
// public override val zoomRange: FloatRange,
|
||||||
|
// override val attributes: Attributes = Attributes.EMPTY,
|
||||||
|
//) : FeatureBuilder<T>, Feature<T> {
|
||||||
|
//
|
||||||
|
// override val space: CoordinateSpace<T> get() = parentBuilder.space
|
||||||
|
//
|
||||||
|
// override fun generateUID(feature: Feature<T>?): String = parentBuilder.generateUID(feature)
|
||||||
|
//
|
||||||
|
// override fun <F : Feature<T>> feature(id: String?, feature: F): FeatureId<F> =
|
||||||
|
// parentBuilder.feature("${groupId}.${id ?: parentBuilder.generateUID(feature)}", feature)
|
||||||
|
//
|
||||||
|
// override fun <F : Feature<T>, V> FeatureId<F>.withAttribute(key: Attribute<V>, value: V?): FeatureId<F> =
|
||||||
|
// with(parentBuilder) {
|
||||||
|
// FeatureId<F>("${groupId}.${this@withAttribute.id}").withAttribute(key, value)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun getBoundingBox(zoom: Float): Rectangle<T>? {
|
||||||
|
// TODO("Not yet implemented")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun withAttributes(modify: Attributes.() -> Attributes): Feature<T> =
|
||||||
|
// copy(attributes = attributes.modify())
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//public fun <T : Any> FeatureBuilder<T>.group(
|
||||||
|
// zoomRange: FloatRange = defaultZoomRange,
|
||||||
|
// id: String? = null,
|
||||||
|
// builder: FeatureBuilder<T>.() -> Unit,
|
||||||
|
//): FeatureId<FeatureGroup<T>> = feature(id, FeatureGroup(this, id ?: generateUID(null), zoomRange).apply(builder))
|
@ -4,13 +4,13 @@ import androidx.compose.ui.input.pointer.PointerEvent
|
|||||||
import androidx.compose.ui.input.pointer.isPrimaryPressed
|
import androidx.compose.ui.input.pointer.isPrimaryPressed
|
||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
|
|
||||||
public fun interface ClickListener<in T : Any> {
|
public fun interface MouseListener<in T : Any> {
|
||||||
public fun handle(event: PointerEvent, click: ViewPoint<T>): Unit
|
public fun handle(event: PointerEvent, point: ViewPoint<T>): Unit
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public fun <T : Any> withPrimaryButton(
|
public fun <T : Any> withPrimaryButton(
|
||||||
block: (event: PointerEvent, click: ViewPoint<T>) -> Unit,
|
block: (event: PointerEvent, click: ViewPoint<T>) -> Unit,
|
||||||
): ClickListener<T> = ClickListener { event, click ->
|
): MouseListener<T> = MouseListener { event, click ->
|
||||||
if (event.buttons.isPrimaryPressed) {
|
if (event.buttons.isPrimaryPressed) {
|
||||||
block(event, click)
|
block(event, click)
|
||||||
}
|
}
|
||||||
@ -20,7 +20,7 @@ public fun interface ClickListener<in T : Any> {
|
|||||||
|
|
||||||
public data class ViewConfig<T : Any>(
|
public data class ViewConfig<T : Any>(
|
||||||
val zoomSpeed: Float = 1f / 3f,
|
val zoomSpeed: Float = 1f / 3f,
|
||||||
val onClick: ClickListener<T>? = null,
|
val onClick: MouseListener<T>? = null,
|
||||||
val dragHandle: DragHandle<T>? = null,
|
val dragHandle: DragHandle<T>? = null,
|
||||||
val onViewChange: ViewPoint<T>.() -> Unit = {},
|
val onViewChange: ViewPoint<T>.() -> Unit = {},
|
||||||
val onSelect: (Rectangle<T>) -> Unit = {},
|
val onSelect: (Rectangle<T>) -> Unit = {},
|
||||||
|
@ -24,27 +24,31 @@ public fun <T : Any> Modifier.mapControls(
|
|||||||
awaitPointerEventScope {
|
awaitPointerEventScope {
|
||||||
while (true) {
|
while (true) {
|
||||||
val event = awaitPointerEvent()
|
val event = awaitPointerEvent()
|
||||||
|
val coordinates = event.changes.first().position.toDpOffset().toCoordinates()
|
||||||
|
val point = space.ViewPoint(coordinates, zoom)
|
||||||
|
|
||||||
|
val sortedFeatures =features.values.sortedByDescending { it.z }
|
||||||
|
|
||||||
|
if (event.type == PointerEventType.Move) {
|
||||||
|
for (feature in sortedFeatures) {
|
||||||
|
val listeners = (feature as? DomainFeature)?.attributes?.get(HoverListenerAttribute)
|
||||||
|
if (listeners != null && point in feature) {
|
||||||
|
listeners.forEach { it.handle(event, point) }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (event.type == PointerEventType.Release) {
|
if (event.type == PointerEventType.Release) {
|
||||||
val coordinates = event.changes.first().position.toDpOffset().toCoordinates()
|
|
||||||
val viewPoint = space.ViewPoint(coordinates, zoom)
|
|
||||||
config.onClick?.handle(
|
config.onClick?.handle(
|
||||||
event,
|
event,
|
||||||
viewPoint
|
point
|
||||||
)
|
)
|
||||||
features.values.mapNotNull { feature ->
|
for (feature in sortedFeatures) {
|
||||||
val clickableFeature = feature as? ClickableFeature
|
val listeners = (feature as? DomainFeature)?.attributes?.get(ClickListenerAttribute)
|
||||||
?: return@mapNotNull null
|
if (listeners != null && point in feature) {
|
||||||
val listeners = clickableFeature.attributes[ClickableListenerAttribute]
|
listeners.forEach { it.handle(event, point) }
|
||||||
?: return@mapNotNull null
|
break
|
||||||
if (viewPoint in clickableFeature) {
|
|
||||||
feature to listeners
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}.maxByOrNull {
|
|
||||||
it.first.z
|
|
||||||
}?.second?.forEach {
|
|
||||||
it.handle(event, viewPoint)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package center.sciprog.maps.features
|
|||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.geometry.Size
|
import androidx.compose.ui.geometry.Size
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Path
|
||||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||||
@ -24,7 +25,9 @@ public fun <T : Any> DrawScope.drawFeature(
|
|||||||
state: CoordinateViewScope<T>,
|
state: CoordinateViewScope<T>,
|
||||||
painterCache: Map<PainterFeature<T>, Painter>,
|
painterCache: Map<PainterFeature<T>, Painter>,
|
||||||
feature: Feature<T>,
|
feature: Feature<T>,
|
||||||
|
|
||||||
): Unit = with(state) {
|
): Unit = with(state) {
|
||||||
|
val alpha = feature.attributes[AlphaAttribute]?:1f
|
||||||
fun T.toOffset(): Offset = toOffset(this@drawFeature)
|
fun T.toOffset(): Offset = toOffset(this@drawFeature)
|
||||||
|
|
||||||
when (feature) {
|
when (feature) {
|
||||||
@ -57,7 +60,8 @@ public fun <T : Any> DrawScope.drawFeature(
|
|||||||
useCenter = false,
|
useCenter = false,
|
||||||
topLeft = dpRect.topLeft,
|
topLeft = dpRect.topLeft,
|
||||||
size = size,
|
size = size,
|
||||||
style = Stroke()
|
style = Stroke(),
|
||||||
|
alpha = alpha
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -93,6 +97,7 @@ public fun <T : Any> DrawScope.drawFeature(
|
|||||||
}
|
}
|
||||||
|
|
||||||
is FeatureGroup -> {
|
is FeatureGroup -> {
|
||||||
|
//do nothing
|
||||||
feature.children.values.forEach {
|
feature.children.values.forEach {
|
||||||
drawFeature(state, painterCache, it)
|
drawFeature(state, painterCache, it)
|
||||||
}
|
}
|
||||||
@ -113,7 +118,23 @@ public fun <T : Any> DrawScope.drawFeature(
|
|||||||
points = points,
|
points = points,
|
||||||
color = feature.color,
|
color = feature.color,
|
||||||
strokeWidth = feature.stroke,
|
strokeWidth = feature.stroke,
|
||||||
pointMode = feature.pointMode
|
pointMode = feature.pointMode,
|
||||||
|
alpha = alpha
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is PolygonFeature -> {
|
||||||
|
val points = feature.points.map { it.toOffset() }
|
||||||
|
val last = points.last()
|
||||||
|
val polygonPath = Path()
|
||||||
|
polygonPath.moveTo(last.x, last.y)
|
||||||
|
for ((x,y) in points){
|
||||||
|
polygonPath.lineTo(x,y)
|
||||||
|
}
|
||||||
|
drawPath(
|
||||||
|
path = polygonPath,
|
||||||
|
color = feature.color,
|
||||||
|
alpha = alpha
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,19 +41,17 @@ public fun FeatureBuilder<Gmc>.geoJsonGeometry(
|
|||||||
|
|
||||||
is GeoJsonMultiPolygon -> group(id = id) {
|
is GeoJsonMultiPolygon -> group(id = id) {
|
||||||
geometry.coordinates.forEach {
|
geometry.coordinates.forEach {
|
||||||
points(
|
polygon(
|
||||||
it.first(),
|
it.first(),
|
||||||
color = color,
|
color = color,
|
||||||
pointMode = PointMode.Polygon
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is GeoJsonPoint -> circle(geometry.coordinates, color = color, id = id)
|
is GeoJsonPoint -> circle(geometry.coordinates, color = color, id = id)
|
||||||
is GeoJsonPolygon -> points(
|
is GeoJsonPolygon -> polygon(
|
||||||
geometry.coordinates.first(),
|
geometry.coordinates.first(),
|
||||||
color = color,
|
color = color,
|
||||||
pointMode = PointMode.Polygon
|
|
||||||
)
|
)
|
||||||
|
|
||||||
is GeoJsonGeometryCollection -> group(id = id) {
|
is GeoJsonGeometryCollection -> group(id = id) {
|
||||||
@ -61,6 +59,8 @@ public fun FeatureBuilder<Gmc>.geoJsonGeometry(
|
|||||||
geoJsonGeometry(it)
|
geoJsonGeometry(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}.apply {
|
||||||
|
withAttribute(AlphaAttribute, 0.5f)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun FeatureBuilder<Gmc>.geoJsonFeature(
|
public fun FeatureBuilder<Gmc>.geoJsonFeature(
|
||||||
|
@ -6,6 +6,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import center.sciprog.maps.features.CoordinateSpace
|
import center.sciprog.maps.features.CoordinateSpace
|
||||||
import center.sciprog.maps.features.Rectangle
|
import center.sciprog.maps.features.Rectangle
|
||||||
import center.sciprog.maps.features.ViewPoint
|
import center.sciprog.maps.features.ViewPoint
|
||||||
|
import kotlin.math.abs
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
object XYCoordinateSpace : CoordinateSpace<XY> {
|
object XYCoordinateSpace : CoordinateSpace<XY> {
|
||||||
@ -70,4 +71,11 @@ object XYCoordinateSpace : CoordinateSpace<XY> {
|
|||||||
(b.x - x).dp * zoom,
|
(b.x - x).dp * zoom,
|
||||||
(b.y - y).dp * zoom
|
(b.y - y).dp * zoom
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override fun XY.isInsidePolygon(points: List<XY>): Boolean = points.zipWithNext().count { (left, right) ->
|
||||||
|
val dist = right.y - left.y
|
||||||
|
val intersection = left.y * abs((right.x - x) / dist) +
|
||||||
|
right.y * abs((x - left.x) / dist)
|
||||||
|
x in left.x..right.x && intersection >= y
|
||||||
|
} % 2 == 0
|
||||||
}
|
}
|
@ -25,9 +25,13 @@ fun FeatureBuilder<XY>.background(
|
|||||||
)
|
)
|
||||||
return feature(
|
return feature(
|
||||||
id,
|
id,
|
||||||
ScalableImageFeature(coordinateSpace, box, zoomRange = defaultZoomRange, painter = painter).apply {
|
ScalableImageFeature(
|
||||||
z = -100f
|
space,
|
||||||
}
|
box,
|
||||||
|
zoomRange = defaultZoomRange,
|
||||||
|
painter = painter,
|
||||||
|
attributes = Attributes(ZAttribute, -100f)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +72,8 @@ public fun FeatureBuilder<XY>.arc(
|
|||||||
startAngle = startAngle,
|
startAngle = startAngle,
|
||||||
arcLength = arcLength,
|
arcLength = arcLength,
|
||||||
zoomRange = zoomRange,
|
zoomRange = zoomRange,
|
||||||
color = color
|
color = color,
|
||||||
|
id = id
|
||||||
)
|
)
|
||||||
|
|
||||||
fun FeatureBuilder<XY>.image(
|
fun FeatureBuilder<XY>.image(
|
||||||
|
Loading…
Reference in New Issue
Block a user