package center.sciprog.maps.features import androidx.compose.runtime.* import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.* /** * A state holder for current canvas size and view point. Allows transformation from coordinates to pixels and back */ public abstract class CanvasState<T: Any>( public val viewConfig: ViewConfig<T> ){ public abstract val space: CoordinateSpace<T> private var canvasSizeState: MutableState<DpSize?> = mutableStateOf(null) private var viewPointState: MutableState<ViewPoint<T>?> = mutableStateOf(null) public var canvasSize: DpSize get() = canvasSizeState.value ?: DpSize(512.dp, 512.dp) set(value) { canvasSizeState.value = value viewConfig.onCanvasSizeChange(value) } public var viewPoint: ViewPoint<T> get() = viewPointState.value ?: space.defaultViewPoint set(value) { viewPointState.value = value viewConfig.onViewChange(viewPoint) } public val zoom: Float get() = viewPoint.zoom // Selection rectangle. If null - no selection public var selectRect: DpRect? by mutableStateOf(null) public abstract fun Rectangle<T>.toDpRect(): DpRect public abstract fun ViewPoint<T>.moveBy(x: Dp, y: Dp): ViewPoint<T> public abstract fun computeViewPoint(rectangle: Rectangle<T>): ViewPoint<T> public abstract fun DpOffset.toCoordinates(): T public abstract fun T.toDpOffset(): DpOffset public fun toCoordinates(offset: Offset, density: Density): T = with(density){ val dpOffset = DpOffset(offset.x.toDp(), offset.y.toDp()) dpOffset.toCoordinates() } public fun toOffset(coordinates: T, density: Density): Offset = with(density){ val dpOffset = coordinates.toDpOffset() return Offset(dpOffset.x.toPx(), dpOffset.y.toPx()) } } public val DpRect.topLeft: DpOffset get() = DpOffset(left, top) public val DpRect.bottomRight: DpOffset get() = DpOffset(right, bottom)