Add bulk set for features

This commit is contained in:
Alexander Nozik 2024-07-08 09:13:42 +03:00
parent 29074a9624
commit 3a4c9133c6
3 changed files with 33 additions and 18 deletions

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 = key(features) { val painterCache = features.values
features.values
.filterIsInstance<PainterFeature<T>>() .filterIsInstance<PainterFeature<T>>()
.associateWith { it.getPainter() } .associateWith { it.getPainter() }
}
Canvas(modifier) { Canvas(modifier) {
if (state.canvasSize != size.toDpSize()) { if (state.canvasSize != size.toDpSize()) {
@ -107,7 +111,8 @@ public fun <T : Any> FeatureCanvas(
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 -> {