Improve map-object link handling
authorLanius Trolling <lanius@laniustrolling.dev>
Sat, 27 Jan 2024 15:34:46 +0000 (10:34 -0500)
committerLanius Trolling <lanius@laniustrolling.dev>
Sat, 27 Jan 2024 15:34:46 +0000 (10:34 -0500)
.idea/misc.xml
src/mapMain/kotlin/info/mechyrdia/mapviewer/entryPoint.kt
src/mapMain/kotlin/info/mechyrdia/mapviewer/render.kt
src/mapMain/kotlin/info/mechyrdia/mapviewer/utils.kt

index 1ad7afe2ee65abc9378b34639a0c859d6a1bb5ae..585a745f14f438cc3ad9a65da9c831ca22e24873 100644 (file)
@@ -92,6 +92,7 @@
     </LinkMapSettings>
   </component>
   <component name="PWA">
+    <option name="enabled" value="true" />
     <option name="wasEnabledAtLeastOnce" value="true" />
   </component>
   <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
index a06770c81386fc20bc65c3d59c550aa001f01fa2..45dccd104c8b23d577127669f381b65a32bc7c20 100644 (file)
@@ -1,15 +1,15 @@
 package info.mechyrdia.mapviewer
 
 import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.supervisorScope
 
 lateinit var galaxyMap: GalaxyMap
 
 lateinit var galaxyLore: GalaxyLore
 
 suspend fun main() {
-       supervisorScope {
+       coroutineScope {
                val ptrProvider = initPopHistoryEntryHandler()
                
                val (map, lore) = showLoadingScreen {
index 300bdcd0620521809b2de42dc55a8e3c2089e205..7eff1b56eca45d95516957995a9a6b680753c1e3 100644 (file)
@@ -26,9 +26,24 @@ private fun renderInNewTab(ptr: MapObjectPtr) {
        window.open(ptr.toUrl().href, "_blank")
 }
 
+private var mapPan: MapPan? = null
+
 private var hammerInstance: HammerManager? = null
 
-private fun CoroutineScope.openPtr(target: MapObjectPtr, currentSectorId: String?) {
+private fun SectorMapPan.moveCameraToSelection(sector: SectorMap, selection: SectorMapSelection) {
+       val (newX, newY) = when (selection) {
+               SectorMapSelection.NoSelection -> Vector2(0, 0)
+               is SectorMapSelection.SelectedSystem -> sector.starSystems.getValue(selection.system).location
+               is SectorMapSelection.SelectedBody -> {
+                       val system = sector.starSystems.getValue(selection.system)
+                       Vector2().copy(system.location).add(system.celestialBodies.getValue(selection.body).location)
+               }
+       }
+       
+       moveTo(newX, newY)
+}
+
+private fun CoroutineScope.openPtr(target: MapObjectPtr, currentSectorId: String?, panToSelection: Boolean) {
        if (target.sector == currentSectorId) {
                val selection = when (target) {
                        GalaxyPtr -> null
@@ -41,6 +56,9 @@ private fun CoroutineScope.openPtr(target: MapObjectPtr, currentSectorId: String
                        setSelectedLabel(selection)
                
                setCurrentLocation(target)
+               
+               if (panToSelection)
+                       (mapPan as? SectorMapPan)?.moveCameraToSelection(galaxyMap.sectors.getValue(currentSectorId!!), selection!!)
        } else {
                if (target == GalaxyPtr && currentSectorId != null)
                        renderGalaxy(galaxyMap.sectors.getValue(currentSectorId).location)
@@ -59,6 +77,8 @@ private abstract class MapPan(val scope: CoroutineScope) {
        private var prevMouseX = 0.0
        private var prevMouseY = 0.0
        
+       abstract fun moveTo(newX: Double, newY: Double)
+       
        abstract fun handleClick(currX: Double, currY: Double): MapObjectPtr?
        abstract fun handleDrag(currX: Double, currY: Double, prevX: Double, prevY: Double)
        
@@ -68,7 +88,7 @@ private abstract class MapPan(val scope: CoroutineScope) {
                if (ev.ctrlKey)
                        renderInNewTab(target)
                else
-                       scope.openPtr(target, sectorId)
+                       scope.openPtr(target, sectorId, false)
        }
        
        private fun onAuxClick(ev: MouseEvent) {
@@ -112,7 +132,7 @@ private abstract class MapPan(val scope: CoroutineScope) {
                
                val target = handleClick(ev.center.x.toDouble(), ev.center.y.toDouble()) ?: return
                
-               scope.openPtr(target, sectorId)
+               scope.openPtr(target, sectorId, false)
        }
        
        private fun onHammerPress(ev: HammerInput) {
@@ -186,6 +206,11 @@ private class GalaxyMapPan(scope: CoroutineScope) : MapPan(scope) {
        var x = 0.0
        var y = 0.0
        
+       override fun moveTo(newX: Double, newY: Double) {
+               x = newX
+               y = newY
+       }
+       
        override fun handleClick(currX: Double, currY: Double): MapObjectPtr? {
                val worldX = (currX - x) / GALAXY_MAP_SIZE_FACTOR
                val worldY = (currY - y) / GALAXY_MAP_SIZE_FACTOR
@@ -322,8 +347,8 @@ private fun CoroutineScope.renderGalaxy(panPosition: Vector2? = null) {
        setCurrentLocation(GalaxyPtr)
        
        val pan = GalaxyMapPan()
-       
        preparePan(pan, panPosition)
+       mapPan = pan
        
        val galaxySvg = document.create.ksvg {
                viewBox = "0 0 ${galaxyMap.background.size.x.toDouble()} ${galaxyMap.background.size.y.toDouble()}"
@@ -505,6 +530,11 @@ private fun CoroutineScope.renderGalaxy(panPosition: Vector2? = null) {
 private class SectorMapPan(private val camera: PerspectiveCamera, override val sectorId: String, private val sector: SectorMap, private val interSectorLinks: List<InterSectorLinkDestination>, scope: CoroutineScope) : MapPan(scope) {
        private val rayCaster = Raycaster()
        
+       override fun moveTo(newX: Double, newY: Double) {
+               camera.position.set(newX, 110.851252, newY + 64)
+               camera.updateMatrixWorld(true)
+       }
+       
        override fun handleClick(currX: Double, currY: Double): MapObjectPtr? {
                rayCaster.setFromCamera(configure {
                        x = (currX / window.innerWidth * 2) - 1
@@ -686,21 +716,11 @@ private fun CoroutineScope.renderSector(sectorId: String, initialSelection: Sect
        
        val interSectorLinks = mutableListOf<InterSectorLinkDestination>()
        
-       val cameraPos = when (initialSelection) {
-               SectorMapSelection.NoSelection -> Vector2(0, 0)
-               is SectorMapSelection.SelectedSystem -> sector.starSystems.getValue(initialSelection.system).location
-               is SectorMapSelection.SelectedBody -> {
-                       val system = sector.starSystems.getValue(initialSelection.system)
-                       Vector2().copy(system.location).add(system.celestialBodies.getValue(initialSelection.body).location)
-               }
-       }
-       
        val camera = PerspectiveCamera(69, window.aspectRatio, 1, 10000)
        camera.rotateX(-PI / 3)
-       camera.position.set(cameraPos.x, 110.851252, cameraPos.y.toDouble() + 64)
-       camera.updateMatrixWorld(true)
        
        val pan = SectorMapPan(camera, sectorId, sector, interSectorLinks)
+       pan.moveCameraToSelection(sector, initialSelection)
        
        val glCanvas = document.create.canvas(content = "")
        glCanvas.width = (window.innerWidth * window.devicePixelRatio).roundToInt()
@@ -713,6 +733,8 @@ private fun CoroutineScope.renderSector(sectorId: String, initialSelection: Sect
                powerPreference = "high-performance"
        })
        
+       mapPan = pan
+       
        val cssCanvas = document.create.div {
                style = "pointer-events:none;position:fixed;top:0;left:0;width:100vw;height:100vh"
        }
@@ -911,6 +933,19 @@ private fun CoroutineScope.renderSector(sectorId: String, initialSelection: Sect
        }
 }
 
+context(CoroutineScope)
+@HtmlTagMarker
+private inline fun FlowOrInteractiveOrPhrasingContent.a(ptr: MapObjectPtr, currentSectorId: String?, crossinline block: A.() -> Unit = {}) {
+       a(href = ptr.toUrl().href) {
+               onClickFunction = { ev ->
+                       ev.preventDefault()
+                       openPtr(ptr, currentSectorId, true)
+               }
+               
+               block()
+       }
+}
+
 private fun CoroutineScope.renderLore(ptr: MapObjectPtr) {
        val loreBar = document.getElementById("lore-bar")!!.unsafeCast<HTMLDivElement>()
        
@@ -925,12 +960,7 @@ private fun CoroutineScope.renderLore(ptr: MapObjectPtr) {
                        is SectorPtr -> {
                                val sector = galaxyMap.sectors.getValue(ptr.sector)
                                p {
-                                       a(href = GalaxyPtr.toUrl().href) {
-                                               onClickFunction = { ev ->
-                                                       ev.preventDefault()
-                                                       openPtr(GalaxyPtr, ptr.sector)
-                                               }
-                                               
+                                       a(GalaxyPtr, ptr.sector) {
                                                +galaxyMap.universeTitle
                                        }
                                }
@@ -943,22 +973,12 @@ private fun CoroutineScope.renderLore(ptr: MapObjectPtr) {
                                val sector = galaxyMap.sectors.getValue(ptr.sector)
                                val system = sector.starSystems.getValue(ptr.system)
                                p {
-                                       a(href = GalaxyPtr.toUrl().href) {
-                                               onClickFunction = { ev ->
-                                                       ev.preventDefault()
-                                                       openPtr(GalaxyPtr, ptr.sector)
-                                               }
-                                               
+                                       a(GalaxyPtr, ptr.sector) {
                                                +galaxyMap.universeTitle
                                        }
                                        +" > "
                                        val sectorPtr = SectorPtr(ptr.sector)
-                                       a(href = sectorPtr.toUrl().href) {
-                                               onClickFunction = { ev ->
-                                                       ev.preventDefault()
-                                                       openPtr(sectorPtr, ptr.sector)
-                                               }
-                                               
+                                       a(sectorPtr, ptr.sector) {
                                                +sector.name
                                        }
                                }
@@ -977,32 +997,17 @@ private fun CoroutineScope.renderLore(ptr: MapObjectPtr) {
                                val system = sector.starSystems.getValue(ptr.system)
                                val body = system.celestialBodies.getValue(ptr.body)
                                p {
-                                       a(href = GalaxyPtr.toUrl().href) {
-                                               onClickFunction = { ev ->
-                                                       ev.preventDefault()
-                                                       openPtr(GalaxyPtr, ptr.sector)
-                                               }
-                                               
+                                       a(GalaxyPtr, ptr.sector) {
                                                +galaxyMap.universeTitle
                                        }
                                        +" > "
                                        val sectorPtr = SectorPtr(ptr.sector)
-                                       a(href = sectorPtr.toUrl().href) {
-                                               onClickFunction = { ev ->
-                                                       ev.preventDefault()
-                                                       openPtr(sectorPtr, ptr.sector)
-                                               }
-                                               
+                                       a(sectorPtr, ptr.sector) {
                                                +sector.name
                                        }
                                        +" > "
                                        val starSystemPtr = StarSystemPtr(ptr.sector, ptr.system)
-                                       a(href = starSystemPtr.toUrl().href) {
-                                               onClickFunction = { ev ->
-                                                       ev.preventDefault()
-                                                       openPtr(starSystemPtr, ptr.sector)
-                                               }
-                                               
+                                       a(starSystemPtr, ptr.sector) {
                                                +system.name
                                        }
                                }
@@ -1041,12 +1046,7 @@ private fun CoroutineScope.renderLore(ptr: MapObjectPtr) {
                                                        for ((sectorId, sector) in sectorsList)
                                                                li {
                                                                        val sectorPtr = SectorPtr(sectorId)
-                                                                       a(href = sectorPtr.toUrl().href) {
-                                                                               onClickFunction = { ev ->
-                                                                                       ev.preventDefault()
-                                                                                       renderMap(sectorPtr)
-                                                                               }
-                                                                               
+                                                                       a(sectorPtr, null) {
                                                                                +sector.name
                                                                        }
                                                                }
@@ -1066,12 +1066,7 @@ private fun CoroutineScope.renderLore(ptr: MapObjectPtr) {
                                                        for ((systemId, system) in systemsList)
                                                                li {
                                                                        val systemPtr = StarSystemPtr(ptr.sector, systemId)
-                                                                       a(href = systemPtr.toUrl().href) {
-                                                                               onClickFunction = { ev ->
-                                                                                       ev.preventDefault()
-                                                                                       renderMap(systemPtr)
-                                                                               }
-                                                                               
+                                                                       a(systemPtr, ptr.sector) {
                                                                                +system.name
                                                                        }
                                                                }
@@ -1093,12 +1088,7 @@ private fun CoroutineScope.renderLore(ptr: MapObjectPtr) {
                                                        for ((bodyId, body) in bodiesList)
                                                                li {
                                                                        val bodyPtr = CelestialBodyPtr(ptr.sector, ptr.system, bodyId)
-                                                                       a(href = bodyPtr.toUrl().href) {
-                                                                               onClickFunction = { ev ->
-                                                                                       ev.preventDefault()
-                                                                                       renderMap(CelestialBodyPtr(ptr.sector, ptr.system, bodyId))
-                                                                               }
-                                                                               
+                                                                       a(bodyPtr, ptr.sector) {
                                                                                +body.name
                                                                        }
                                                                }
index 772756d97540d7398d63937b5d1a6b0aba9e629c..d721cf819644d500030a61e10f643bcd6daac021 100644 (file)
@@ -1,6 +1,7 @@
 package info.mechyrdia.mapviewer
 
 import com.github.nwillc.ksvg.RenderMode
+import externals.threejs.Vector2
 import kotlinx.browser.window
 import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.flow.Flow
@@ -27,6 +28,9 @@ suspend fun awaitAnimationFrame(): Double = suspendCancellableCoroutine { contin
        }
 }
 
+operator fun Vector2.component1() = x.toDouble()
+operator fun Vector2.component2() = y.toDouble()
+
 val deltaTimeFlow: Flow<Double>
        get() = flow {
                var prevTime = awaitAnimationFrame()