Optimize attributes working

This commit is contained in:
Alexander Nozik 2023-01-05 12:15:52 +03:00
parent ea8c5571bb
commit cb160110e7
11 changed files with 107 additions and 42 deletions
demo/maps/src/jvmMain/kotlin
maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose
maps-kt-features/src
commonMain/kotlin
jvmMain/kotlin/center/sciprog/maps
maps-kt-scheme/src/commonMain/kotlin/center/sciprog/maps/scheme

@ -11,6 +11,10 @@ import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application import androidx.compose.ui.window.application
import center.sciprog.attributes.AlphaAttribute
import center.sciprog.attributes.Attributes
import center.sciprog.attributes.ColorAttribute
import center.sciprog.attributes.ZAttribute
import center.sciprog.maps.compose.* import center.sciprog.maps.compose.*
import center.sciprog.maps.coordinates.* import center.sciprog.maps.coordinates.*
import center.sciprog.maps.features.* import center.sciprog.maps.features.*

@ -85,6 +85,7 @@ public object WebMercatorSpace : CoordinateSpace<Gmc> {
} }
override fun Gmc.isInsidePolygon(points: List<Gmc>): Boolean = points.zipWithNext().count { (left, right) -> override fun Gmc.isInsidePolygon(points: List<Gmc>): Boolean = points.zipWithNext().count { (left, right) ->
//using raytracing algorithm with the ray pointing "up"
val longitudeRange = if(right.longitude >= left.longitude) { val longitudeRange = if(right.longitude >= left.longitude) {
left.longitude..right.longitude left.longitude..right.longitude
} else { } else {

@ -0,0 +1,29 @@
package center.sciprog.attributes
import androidx.compose.ui.graphics.Color
import center.sciprog.maps.features.DragHandle
import center.sciprog.maps.features.DragListener
import center.sciprog.maps.features.FloatRange
import center.sciprog.maps.features.MouseListener
public interface Attribute<T>
public object ZAttribute : Attribute<Float>
public object DraggableAttribute : Attribute<DragHandle<Any>>
public interface SetAttribute<V> : Attribute<Set<V>>
public object DragListenerAttribute : SetAttribute<DragListener<Any>>
public object ClickListenerAttribute : SetAttribute<MouseListener<Any>>
public object HoverListenerAttribute : SetAttribute<MouseListener<Any>>
public object VisibleAttribute : Attribute<Boolean>
public object ColorAttribute : Attribute<Color>
public object ZoomRangeAttribute : Attribute<FloatRange>
public object AlphaAttribute : Attribute<Float>

@ -1,6 +1,7 @@
package center.sciprog.maps.features package center.sciprog.attributes
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import center.sciprog.maps.features.Feature
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@Stable @Stable
@ -9,16 +10,16 @@ public value class Attributes internal constructor(internal val map: Map<Attribu
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public operator fun <T> get(attribute: Attribute<T>): T? = map[attribute] as? T public operator fun <T> get(attribute: Attribute<T>): T? = map[attribute] as? T
public operator fun <T> Attribute<T>.invoke(value: T?): Attributes = withAttribute(this, value) // public operator fun <T> Attribute<T>.invoke(value: T?): Attributes = withAttribute(this, value)
override fun toString(): String = "AttributeMap(value=${map.entries})" override fun toString(): String = "Attributes(value=${map.entries})"
public companion object { public companion object {
public val EMPTY: Attributes = Attributes(emptyMap()) public val EMPTY: Attributes = Attributes(emptyMap())
} }
} }
public fun <T, A : Attribute<T>> Attributes.withAttribute( public fun <T, A : Attribute<T>> Attributes.attribute(
attribute: A, attribute: A,
attrValue: T?, attrValue: T?,
): Attributes = Attributes( ): Attributes = Attributes(
@ -29,6 +30,34 @@ public fun <T, A : Attribute<T>> Attributes.withAttribute(
} }
) )
/**
* Add an element to a [SetAttribute]
*/
public fun <T, A : SetAttribute<T>> Attributes.addValue(
attribute: A,
attrValue: T,
): Attributes {
val currentSet: Set<T> = get(attribute) ?: emptySet()
return Attributes(
map + (attribute to (currentSet + attrValue))
)
}
/**
* Remove an element from [SetAttribute]
*/
public fun <T, A : SetAttribute<T>> Attributes.removeValue(
attribute: A,
attrValue: T,
): Attributes {
val currentSet: Set<T> = get(attribute) ?: emptySet()
return Attributes(
map + (attribute to (currentSet - attrValue))
)
}
public fun <T : Any, A : Attribute<T>> Attributes( public fun <T : Any, A : Attribute<T>> Attributes(
attribute: A, attribute: A,
attrValue: T, attrValue: T,

@ -1,23 +0,0 @@
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 ZoomRangeAttribute : Attribute<FloatRange>
public object AlphaAttribute : Attribute<Float>

@ -14,6 +14,9 @@ import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.unit.Dp 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
import center.sciprog.attributes.Attributes
import center.sciprog.attributes.ColorAttribute
import center.sciprog.attributes.ZoomRangeAttribute
public typealias FloatRange = ClosedFloatingPointRange<Float> public typealias FloatRange = ClosedFloatingPointRange<Float>

@ -13,6 +13,7 @@ import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.unit.Dp 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
import center.sciprog.attributes.*
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@JvmInline @JvmInline
@ -64,7 +65,7 @@ public data class FeatureGroup<T : Any>(
if (feature is FeatureGroup<T>) { if (feature is FeatureGroup<T>) {
feature.visitUntil(visitor) feature.visitUntil(visitor)
} else { } else {
if (!visitor(this, key, feature)) return@forEach if (!visitor(this, key, feature)) return@visitUntil
} }
} }
} }
@ -79,7 +80,15 @@ public data class FeatureGroup<T : Any>(
} }
public fun <F : Feature<T>, V> FeatureId<F>.attribute(key: Attribute<V>, value: V?): FeatureId<F> { public fun <F : Feature<T>, V> FeatureId<F>.attribute(key: Attribute<V>, value: V?): FeatureId<F> {
feature(this, get(this).withAttributes { withAttribute(key, value) }) feature(this, get(this).withAttributes { attribute(key, value) })
return this
}
/**
* Add multi-entry [SetAttribute] value
*/
public fun <F : Feature<T>, V> FeatureId<F>.addAttribute(key: SetAttribute<V>, value: V): FeatureId<F> {
feature(this, get(this).withAttributes { addValue(key, value) })
return this return this
} }
@ -126,12 +135,9 @@ public data class FeatureGroup<T : Any>(
public fun FeatureId<DraggableFeature<T>>.onDrag( public fun FeatureId<DraggableFeature<T>>.onDrag(
listener: PointerEvent.(from: ViewPoint<T>, to: ViewPoint<T>) -> Unit, listener: PointerEvent.(from: ViewPoint<T>, to: ViewPoint<T>) -> Unit,
) { ) {
attribute( addAttribute(
DragListenerAttribute, DragListenerAttribute,
(getAttribute(this, DragListenerAttribute) ?: emptySet()) + DragListener { event, from, to -> event.listener(from as ViewPoint<T>, to as ViewPoint<T>) }
DragListener { event, from, to ->
event.listener(from as ViewPoint<T>, to as ViewPoint<T>)
}
) )
} }
@ -139,20 +145,19 @@ public data class FeatureGroup<T : Any>(
public fun <F : DomainFeature<T>> FeatureId<F>.onClick( public fun <F : DomainFeature<T>> FeatureId<F>.onClick(
onClick: PointerEvent.(click: ViewPoint<T>) -> Unit, onClick: PointerEvent.(click: ViewPoint<T>) -> Unit,
) { ) {
attribute( addAttribute(
ClickListenerAttribute, ClickListenerAttribute,
(getAttribute(this, ClickListenerAttribute) ?: emptySet()) +
MouseListener { event, point -> event.onClick(point as ViewPoint<T>) } MouseListener { event, point -> event.onClick(point as ViewPoint<T>) }
) )
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public fun <F : DomainFeature<T>> FeatureId<F>.onHover( public fun <F : DomainFeature<T>> FeatureId<F>.onHover(
onClick: PointerEvent.(move: ViewPoint<T>) -> Unit, onClick: PointerEvent.(move: ViewPoint<T>) -> Unit,
) { ) {
attribute( addAttribute(
HoverListenerAttribute, HoverListenerAttribute,
(getAttribute(this, HoverListenerAttribute) ?: emptySet()) +
MouseListener { event, point -> event.onClick(point as ViewPoint<T>) } MouseListener { event, point -> event.onClick(point as ViewPoint<T>) }
) )
} }

@ -1,5 +1,9 @@
package center.sciprog.maps.features package center.sciprog.maps.features
import center.sciprog.attributes.Attributes
import center.sciprog.attributes.ZAttribute
import center.sciprog.attributes.attribute
public fun <T : Any> FeatureGroup<T>.draggableLine( public fun <T : Any> FeatureGroup<T>.draggableLine(
aId: FeatureId<MarkerFeature<T>>, aId: FeatureId<MarkerFeature<T>>,
@ -16,7 +20,7 @@ public fun <T : Any> FeatureGroup<T>.draggableLine(
get(bId).center, get(bId).center,
lineId?.id ?: id lineId?.id ?: id
) )
if (attributes != null) currentId.modifyAttributes { attributes.withAttribute(ZAttribute, -10f) } if (attributes != null) currentId.modifyAttributes { attributes.attribute(ZAttribute, -10f) }
lineId = currentId lineId = currentId
return currentId return currentId
} }

@ -7,6 +7,9 @@ import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.DpRect import androidx.compose.ui.unit.DpRect
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import center.sciprog.attributes.ClickListenerAttribute
import center.sciprog.attributes.DraggableAttribute
import center.sciprog.attributes.HoverListenerAttribute
import center.sciprog.maps.features.* import center.sciprog.maps.features.*
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@ -94,11 +97,17 @@ public fun <T : Any> Modifier.mapControls(
val dragResult = config.dragHandle?.handle(event, dragStart, dragEnd) val dragResult = config.dragHandle?.handle(event, dragStart, dragEnd)
if (dragResult?.handleNext == false) return@drag if (dragResult?.handleNext == false) return@drag
var continueAfter = true
features.forEachWithAttributeUntil(DraggableAttribute) { _, _, handler -> features.forEachWithAttributeUntil(DraggableAttribute) { _, _, handler ->
handler.handle(event, dragStart, dragEnd).handleNext handler.handle(event, dragStart, dragEnd).handleNext.also {
if (!it) continueAfter = false
} }
} }
if (!continueAfter) return@drag
}
if (event.buttons.isPrimaryPressed) { if (event.buttons.isPrimaryPressed) {
//If selection process is started, modify the frame //If selection process is started, modify the frame
selectionStart?.let { start -> selectionStart?.let { start ->

@ -11,6 +11,8 @@ import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.nativeCanvas import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import center.sciprog.attributes.AlphaAttribute
import center.sciprog.attributes.plus
import org.jetbrains.skia.Font import org.jetbrains.skia.Font
import org.jetbrains.skia.Paint import org.jetbrains.skia.Paint
import kotlin.math.PI import kotlin.math.PI

@ -8,6 +8,8 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp 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
import center.sciprog.attributes.Attributes
import center.sciprog.attributes.ZAttribute
import center.sciprog.maps.features.* import center.sciprog.maps.features.*
internal fun Pair<Number, Number>.toCoordinates(): XY = XY(first.toFloat(), second.toFloat()) internal fun Pair<Number, Number>.toCoordinates(): XY = XY(first.toFloat(), second.toFloat())