immutable_features #24

Merged
altavir merged 7 commits from immutable_features into dev 2024-07-08 11:56:05 +03:00
3 changed files with 33 additions and 18 deletions
Showing only changes of commit 3a4c9133c6 - Show all commits

View File

@ -4,7 +4,6 @@ import androidx.compose.foundation.Canvas
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -19,9 +18,13 @@ import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.DpRect import androidx.compose.ui.unit.DpRect
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.sample
import space.kscience.attributes.Attributes import space.kscience.attributes.Attributes
import space.kscience.attributes.plus import space.kscience.attributes.plus
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
/** /**
* An extension of [DrawScope] to include map-specific features * An extension of [DrawScope] to include map-specific features
@ -76,22 +79,23 @@ public class ComposeFeatureDrawScope<T : Any>(
/** /**
* Create a canvas with extended functionality (e.g., drawing text) * Create a canvas with extended functionality (e.g., drawing text)
*/ */
@OptIn(FlowPreview::class)
@Composable @Composable
public fun <T : Any> FeatureCanvas( public fun <T : Any> FeatureCanvas(
state: CanvasState<T>, state: CanvasState<T>,
featureFlow: StateFlow<Map<String, Feature<T>>>, featureFlow: StateFlow<Map<String, Feature<T>>>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
sampleDuration: Duration = 20.milliseconds,
draw: FeatureDrawScope<T>.() -> Unit = {}, draw: FeatureDrawScope<T>.() -> Unit = {},
) { ) {
val textMeasurer = rememberTextMeasurer(0) val textMeasurer = rememberTextMeasurer(0)
val features by featureFlow.collectAsState() val features by featureFlow.sample(sampleDuration).collectAsState(featureFlow.value)
val painterCache = features.values
.filterIsInstance<PainterFeature<T>>()
.associateWith { it.getPainter() }
val painterCache = key(features) {
features.values
.filterIsInstance<PainterFeature<T>>()
.associateWith { it.getPainter() }
}
Canvas(modifier) { Canvas(modifier) {
if (state.canvasSize != size.toDpSize()) { if (state.canvasSize != size.toDpSize()) {
@ -102,12 +106,13 @@ public fun <T : Any> FeatureCanvas(
val attributesCache = mutableMapOf<List<String>, Attributes>() val attributesCache = mutableMapOf<List<String>, Attributes>()
fun computeGroupAttributes(path: List<String>): Attributes = attributesCache.getOrPut(path){ fun computeGroupAttributes(path: List<String>): Attributes = attributesCache.getOrPut(path) {
if (path.isEmpty()) return Attributes.EMPTY if (path.isEmpty()) return Attributes.EMPTY
else if (path.size == 1) { else if (path.size == 1) {
features[path.first()]?.attributes ?: Attributes.EMPTY features[path.first()]?.attributes ?: Attributes.EMPTY
} else { } else {
computeGroupAttributes(path.dropLast(1)) + (features[path.first()]?.attributes ?: Attributes.EMPTY) computeGroupAttributes(path.dropLast(1)) + (features[path.first()]?.attributes
?: Attributes.EMPTY)
} }
} }

View File

@ -46,10 +46,12 @@ public interface FeatureBuilder<T : Any> {
public val space: CoordinateSpace<T> public val space: CoordinateSpace<T>
/** /**
* Add or replace feature. If [id] is null, then a unique id is genertated * Add or replace feature. If [id] is null, then a unique id is generated
*/ */
public fun <F : Feature<T>> feature(id: String?, feature: F): FeatureRef<T, F> public fun <F : Feature<T>> feature(id: String?, feature: F): FeatureRef<T, F>
public fun putFeatures(features: Map<String, Feature<T>?>)
/** /**
* Update existing feature if it is present and is of type [F] * Update existing feature if it is present and is of type [F]
*/ */
@ -89,6 +91,18 @@ public class FeatureStore<T : Any>(
return FeatureRef(this, safeId) return FeatureRef(this, safeId)
} }
public override fun putFeatures(features: Map<String, Feature<T>?>) {
_featureFlow.value = _featureFlow.value.toMutableMap().apply {
features.forEach { (key, value) ->
if (value == null) {
remove(key)
} else {
put(key, value)
}
}
}
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <F : Feature<T>> updateFeature(id: String, block: (F?) -> F): FeatureRef<T, F> = override fun <F : Feature<T>> updateFeature(id: String, block: (F?) -> F): FeatureRef<T, F> =
feature(id, block(features[id] as? F)) feature(id, block(features[id] as? F))
@ -159,10 +173,13 @@ public data class FeatureGroup<T : Any> internal constructor(
override fun withAttributes(modify: Attributes.() -> Attributes): FeatureGroup<T> = override fun withAttributes(modify: Attributes.() -> Attributes): FeatureGroup<T> =
FeatureGroup(store, groupId, modify(attributes)) FeatureGroup(store, groupId, modify(attributes))
override fun <F : Feature<T>> feature(id: String?, feature: F): FeatureRef<T, F> = override fun <F : Feature<T>> feature(id: String?, feature: F): FeatureRef<T, F> =
store.feature("$groupId/${id ?: generateFeatureId(feature)}", feature) store.feature("$groupId/${id ?: generateFeatureId(feature)}", feature)
public override fun putFeatures(features: Map<String, Feature<T>?>) {
store.putFeatures(features.mapKeys { "$groupId/${it.key}" })
}
override fun <F : Feature<T>> updateFeature(id: String, block: (F?) -> F): FeatureRef<T, F> = override fun <F : Feature<T>> updateFeature(id: String, block: (F?) -> F): FeatureRef<T, F> =
store.updateFeature("$groupId/$id", block) store.updateFeature("$groupId/$id", block)

View File

@ -98,13 +98,6 @@ public fun <T : Any> FeatureDrawScope<T>.drawFeature(
is FeatureGroup -> { is FeatureGroup -> {
//ignore groups //ignore groups
// feature.features.values.forEach {
// drawFeature(
// it.withAttributes {
// feature.attributes + this
// }
// )
// }
} }
is PathFeature -> { is PathFeature -> {