From b8eae5b514deba5bdaca16a4e2218fd4f27413de Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Fri, 6 Nov 2026 20:49:58 -0800 Subject: [PATCH] Add Polygons(states) to register many polygons in one effect Same shape as Markers(states): diffs the list against the ids this call added last time and publishes the result in one collector update, so several Polygons()/Polygon() can share a map. --- api/android-sdk-compose.api | 1 + .../compose/polygon/PolygonComponent.kt | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/api/android-sdk-compose.api b/api/android-sdk-compose.api index d5e97f2..f7a2295 100644 --- a/api/android-sdk-compose.api +++ b/api/android-sdk-compose.api @@ -79,6 +79,7 @@ public final class com.mapconductor.compose.polygon.LocalPolygonCollectorKt { } public final class com.mapconductor.compose.polygon.PolygonComponentKt { public static final void Polygon(com.mapconductor.compose.MapViewScope, com.mapconductor.core.polygon.PolygonState, androidx.compose.runtime.Composer, int); + public static final void Polygons(com.mapconductor.compose.MapViewScope, java.util.List, androidx.compose.runtime.Composer, int); public static final void Polygon-YoVAMyA(com.mapconductor.compose.MapViewScope, java.util.List, java.util.List>, java.lang.String, long, float, long, boolean, int, java.io.Serializable, kotlin.jvm.functions.Function1, androidx.compose.runtime.Composer, int, int, int); public static final void Polygon-IvR-a-0(com.mapconductor.compose.MapViewScope, com.mapconductor.core.features.GeoRectBounds, java.lang.String, long, float, long, boolean, int, java.io.Serializable, kotlin.jvm.functions.Function1, androidx.compose.runtime.Composer, int, int); } diff --git a/src/main/java/com/mapconductor/compose/polygon/PolygonComponent.kt b/src/main/java/com/mapconductor/compose/polygon/PolygonComponent.kt index 9bc164f..da37eb4 100644 --- a/src/main/java/com/mapconductor/compose/polygon/PolygonComponent.kt +++ b/src/main/java/com/mapconductor/compose/polygon/PolygonComponent.kt @@ -3,6 +3,8 @@ package com.mapconductor.compose.polygon import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -14,6 +16,9 @@ import com.mapconductor.core.polygon.OnPolygonEventHandler import com.mapconductor.core.polygon.PolygonState import com.mapconductor.core.polygon.unionHolesInPlace import java.io.Serializable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.withContext @Composable fun MapViewScope.Polygon(state: PolygonState) { @@ -32,6 +37,57 @@ fun MapViewScope.Polygon(state: PolygonState) { } } +/** + * Efficiently add many polygons without creating one Composable per polygon. + * + * The same shape as [com.mapconductor.compose.marker.Markers]: one effect diffs + * the list against the ids this call added last time and publishes the result + * in a single collector update. + */ +@Composable +fun MapViewScope.Polygons(states: List) { + val collector = LocalPolygonCollector.current + val prevIdsState = remember { mutableStateOf>(emptySet()) } + + LaunchedEffect(states) { + withContext(Dispatchers.Default) { + val prevIds = prevIdsState.value + val nextIds = states.asSequence().map { it.id }.toSet() + val removedIds = prevIds - nextIds + // Union the holes once per newly added state, as Polygon(state) does. + for (state in states) { + if (state.id !in prevIds && state.holes.size > 1) { + state.unionHolesInPlace() + } + } + prevIdsState.value = nextIds + // Merge only the ids this Polygons() owns, so several + // Polygons()/Polygon() can share one map's collector. + collector.flow.update { current -> + val next = current.toMutableMap() + for (id in removedIds) next.remove(id) + for (state in states) next[state.id] = state + next + } + } + } + + DisposableEffect(Unit) { + onDispose { + // Remove only the polygons this Polygons() owns on dispose. + val ownIds = prevIdsState.value + if (ownIds.isNotEmpty()) { + collector.flow.update { current -> + val next = current.toMutableMap() + for (id in ownIds) next.remove(id) + next + } + } + prevIdsState.value = emptySet() + } + } +} + @Composable fun MapViewScope.Polygon( points: List,