{ it.pos.y }.thenBy { it.pos.z }
@@ -334,6 +339,4 @@ internal class GameplayScreen(private val core: TerraModulus, private val camera
}
internal fun renderGwrGeo(drawable: WorldObjDrawable) = camera.renderGwrGeo(drawable, geoShaders)
-
- override fun exit() {}
}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/GeomComponent.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/GeomComponent.kt
new file mode 100644
index 00000000..865c06a2
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/GeomComponent.kt
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.agim.Component
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.mui.gui.gfx.GuiGeometry
+import net.terramodulus.mui.gui.gfx.RenderSystem
+
+class GeomComponent(val geom: GuiGeometry, asdHandle: AsdHandle) : Component(asdHandle) {
+ override fun render(renderSystem: RenderSystem) {
+ geom.render(renderSystem)
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/GraphicsComponent.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/GraphicsComponent.kt
new file mode 100644
index 00000000..d2d8077f
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/GraphicsComponent.kt
@@ -0,0 +1,28 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.agim.AbstractPane
+import net.terramodulus.mui.gui.agim.Component
+import net.terramodulus.mui.gui.agim.Layout
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.mui.gui.gfx.GuiSprite
+import net.terramodulus.mui.gui.gfx.RenderSystem
+
+sealed interface GraphicsComponent
+
+class SpriteComponent(val sprite: GuiSprite, asdHandle: AsdHandle) : Component(asdHandle), GraphicsComponent {
+ override fun render(renderSystem: RenderSystem) {
+ sprite.render(renderSystem)
+ }
+}
+
+@Suppress("CanSealedSubClassBeObject")
+class CanvasComponent(override val layout: Layout, asdHandle: AsdHandle) : AbstractPane(asdHandle), GraphicsComponent {
+ override fun render(renderSystem: RenderSystem) {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/IntrinsicProperties.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/IntrinsicProperties.kt
new file mode 100644
index 00000000..abd0e86d
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/IntrinsicProperties.kt
@@ -0,0 +1,30 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.agim.AgimoProperty
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.util.gcd
+
+/**
+ * @constructor Simple constructor without any preprocessing
+ */
+data class IntrinsicRatioProperty(val width: UInt, val height: UInt) : AgimoProperty() {
+ companion object {
+ val KEY = AsdHandle.PropertyKey(IntrinsicRatioProperty::class.java)
+ fun compute(width: UInt, height: UInt): IntrinsicRatioProperty {
+ val gcd = gcd(width, height)
+ return IntrinsicRatioProperty(width / gcd, height / gcd)
+ }
+ }
+}
+
+class IntrinsicDimensionsProperty(val width: UInt, val height: UInt) : AgimoProperty() {
+ companion object {
+ val KEY = AsdHandle.PropertyKey(IntrinsicDimensionsProperty::class.java)
+ }
+ fun computeRatio() = IntrinsicRatioProperty.compute(width, height)
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/LaunchingScreen.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/LaunchingScreen.kt
new file mode 100644
index 00000000..dfe83d18
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/LaunchingScreen.kt
@@ -0,0 +1,85 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.gfx.AlphaFilter
+import net.terramodulus.mui.gui.gfx.Dimension2I
+import net.terramodulus.mui.gui.gfx.GuiRect
+import net.terramodulus.mui.gui.gfx.GuiSprite
+import net.terramodulus.mui.gui.gfx.RectangleI
+import net.terramodulus.mui.gui.gfx.RenderSystem
+import net.terramodulus.mui.gui.gfx.SmartScaling
+import net.terramodulus.mui.gui.agim.Screen
+import net.terramodulus.mui.gui.agim.ScreenManager
+import net.terramodulus.mui.gui.agim.event.ScreenEvent
+import net.terramodulus.mui.gui.asd.AsdHandle
+
+private val REF_SIZE = Dimension2I(800, 480)
+
+private val BG_COLOR = floatArrayOf(.145F, .776F, .768F)
+
+private const val ANI_DURATION = .75F // in second
+
+private const val PAUSE_DURATION = 2 // in second
+
+internal class LaunchingScreen(
+ managerHandle: ScreenManager.Handle,
+ asdHandle: AsdHandle.Container,
+ renderSystemHandle: RenderSystem.Handle,
+) : Screen(managerHandle, asdHandle) {
+ private var stage = 0
+ private var last = System.currentTimeMillis() // timestamp in milliseconds
+ private var alphaFilter = AlphaFilter(0F)
+ override val layout = CompositeLayout(this)
+
+ init {
+ layout.update {
+ add(AbsoluteLayout(this@LaunchingScreen, GeomComponent(GuiRect(
+ 0, 0, 1, 1, 37, 198, 196, 255
+ ), ComponentAsdHandleImpl()).apply {
+ geom.add(alphaFilter)
+ }, AbsoluteLayout.Config.Full))
+ }
+ layout.update {
+ add(AbsoluteLayout(this@LaunchingScreen, SpriteComponent(GuiSprite(
+ RectangleI(0, 0, 512, 128),
+ renderSystemHandle.loadTexture("/studio_logo.png"),
+ ), ComponentAsdHandleImpl()).apply {
+ sprite.add(alphaFilter)
+ sprite.add(SmartScaling.both(REF_SIZE.width, REF_SIZE.height, 512, 128))
+ }, AbsoluteLayout.Config.Full))
+ }
+
+ addListener(ScreenEvent.Update::class.java) {
+ val current = System.currentTimeMillis()
+ val elapsed = (current - last) / 1000F // elapsed time for this stage
+ when (stage) {
+ 0 -> if (elapsed >= ANI_DURATION) {
+ stage = 1
+ last = current
+ alphaFilter.alpha = 1F
+ } else {
+ alphaFilter.alpha = elapsed / ANI_DURATION
+ }
+
+ 1 -> if (elapsed >= PAUSE_DURATION) {
+ stage = 2
+ last = current
+ }
+
+ 2 -> if (elapsed >= ANI_DURATION) {
+ stage = 3
+ last = current
+ alphaFilter.alpha = 0F
+ } else {
+ alphaFilter.alpha = 1 - elapsed / ANI_DURATION
+ }
+
+ 3 -> it.muiIoI.screenManager.handle.reset(::ResourceLoadingScreen)
+ }
+ }
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ObjectFitLayout.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ObjectFitLayout.kt
new file mode 100644
index 00000000..ef64f4ba
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ObjectFitLayout.kt
@@ -0,0 +1,104 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import com.cout970.math.vec2.ImmVec2d
+import net.terramodulus.mui.gui.agim.AnchorAlignmentHelper
+import net.terramodulus.mui.gui.agim.Component
+import net.terramodulus.mui.gui.agim.Container
+import net.terramodulus.mui.gui.agim.Layout
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.mui.gui.gfx.Dimension2D
+import net.terramodulus.mui.gui.gfx.RectangleF
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * Only applicable to AGIMO elements with intrinsic ratios.
+ */
+class ObjectFitLayout(
+ container: Container,
+ component: Component,
+ private var mode: ModeConfig,
+ private var alignment: AlignmentConfig,
+) : Layout(container) {
+ override val components = componentsSequence(::component)
+ var component = component
+ private set
+
+ // TODO should Fill be implemented?
+ enum class ModeConfig {
+ Contain {
+ override fun compute(container: RectangleF, component: IntrinsicRatioProperty): Dimension2D {
+ val w = container.width.toDouble() / component.width.toDouble()
+ val h = container.height.toDouble() / component.height.toDouble()
+ val scale = min(w, h)
+ return Dimension2D(
+ component.width.toDouble() * scale,
+ component.height.toDouble() * scale,
+ )
+ }
+ },
+ Cover {
+ override fun compute(container: RectangleF, component: IntrinsicRatioProperty): Dimension2D {
+ val w = container.width.toDouble() / component.width.toDouble()
+ val h = container.height.toDouble() / component.height.toDouble()
+ val scale = max(w, h)
+ return Dimension2D(
+ component.width.toDouble() * scale,
+ component.height.toDouble() * scale,
+ )
+ }
+ },
+ ;
+
+ abstract fun compute(container: RectangleF, component: IntrinsicRatioProperty): Dimension2D
+ }
+
+ /**
+ * Range within `[0,1]`
+ */
+ data class AlignmentConfig(val x: Double, val y: Double) {
+ companion object {
+ val DEFAULT = AlignmentConfig(0.5, 0.5)
+ fun withX(x: Double) = AlignmentConfig(x, 0.5)
+ fun withY(y: Double) = AlignmentConfig(y, 0.5)
+ }
+ }
+
+ fun update(component: Component) {
+ operate {
+ this@ObjectFitLayout.component = component
+ }
+ }
+
+ interface Config {
+ var mode: ModeConfig
+ var alignment: AlignmentConfig
+ }
+
+ fun update(operation: Config.() -> Unit) {
+ operate {
+ operation(object : Config {
+ override var mode: ModeConfig by this@ObjectFitLayout::mode
+ override var alignment: AlignmentConfig by this@ObjectFitLayout::alignment
+ })
+ }
+ }
+
+ private operator fun RectangleF.times(other: AlignmentConfig) = ImmVec2d(width * other.x, height * other.y)
+ private operator fun Dimension2D.times(other: AlignmentConfig) = ImmVec2d(width * other.x, height * other.y)
+
+ override fun layOut(handle: AsdHandle) {
+ val ratio = component.asdHandle.getProperty(IntrinsicRatioProperty.KEY)
+ if (ratio === null)
+ throw IllegalStateException("component has no intrinsic ratio")
+ val target = mode.compute(handle.rect, ratio)
+ component.asdHandle.rect = AnchorAlignmentHelper.Subject(handle.rect.toDouble(), handle.rect * alignment)
+ .alignTarget(AnchorAlignmentHelper.Target(target, target * alignment)).toFloat()
+ component.asdHandle.triggerRectObservers()
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gms/impl/ResourceLoadingScreen.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ResourceLoadingScreen.kt
similarity index 72%
rename from src/kernel/client/kotlin/net/terramodulus/mui/gms/impl/ResourceLoadingScreen.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ResourceLoadingScreen.kt
index e62d091b..9b92ccee 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/gms/impl/ResourceLoadingScreen.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ResourceLoadingScreen.kt
@@ -1,23 +1,23 @@
/*
- * SPDX-FileCopyrightText: 2025 TerraModulus Team and Contributors
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.gms.impl
+package net.terramodulus.mui.gui.agim.impl
-import net.terramodulus.mui.gfx.AlphaFilter
-import net.terramodulus.mui.gfx.Dimension2I
-import net.terramodulus.mui.gfx.FullScaling
-import net.terramodulus.mui.gfx.GuiRect
-import net.terramodulus.mui.gfx.GuiSprite
-import net.terramodulus.mui.gfx.RectangleI
-import net.terramodulus.mui.gfx.RenderSystem
-import net.terramodulus.mui.gfx.SmartScaling
-import net.terramodulus.mui.gfx.Vector3F
-import net.terramodulus.mui.gms.Screen
-import net.terramodulus.mui.gms.ScreenManager
-import net.terramodulus.mui.input.InputSystem
-import kotlin.math.max
+import net.terramodulus.engine.common.ZeroImmVec3f
+import net.terramodulus.mui.gui.agim.Screen
+import net.terramodulus.mui.gui.agim.ScreenManager
+import net.terramodulus.mui.gui.gfx.AlphaFilter
+import net.terramodulus.mui.gui.gfx.Dimension2I
+import net.terramodulus.mui.gui.gfx.FullScaling
+import net.terramodulus.mui.gui.gfx.GuiRect
+import net.terramodulus.mui.gui.gfx.GuiSprite
+import net.terramodulus.mui.gui.gfx.Rectangle
+import net.terramodulus.mui.gui.gfx.RectangleI
+import net.terramodulus.mui.gui.gfx.RenderSystem
+import net.terramodulus.mui.gui.gfx.SmartScaling
+import net.terramodulus.mui.kui.InputSystem
import kotlin.math.min
import kotlin.properties.Delegates
@@ -30,7 +30,11 @@ private val BG_COLOR = floatArrayOf(.145F, .776F, 0.768F)
private const val ANI_DURATION = 1F // in second
private const val PAUSE_DURATION = 2F // in second
-class ResourceLoadingScreen(renderSystemHandle: RenderSystem.Handle) : Screen() {
+class ResourceLoadingScreen(
+ managerHandle: ScreenManager.Handle,
+ rect: ScreenManager.DelegatedRect,
+ renderSystemHandle: RenderSystem.Handle,
+) : Screen(managerHandle, rect) {
private var stage = 0
private var last = System.currentTimeMillis() // timestamp in milliseconds
private var alphaFilter = AlphaFilter(0F)
@@ -66,7 +70,7 @@ class ResourceLoadingScreen(renderSystemHandle: RenderSystem.Handle) : Screen()
}
private class ProgressBar {
- val rectDim = RectangleI.withPoints(7, 7, 393, 33)
+ val rectDim = Rectangle.withPoints(7, 7, 393, 33)
val length = rectDim.width
var progress: Float by Delegates.observable(0f) { _, _, _ ->
rect.setPos(7, 7, rectDim.x + (progress * length).toInt(), 33)
@@ -104,11 +108,7 @@ class ResourceLoadingScreen(renderSystemHandle: RenderSystem.Handle) : Screen()
}
// 3 -> screenManager.handle.openBefore(::TitleScreen, this)
- 3 -> screenManager.handle.reset(renderSystem.newGameplayScreen(Vector3F.ZERO))
+ 3 -> screenManager.handle.reset(renderSystem.newGameplayScreen(ZeroImmVec3f))
}
}
-
- override fun exit() {
-
- }
}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ScaledLayout.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ScaledLayout.kt
new file mode 100644
index 00000000..bf01443e
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ScaledLayout.kt
@@ -0,0 +1,108 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.agim.Component
+import net.terramodulus.mui.gui.agim.Container
+import net.terramodulus.mui.gui.agim.Layout
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.mui.gui.gfx.Dimension2D
+
+/**
+ * Scaling intrinsic dimensions of AGIMO element for intrinsic dimensions for this object.
+ */
+class ScaledLayout(
+ container: Container,
+ component: Component,
+ private var config: Config,
+) : Layout(container) {
+ override val components = componentsSequence(::component)
+ var component = component
+ private set
+
+ sealed class Config {
+ abstract fun compute(dim: IntrinsicDimensionsProperty): Dimension2D
+
+ /**
+ * Scale both dimensions by the same scaling
+ * @param scale `> 0`
+ */
+ data class Scale(val scale: Double) : Config() {
+ override fun compute(dim: IntrinsicDimensionsProperty) =
+ Dimension2D(dim.width.toDouble() * scale, dim.height.toDouble() * scale)
+ }
+
+ class Compute private constructor(private val x: Value, private val y: Value) : Config() {
+ private object MathEnvImpl : MathEnv
+
+ constructor(x: MathEnv.() -> Value, y: MathEnv.() -> Value) : this(x(MathEnvImpl), y(MathEnvImpl))
+
+ sealed interface Value {
+ fun compute(dim: IntrinsicDimensionsProperty): Double
+
+ operator fun plus(that: Value) = Operator.Plus(this, that)
+ operator fun minus(that: Value) = Operator.Minus(this, that)
+ operator fun times(that: Value) = Operator.Times(this, that)
+ operator fun div(that: Value) = Operator.Div(this, that)
+ }
+
+ sealed class Operator private constructor() : Value {
+ data class Plus(val a: Value, val b: Value) : Operator() {
+ override fun compute(dim: IntrinsicDimensionsProperty) = a.compute(dim) + b.compute(dim)
+ }
+ data class Minus(val a: Value, val b: Value) : Operator() {
+ override fun compute(dim: IntrinsicDimensionsProperty) = a.compute(dim) - b.compute(dim)
+ }
+ data class Times(val a: Value, val b: Value) : Operator() {
+ override fun compute(dim: IntrinsicDimensionsProperty) = a.compute(dim) * b.compute(dim)
+ }
+ data class Div(val a: Value, val b: Value) : Operator() {
+ override fun compute(dim: IntrinsicDimensionsProperty) = a.compute(dim) / b.compute(dim)
+ }
+ }
+
+ sealed class Param private constructor() : Value {
+ data class Num(val value: Double) : Param() {
+ override fun compute(dim: IntrinsicDimensionsProperty) = value
+ }
+ data object DimX : Param() {
+ override fun compute(dim: IntrinsicDimensionsProperty) = dim.width.toDouble()
+ }
+ data object DimY : Param() {
+ override fun compute(dim: IntrinsicDimensionsProperty) = dim.height.toDouble()
+ }
+ }
+
+ sealed interface MathEnv {
+ val dimX: Param get() = Param.DimX
+ val dimY: Param get() = Param.DimY
+ fun num(value: Double) = Param.Num(value)
+ }
+
+ override fun compute(dim: IntrinsicDimensionsProperty) = Dimension2D(x.compute(dim), y.compute(dim))
+ }
+ }
+
+ fun update(component: Component) {
+ operate {
+ this@ScaledLayout.component = component
+ }
+ }
+
+ fun update(operation: (Config) -> Config) {
+ operate {
+ config = operation(config)
+ }
+ }
+
+ override fun layOut(handle: AsdHandle) {
+ val dim = component.asdHandle.getProperty(IntrinsicDimensionsProperty.KEY)
+ if (dim === null)
+ throw IllegalStateException("component has no intrinsic dimensions")
+ config.compute(dim)
+ TODO("Then?")
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ScrollPane.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ScrollPane.kt
new file mode 100644
index 00000000..73587106
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/ScrollPane.kt
@@ -0,0 +1,20 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.agim.AbstractPane
+import net.terramodulus.mui.gui.agim.Layout
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.mui.gui.gfx.RenderSystem
+
+class ScrollPane(asdHandle: AsdHandle) : AbstractPane(asdHandle) {
+ override fun render(renderSystem: RenderSystem) {
+ TODO("Not yet implemented")
+ }
+
+ override val layout: Layout
+ get() = TODO("Not yet implemented")
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gms/impl/SequenceLayout.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SequenceLayout.kt
similarity index 86%
rename from src/kernel/client/kotlin/net/terramodulus/mui/gms/impl/SequenceLayout.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SequenceLayout.kt
index 9d80c12c..1c22a726 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/gms/impl/SequenceLayout.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SequenceLayout.kt
@@ -1,14 +1,15 @@
/*
- * SPDX-FileCopyrightText: 2025 TerraModulus Team and Contributors
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.gms.impl
+package net.terramodulus.mui.gui.agim.impl
-import net.terramodulus.mui.gfx.RectangleF
-import net.terramodulus.mui.gms.Component
-import net.terramodulus.mui.gms.Container
-import net.terramodulus.mui.gms.Layout
+import net.terramodulus.mui.gui.agim.Component
+import net.terramodulus.mui.gui.agim.Container
+import net.terramodulus.mui.gui.agim.Layout
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.mui.gui.gfx.RectangleF
/**
* Common implementation that is either [ColumnLayout] or [RowLayout].
@@ -59,7 +60,7 @@ class ColumnLayout private constructor(container: Container, elements: ElementLi
}
}
- override fun layout(rect: RectangleF) {
+ override fun layOut(handle: AsdHandle) {
elements.forEach { TODO() }
}
}
@@ -87,7 +88,7 @@ class RowLayout private constructor(container: Container, elements: ElementList<
}
}
- override fun layout(rect: RectangleF) {
+ override fun layOut(handle: AsdHandle) {
elements.forEach { TODO() }
}
}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SimplePane.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SimplePane.kt
new file mode 100644
index 00000000..4137b804
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SimplePane.kt
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.agim.AbstractPane
+import net.terramodulus.mui.gui.agim.Layout
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.mui.gui.gfx.RenderSystem
+
+class SimplePane(asdHandle: AsdHandle, override var layout: Layout) : AbstractPane(asdHandle) {
+ override fun render(renderSystem: RenderSystem) {
+ layout.render(renderSystem)
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SingletonLayout.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SingletonLayout.kt
new file mode 100644
index 00000000..4b7e5bfd
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SingletonLayout.kt
@@ -0,0 +1,108 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import com.cout970.math.vec2.ImmVec2d
+import net.terramodulus.mui.gui.agim.AnchorAlignmentHelper
+import net.terramodulus.mui.gui.agim.Component
+import net.terramodulus.mui.gui.agim.Container
+import net.terramodulus.mui.gui.agim.Layout
+import net.terramodulus.mui.gui.asd.AsdHandle
+import net.terramodulus.mui.gui.gfx.Dimension2D
+import net.terramodulus.mui.gui.gfx.InsetsF
+import net.terramodulus.mui.gui.gfx.RectangleF
+import kotlin.math.max
+import kotlin.math.min
+
+class SingletonLayout(container: Container, component: Component, private var config: Config) : Layout(container) {
+ override val components = componentsSequence(::component)
+ var component = component
+ private set
+
+ sealed class Config private constructor() {
+ abstract fun layOut(container: RectangleF): RectangleF
+
+ sealed class Absolute private constructor() : Config() {
+ data object Full : Config() {
+ override fun layOut(container: RectangleF) = container
+ }
+
+ data class Insets(var insets: InsetsF) : Config() {
+ override fun layOut(container: RectangleF) = container - insets
+ }
+ }
+
+ enum class ObjectFit {
+ Contain {
+ override fun compute(container: RectangleF, component: IntrinsicRatioProperty): Dimension2D {
+ val w = container.width.toDouble() / component.width.toDouble()
+ val h = container.height.toDouble() / component.height.toDouble()
+ val scale = min(w, h)
+ return Dimension2D(
+ component.width.toDouble() * scale,
+ component.height.toDouble() * scale,
+ )
+ }
+ },
+ Cover {
+ override fun compute(container: RectangleF, component: IntrinsicRatioProperty): Dimension2D {
+ val w = container.width.toDouble() / component.width.toDouble()
+ val h = container.height.toDouble() / component.height.toDouble()
+ val scale = max(w, h)
+ return Dimension2D(
+ component.width.toDouble() * scale,
+ component.height.toDouble() * scale,
+ )
+ }
+ },
+ ;
+
+ abstract fun compute(container: RectangleF, component: IntrinsicRatioProperty): Dimension2D
+
+ private operator fun RectangleF.times(other: ObjectFitLayout.AlignmentConfig) = ImmVec2d(width * other.x, height * other.y)
+ private operator fun Dimension2D.times(other: ObjectFitLayout.AlignmentConfig) = ImmVec2d(width * other.x, height * other.y)
+
+ override fun layOut(handle: AsdHandle) {
+ val ratio = component.asdHandle.getProperty(IntrinsicRatioProperty.KEY)
+ if (ratio === null)
+ throw IllegalStateException("component has no intrinsic ratio")
+ val target = mode.compute(handle.rect, ratio)
+ component.asdHandle.rect = AnchorAlignmentHelper.Subject(handle.rect.toDouble(), handle.rect * alignment)
+ .alignTarget(AnchorAlignmentHelper.Target(target, target * alignment)).toFloat()
+ component.asdHandle.triggerRectObservers()
+ }
+ }
+
+ /**
+ * Range within `[0,1]`
+ */
+ data class AlignmentConfig(val x: Double, val y: Double) {
+ companion object {
+ val DEFAULT = AlignmentConfig(0.5, 0.5)
+ fun withX(x: Double) = AlignmentConfig(x, 0.5)
+ fun withY(y: Double) = AlignmentConfig(y, 0.5)
+ }
+ }
+
+ }
+
+ fun update(component: Component) {
+ operate {
+ this@SingletonLayout.component = component
+ }
+ }
+
+ fun update(operation: (Config) -> Config) {
+ operate {
+ config = operation(config)
+ }
+ }
+
+ override fun layOut(handle: AsdHandle) {
+ component.asdHandle.rect = config.layOut(handle.rect)
+ component.asdHandle.triggerRectObservers()
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SliderComponent.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SliderComponent.kt
new file mode 100644
index 00000000..1e06a869
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/SliderComponent.kt
@@ -0,0 +1,71 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.agim.Component
+import net.terramodulus.mui.gui.gfx.Anchor5
+import net.terramodulus.mui.gui.gfx.Direction4A
+import net.terramodulus.mui.gui.gfx.Direction4AD
+import net.terramodulus.mui.gui.gfx.GuiRect
+import net.terramodulus.mui.gui.gfx.Rectangle
+import net.terramodulus.mui.gui.gfx.RectangleF
+import net.terramodulus.mui.gui.gfx.RenderSystem
+import kotlin.properties.Delegates
+
+class SliderComponent(val dir: Direction4A) : Component() {
+ private var bgInit = false
+ private var fgInit = false
+ private lateinit var background: GuiRect
+ private lateinit var bgComponent: GeomComponent
+ private lateinit var foreground: GuiRect
+ private lateinit var fgComponent: GeomComponent
+ var fraction: Float by Delegates.observable(0F) { _, _, value ->
+ updateForeground(rect.value, value)
+ }
+
+ init {
+ rect.observe {
+ val anchor = it.anchor(Anchor5.TopRight)
+ if (!bgInit) {
+ background = GuiRect(it.x.toInt(), it.y.toInt(), anchor.xi, anchor.yi, 255, 255, 255, 255)
+ bgComponent = GeomComponent(background)
+ bgInit = true
+ } else {
+ background.setPos(it.x.toInt(), it.y.toInt(), anchor.xi, anchor.yi)
+ }
+ updateForeground(it, fraction)
+ }
+ }
+
+ private fun updateForeground(bounds: RectangleF, fraction: Float) {
+ // (bounds.x, bounds.y) is the bottom-left anchor
+ val topRight = bounds.anchor(Anchor5.TopRight)
+ val rect = when (dir) {
+ Direction4A.XPos -> // left to right
+ Rectangle.withDirection(bounds.x, bounds.y, bounds.width * fraction, bounds.height, Direction4AD.QuadOne)
+ Direction4A.XNeg -> // right to left
+ Rectangle.withDirection(topRight.x, bounds.y, bounds.width * fraction, bounds.height, Direction4AD.QuadTwo)
+ Direction4A.YPos -> // bottom to top
+ Rectangle.withDirection(bounds.x, bounds.y, bounds.width, bounds.height * fraction, Direction4AD.QuadOne)
+ Direction4A.YNeg -> // top to bottom
+ Rectangle.withDirection(bounds.x, topRight.y, bounds.width, bounds.height * fraction, Direction4AD.QuadFour)
+ }
+ val anchor = rect.anchor(Anchor5.TopRight)
+ if (!fgInit) {
+ foreground = GuiRect(rect.x.toInt(), rect.y.toInt(), anchor.xi, anchor.yi, 122, 122, 122, 255)
+ fgComponent = GeomComponent(foreground)
+ fgInit = true
+ } else {
+ foreground.setPos(rect.x.toInt(), rect.y.toInt(), anchor.xi, anchor.yi)
+ }
+ }
+
+ override fun render(renderSystem: RenderSystem) {
+ rect.value
+ bgComponent.render(renderSystem)
+ fgComponent.render(renderSystem)
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/TitleScreen.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/TitleScreen.kt
new file mode 100644
index 00000000..ab0881e0
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/impl/TitleScreen.kt
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.agim.impl
+
+import net.terramodulus.mui.gui.gfx.RenderSystem
+import net.terramodulus.mui.gui.agim.Screen
+import net.terramodulus.mui.gui.agim.ScreenManager
+
+class TitleScreen(
+ renderSystemHandle: RenderSystem.Handle,
+ managerHandle: ScreenManager.Handle,
+ rect: ScreenManager.DelegatedRect,
+) : Screen(managerHandle, rect) {
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/package-info.java b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/package-info.java
new file mode 100644
index 00000000..5723fbd2
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/agim/package-info.java
@@ -0,0 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+/**
+ * Abstract Graphical Interface Model (AGIM)
+ *
+ * Defined in EFP 9 (or a dedicated EFP for this chapter).
+ */
+package net.terramodulus.mui.gui.agim;
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdHandle.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdHandle.kt
new file mode 100644
index 00000000..cadc2284
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdHandle.kt
@@ -0,0 +1,58 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.asd
+
+import net.terramodulus.mui.gui.agim.AgimoProperty
+import net.terramodulus.mui.gui.agim.AgimoPropertyMap
+import net.terramodulus.mui.gui.gfx.RectangleF
+import net.terramodulus.util.TypedAnchorMap
+import java.util.function.BiFunction
+
+/**
+ * Since rectangles are modified only during layout processing,
+ * further follow-ups must not be deferred to next tick.
+ */
+abstract class AsdHandle internal constructor() {
+ /**
+ * Caveat: Must only be modified by [Layout][net.terramodulus.mui.gui.agim.Layout].
+ * When modified, [triggerRectObservers] must be invoked.
+ */
+ abstract var rect: RectangleF
+ internal set
+
+ protected val rectObservers = LinkedHashSet<() -> Unit>()
+
+ internal fun observeRect(observer: () -> Unit) {
+ rectObservers.add(observer)
+ }
+
+ internal fun unobserveRect(observer: () -> Unit) {
+ rectObservers.remove(observer)
+ }
+
+ internal fun triggerRectObservers() {
+ rectObservers.forEach { it() }
+ }
+
+ /**
+ * Permanent AGIMO Properties
+ */
+ val properties = AgimoPropertyMap()
+
+ /**
+ * Registers ASD Processors from AGIMOs to [AsdManager].
+ */
+ abstract fun registerAsdProcessor(processor: AsdProcessor<*>)
+
+ // TODO likely those below are useless
+ abstract class Container : AsdHandle() {}
+
+ abstract class Menu : Container() {}
+
+ abstract class Screen : Container() {}
+
+// interface Component : LayoutHandle {} // Do we need this?
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdIr.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdIr.kt
new file mode 100644
index 00000000..355c613c
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdIr.kt
@@ -0,0 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.asd
+
+/**
+ * ASD Intermediate Representation (IR)
+ */
+class AsdIr {
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdManager.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdManager.kt
new file mode 100644
index 00000000..6d6e75af
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdManager.kt
@@ -0,0 +1,41 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.asd
+
+import net.terramodulus.mui.gui.agim.AgimoTreeVisitor
+
+// TODO Should ASD affect choices of Layout?
+// There are two proposals:
+// - Each Container manages a Layout, then the Layout is put into LayoutManager/LayoutViewport
+// - Each Container is totally managed by AsdManager except for Facets, but this rather complicated for ASD
+internal class AsdManager internal constructor() {
+ companion object {
+ // TODO temporary demonstrative testing default
+ internal fun default(): AsdManager = AsdManager()
+ }
+
+ private val processors = HashSet>()
+
+ internal fun registerProcessor(processor: AsdProcessor<*>) {
+ processors.add(processor)
+ }
+
+ internal inner class AgimHandle {
+ internal fun registerProcessor(processor: AsdProcessor<*>) = this@AsdManager.registerProcessor(processor)
+ }
+
+ internal fun process() {
+ fun process(processor: AsdProcessor) = processor.processDefined(processor.produceDefined())
+ processors.forEach { process(it) }
+ }
+
+ // TODO What should be the use of this
+ private inner class TreeVisitor(visitorScreens: ScreenTreeVisitor, visitorMenus: MenuTreeVisitor) : AgimoTreeVisitor() {
+ init {
+ val root = RootNode(visitorScreens.visit(), visitorMenus.visit())
+ }
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdProcessor.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdProcessor.kt
new file mode 100644
index 00000000..4a50dc05
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/AsdProcessor.kt
@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.asd
+
+import net.terramodulus.engine.BaseAsdProcessor
+
+// TODO in reality, only binary data are consumed, but
+// in testing environment, objects are created directly
+// - most likely, each AGIMO and Layout register necessary Processor
+// to consume input data and configure its managed instance of AGIMO/Layout
+abstract class AsdProcessor {
+ companion object {
+ fun get(): BaseAsdProcessor = TODO("This processor's only use is to provide InputStream")
+ }
+
+ // TODO Temporary implementation to configure ASD without binary data but produced configs
+ abstract fun processDefined(definitions: T)
+
+ // TODO Temporary implementation to produce defined configurations for testing and demo
+ abstract fun produceDefined(): T
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/MenuStyles.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/MenuStyles.kt
new file mode 100644
index 00000000..0022ce5d
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/MenuStyles.kt
@@ -0,0 +1,9 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.asd
+
+class MenuStyles {
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/ScreenStyles.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/ScreenStyles.kt
new file mode 100644
index 00000000..76571c6e
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/asd/ScreenStyles.kt
@@ -0,0 +1,9 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.asd
+
+class ScreenStyles {
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/Anchor.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Anchor.kt
similarity index 83%
rename from src/kernel/client/kotlin/net/terramodulus/mui/gfx/Anchor.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Anchor.kt
index b000efe7..b3880d64 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/Anchor.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Anchor.kt
@@ -1,9 +1,9 @@
/*
- * SPDX-FileCopyrightText: 2025 TerraModulus Team and Contributors
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.gfx
+package net.terramodulus.mui.gui.gfx
/*
* Every set of anchor position directions has different meanings in different context,
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ColorFilter.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ColorFilter.kt
new file mode 100644
index 00000000..fa70486f
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ColorFilter.kt
@@ -0,0 +1,13 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.gfx
+
+import net.terramodulus.engine.AlphaFilter
+import net.terramodulus.engine.ColorFilter
+
+typealias ColorFilter = ColorFilter
+
+typealias AlphaFilter = AlphaFilter
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/Dimension.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Dimension.kt
similarity index 65%
rename from src/kernel/client/kotlin/net/terramodulus/mui/gfx/Dimension.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Dimension.kt
index 6825bcb0..ede9a02c 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/Dimension.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Dimension.kt
@@ -1,14 +1,16 @@
/*
- * SPDX-FileCopyrightText: 2025 TerraModulus Team and Contributors
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.gfx
+package net.terramodulus.mui.gui.gfx
data class Dimension2I(val width: Int, val height: Int)
data class Dimension2F(val width: Float, val height: Float)
+data class Dimension2D(val width: Double, val height: Double)
+
data class Dimension3I(val width: Int, val height: Int, val length: Int)
data class Dimension3F(val width: Float, val height: Float, val length: Float)
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/Direction.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Direction.kt
similarity index 83%
rename from src/kernel/client/kotlin/net/terramodulus/mui/gfx/Direction.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Direction.kt
index 621776f8..1e3aed54 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/Direction.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Direction.kt
@@ -1,9 +1,9 @@
/*
- * SPDX-FileCopyrightText: 2025 TerraModulus Team and Contributors
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.gfx
+package net.terramodulus.mui.gui.gfx
/*
* Every set of directions has different meanings in different context,
@@ -39,6 +39,13 @@ enum class Direction4A {
XPos, XNeg, YPos, YNeg;
}
+/**
+ * Set of 4 axial diagonal directions, by Cartesian quadrants.
+ */
+enum class Direction4AD {
+ QuadOne, QuadTwo, QuadThree, QuadFour;
+}
+
/**
* Set of 8 compass directions.
*/
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/GuiGeometry.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/GuiGeometry.kt
similarity index 90%
rename from src/kernel/client/kotlin/net/terramodulus/mui/gfx/GuiGeometry.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/GuiGeometry.kt
index cc6a61b4..950c8236 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/GuiGeometry.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/GuiGeometry.kt
@@ -1,9 +1,9 @@
/*
- * SPDX-FileCopyrightText: 2025 TerraModulus Team and Contributors
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.gfx
+package net.terramodulus.mui.gui.gfx
import net.terramodulus.engine.GeomDrawable
import net.terramodulus.engine.SimpleLineGeom
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/GuiSprite.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/GuiSprite.kt
similarity index 80%
rename from src/kernel/client/kotlin/net/terramodulus/mui/gfx/GuiSprite.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/GuiSprite.kt
index d7d7a944..11ba563d 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/GuiSprite.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/GuiSprite.kt
@@ -1,9 +1,9 @@
/*
- * SPDX-FileCopyrightText: 2025 TerraModulus Team and Contributors
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.gfx
+package net.terramodulus.mui.gui.gfx
import net.terramodulus.engine.SpriteMesh
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Insets.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Insets.kt
new file mode 100644
index 00000000..623d7554
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Insets.kt
@@ -0,0 +1,35 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.gfx
+
+abstract class Insets(open val left: N, open val top: N, open val right: N, open val bottom: N) {
+
+}
+
+data class InsetsI(
+ override val left: Int,
+ override val top: Int,
+ override val right: Int,
+ override val bottom: Int,
+) : Insets(left, top, right, bottom)
+
+data class InsetsF(
+ override val left: Float,
+ override val top: Float,
+ override val right: Float,
+ override val bottom: Float,
+) : Insets(left, top, right, bottom) {
+ operator fun plus(other: InsetsF) = InsetsF(left + other.left, top + other.top, right + other.right, bottom + other.bottom)
+}
+
+data class InsetsD(
+ override val left: Double,
+ override val top: Double,
+ override val right: Double,
+ override val bottom: Double,
+) : Insets(left, top, right, bottom) {
+ operator fun plus(other: InsetsD) = InsetsD(left + other.left, top + other.top, right + other.right, bottom + other.bottom)
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ManagedRect.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ManagedRect.kt
new file mode 100644
index 00000000..f661b451
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ManagedRect.kt
@@ -0,0 +1,37 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.gfx
+
+import kotlin.properties.Delegates.observable
+
+/**
+ * AGIM internally Managed Rectangle
+ */
+abstract class ManagedRect {
+ abstract val value: RectangleF
+
+ protected val observers = LinkedHashSet<(RectangleF) -> Unit>()
+
+ // What is this?
+ internal abstract fun setValue(rect: RectangleF)
+
+ class Normal(rect: RectangleF) : ManagedRect() {
+ override var value: RectangleF by observable(rect) { _, _, newValue -> observers.forEach { it(newValue) } }
+ internal set
+
+ override fun setValue(rect: RectangleF) {
+ value = rect
+ }
+ }
+
+ internal fun observe(observer: (RectangleF) -> Unit) {
+ observers.add(observer)
+ }
+
+ internal fun unobserve(observer: (RectangleF) -> Unit) {
+ observers.remove(observer)
+ }
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ModelTransform.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ModelTransform.kt
new file mode 100644
index 00000000..1dd00547
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/ModelTransform.kt
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.gfx
+
+import net.terramodulus.engine.ModelTransform
+import net.terramodulus.engine.SmartScaling
+
+typealias ModelTransform = ModelTransform
+
+typealias SmartScaling = SmartScaling
+
+typealias FullScaling = net.terramodulus.engine.FullScaling
+
+fun FullScaling(rect: Dimension2I) = FullScaling(rect.width, rect.height)
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Rectangle.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Rectangle.kt
new file mode 100644
index 00000000..6ba2783c
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Rectangle.kt
@@ -0,0 +1,218 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.gfx
+
+import com.cout970.math.vec2.ImmVec2d
+import com.cout970.math.vec2.ImmVec2f
+import com.cout970.math.vec2.ImmVec2i
+import com.cout970.math.vec2.Vec2
+import com.cout970.math.vec2.Vec2d
+import com.cout970.math.vec2.Vec2f
+import com.cout970.math.vec2.Vec2i
+
+/**
+ * Rectangle in a coordinate system with (0, 0) on the bottom left.
+ * The anchor of the rectangle is the bottom-left corner.
+ */
+sealed class Rectangle, N: Number, V: Vec2, D>(
+ open val x: N,
+ open val y: N,
+ open val width: N,
+ open val height: N,
+) {
+ companion object {
+ fun withPoints(x0: Int, y0: Int, x1: Int, y1: Int): RectangleI {
+ val minX: Int;
+ val maxX: Int;
+ if (x0 < x1) {
+ minX = x0;
+ maxX = x1;
+ } else {
+ maxX = x0;
+ minX = x1;
+ }
+ val minY: Int;
+ val maxY: Int;
+ if (y0 < y1) {
+ minY = y0;
+ maxY = y1;
+ } else {
+ maxY = y0;
+ minY = y1;
+ }
+ return RectangleI(minX, minY, maxX - minX, maxY - minY)
+ }
+
+ fun withPoints(x0: Float, y0: Float, x1: Float, y1: Float): RectangleF {
+ val minX: Float;
+ val maxX: Float;
+ if (x0 < x1) {
+ minX = x0;
+ maxX = x1;
+ } else {
+ maxX = x0;
+ minX = x1;
+ }
+ val minY: Float;
+ val maxY: Float;
+ if (y0 < y1) {
+ minY = y0;
+ maxY = y1;
+ } else {
+ maxY = y0;
+ minY = y1;
+ }
+ return RectangleF(minX, maxX, minY, maxY)
+ }
+
+ fun withDirection(x: Int, y: Int, width: Int, height: Int, dir: Direction4AD) = when (dir) {
+ Direction4AD.QuadOne -> RectangleI(x, y, width, height)
+ Direction4AD.QuadTwo -> RectangleI(x - width, y, width, height)
+ Direction4AD.QuadThree -> RectangleI(x - width, y - height, width, height)
+ Direction4AD.QuadFour -> RectangleI(x, y - height, width, height)
+ }
+
+ fun withDirection(x: Float, y: Float, width: Float, height: Float, dir: Direction4AD) = when (dir) {
+ Direction4AD.QuadOne -> RectangleF(x, y, width, height)
+ Direction4AD.QuadTwo -> RectangleF(x - width, y, width, height)
+ Direction4AD.QuadThree -> RectangleF(x - width, y - height, width, height)
+ Direction4AD.QuadFour -> RectangleF(x, y - height, width, height)
+ }
+ }
+
+ protected abstract fun constructor(x: N, y: N, width: N, height: N): T
+ protected abstract fun vec2(x: N, y: N): V
+ protected abstract operator fun N.plus(other: N): N
+ protected abstract operator fun N.minus(other: N): N
+ protected abstract operator fun N.div(other: Int): N
+ protected abstract val V.x: N
+ protected abstract val V.y: N
+
+ abstract val size: D
+
+ fun anchor(pos: Anchor5) = when (pos) {
+ Anchor5.TopLeft -> vec2(x, y + width)
+ Anchor5.TopRight -> vec2(x + width, y + height)
+ Anchor5.BottomLeft -> vec2(x, y)
+ Anchor5.BottomRight -> vec2(x + width, y)
+ Anchor5.Center -> vec2(x + width / 2, y + height / 2)
+ }
+
+ fun translateBy(pos: V) = constructor(x + pos.x, y + pos.y, width, height)
+
+ fun translateBy(x: N, y: N) = constructor(this.x + x, this.y + y, width, height)
+
+ fun translateByY(y: N) = constructor(x, this.y + y, width, height)
+
+ fun translateByX(x: N) = constructor(this.x + x, y, width, height)
+
+ fun translateToY(y: N) = constructor(x, y, width, height)
+
+ fun translateToX(x: N) = constructor(x, y, width, height)
+
+ fun translateTo(pos: V) = constructor(pos.x, pos.y, width, height)
+
+ fun translateTo(x: N, y: N) = constructor(x, y, width, height)
+
+ /** Inflates the [Rectangle] with the [Insets] */
+ operator fun plus(other: Insets) = constructor(
+ x - other.left,
+ y - other.bottom,
+ width + other.left + other.right,
+ height + other.bottom + other.top,
+ )
+
+ /** Deflates the [Rectangle] with the [Insets] */
+ operator fun minus(other: Insets) = constructor(
+ x + other.left,
+ y + other.bottom,
+ width - other.left - other.right,
+ height - other.bottom - other.top,
+ )
+
+ abstract fun toInt(): RectangleI
+ abstract fun toFloat(): RectangleF
+ abstract fun toDouble(): RectangleD
+}
+
+data class RectangleI(
+ override val x: Int,
+ override val y: Int,
+ override val width: Int,
+ override val height: Int
+) : Rectangle(x, y, width, height) {
+ override fun constructor(x: Int, y: Int, width: Int, height: Int) = RectangleI(x, y, width, height)
+
+ override fun vec2(x: Int, y: Int) = ImmVec2i(x, y)
+
+ override fun Int.plus(other: Int) = this + other
+ override fun Int.minus(other: Int) = this - other
+ override fun Int.div(other: Int) = this / other
+
+ override val Vec2i.x: Int by ::x
+ override val Vec2i.y: Int by ::y
+ override val size = Dimension2I(width, height)
+
+ override fun toInt() = this
+ override fun toFloat() = RectangleF(x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat())
+ override fun toDouble() = RectangleD(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble())
+}
+
+data class RectangleF(
+ override val x: Float,
+ override val y: Float,
+ override val width: Float,
+ override val height: Float
+) : Rectangle(x, y, width, height) {
+ override fun constructor(
+ x: Float,
+ y: Float,
+ width: Float,
+ height: Float
+ ) = RectangleF(x, y, width, height)
+
+ override fun vec2(x: Float, y: Float) = ImmVec2f(x, y)
+
+ override fun Float.plus(other: Float) = this + other
+ override fun Float.minus(other: Float) = this - other
+ override fun Float.div(other: Int) = this / other
+
+ override val Vec2f.x: Float by ::x
+ override val Vec2f.y: Float by ::y
+ override val size = Dimension2F(width, height)
+
+ override fun toInt() = RectangleI(x.toInt(), y.toInt(), width.toInt(), height.toInt())
+ override fun toFloat() = this
+ override fun toDouble() = RectangleD(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble())
+}
+
+data class RectangleD(
+ override val x: Double,
+ override val y: Double,
+ override val width: Double,
+ override val height: Double
+) : Rectangle(x, y, width, height) {
+ override fun constructor(
+ x: Double,
+ y: Double,
+ width: Double,
+ height: Double
+ ) = RectangleD(x, y, width, height)
+
+ override fun vec2(x: Double, y: Double) = ImmVec2d(x, y)
+
+ override fun Double.plus(other: Double) = this + other
+ override fun Double.minus(other: Double) = this - other
+ override fun Double.div(other: Int) = this / other
+
+ override val Vec2d.x: Double by ::x
+ override val Vec2d.y: Double by ::y
+ override val size = Dimension2D(width, height)
+
+ override fun toInt() = RectangleI(x.toInt(), y.toInt(), width.toInt(), height.toInt())
+ override fun toFloat() = RectangleF(x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat())
+ override fun toDouble() = this
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/RenderSystem.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/RenderSystem.kt
similarity index 79%
rename from src/kernel/client/kotlin/net/terramodulus/mui/gfx/RenderSystem.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/RenderSystem.kt
index 0e86fb93..6091ae4c 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/gfx/RenderSystem.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/RenderSystem.kt
@@ -1,17 +1,18 @@
/*
- * SPDX-FileCopyrightText: 2025 TerraModulus Team and Contributors
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.gfx
+package net.terramodulus.mui.gui.gfx
+import com.cout970.math.vec3.Vec3f
import net.terramodulus.core.TerraModulus
import net.terramodulus.core.getResourceAsBytes
import net.terramodulus.core.getResourceAsString
import net.terramodulus.engine.Canvas
import net.terramodulus.engine.GeomDrawable
import net.terramodulus.engine.MeshDrawable
-import net.terramodulus.mui.gms.impl.GameplayScreen
+import net.terramodulus.mui.gui.agim.impl.GameplayScreen
class RenderSystem internal constructor(private val core: TerraModulus, private val canvas: Canvas) {
val handle: Handle = HandleImpl()
@@ -39,8 +40,13 @@ class RenderSystem internal constructor(private val core: TerraModulus, private
}
}
- internal fun newGameplayScreen(pos: Vector3F) =
- { it: Handle -> GameplayScreen(core, canvas.createCamera(floatArrayOf(pos.x, pos.y, pos.z)), it) }
+ internal fun newGameplayScreen(pos: Vec3f) = { it: Handle ->
+ GameplayScreen(
+ core,
+ canvas.createCamera(floatArrayOf(pos.x, pos.y, pos.z)),
+ it
+ )
+ }
internal fun renderGuiTex(drawable: MeshDrawable, texture: UInt) = canvas.renderGuiTex(drawable, texShaders, texture)
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Vector.kt b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Vector.kt
new file mode 100644
index 00000000..47f84c19
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/gui/gfx/Vector.kt
@@ -0,0 +1,6 @@
+/*
+ * SPDX-FileCopyrightText: 2025-2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.gui.gfx
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/hui/HuiManager.kt b/src/kernel/client/kotlin/net/terramodulus/mui/hui/HuiManager.kt
new file mode 100644
index 00000000..f3963b9a
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/hui/HuiManager.kt
@@ -0,0 +1,9 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.hui
+
+class HuiManager {
+}
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/input/InputSystem.kt b/src/kernel/client/kotlin/net/terramodulus/mui/kui/InputSystem.kt
similarity index 99%
rename from src/kernel/client/kotlin/net/terramodulus/mui/input/InputSystem.kt
rename to src/kernel/client/kotlin/net/terramodulus/mui/kui/InputSystem.kt
index 7656eed8..d9f8b3e6 100644
--- a/src/kernel/client/kotlin/net/terramodulus/mui/input/InputSystem.kt
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/kui/InputSystem.kt
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: LGPL-3.0-only
*/
-package net.terramodulus.mui.input
+package net.terramodulus.mui.kui
// TODO Temporary solution, should be rewritten in next update.
class InputSystem internal constructor() {
diff --git a/src/kernel/client/kotlin/net/terramodulus/mui/kui/KuiManager.kt b/src/kernel/client/kotlin/net/terramodulus/mui/kui/KuiManager.kt
new file mode 100644
index 00000000..41701cb3
--- /dev/null
+++ b/src/kernel/client/kotlin/net/terramodulus/mui/kui/KuiManager.kt
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.mui.kui
+
+import net.terramodulus.mui.kui.InputSystem.KeyEvent
+
+class KuiManager {
+ val inputSystem = InputSystem()
+
+ internal fun update(events: List) {
+ inputSystem.update(events)
+ }
+}
diff --git a/src/kernel/common/kotlin/net/terramodulus/util/Math.kt b/src/kernel/common/kotlin/net/terramodulus/util/Math.kt
new file mode 100644
index 00000000..fb7265b8
--- /dev/null
+++ b/src/kernel/common/kotlin/net/terramodulus/util/Math.kt
@@ -0,0 +1,22 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.util
+
+tailrec fun gcd(a: Int, b: Int): Int {
+ return if (b == 0) a else gcd(b, a % b)
+}
+
+tailrec fun gcd(a: UInt, b: UInt): UInt {
+ return if (b == 0u) a else gcd(b, a % b)
+}
+
+tailrec fun gcd(a: Long, b: Long): Long {
+ return if (b == 0L) a else gcd(b, a % b)
+}
+
+tailrec fun gcd(a: ULong, b: ULong): ULong {
+ return if (b == 0uL) a else gcd(b, a % b)
+}
diff --git a/src/kernel/common/kotlin/net/terramodulus/util/TypedMap.kt b/src/kernel/common/kotlin/net/terramodulus/util/TypedMap.kt
new file mode 100644
index 00000000..28a3e58a
--- /dev/null
+++ b/src/kernel/common/kotlin/net/terramodulus/util/TypedMap.kt
@@ -0,0 +1,41 @@
+/*
+ * SPDX-FileCopyrightText: 2026 TerraModulus Team and Contributors
+ * SPDX-License-Identifier: LGPL-3.0-only
+ */
+
+package net.terramodulus.util
+
+import java.util.function.BiFunction
+import java.util.function.Function
+
+/**
+ * A map with access by references to unique keys with type of values specified,
+ * matching the pattern of **Typed Anchor Dynamic Mapping**.
+ * Localized constrains may be applied by copying this universal structure.\
+ * All bulk functions are all unsupported for type safety.
+ * @param T base class for map values
+ */
+open class TypedMap>
+ private constructor(private val map: MutableMap) : MutableMap by map {
+ constructor() : this(HashMap())
+
+ open class Simple : TypedMap>()
+
+ /**
+ * A unique, immutable key that defines and enforces the type for values of a [TypedMap].
+ * By hiding public access to the map instance, constrains may be applied by subclassing this.
+ */
+ open class Key(val type: Class) {
+ companion object {
+ inline operator fun invoke() = Key(T::class.java)
+ }
+ override fun hashCode() = type.hashCode()
+ override fun equals(other: Any?): Boolean {
+ if (other === null) return false
+ if (this === other) return true
+ if (other !is Key<*>) return false
+ if (javaClass != other.javaClass) return false
+ return type == other.type
+ }
+ }
+}
diff --git a/src/kernel/common/kotlin/net/terramodulus/void/World.kt b/src/kernel/common/kotlin/net/terramodulus/void/World.kt
index f8fbea9a..6c5d7d9f 100644
--- a/src/kernel/common/kotlin/net/terramodulus/void/World.kt
+++ b/src/kernel/common/kotlin/net/terramodulus/void/World.kt
@@ -5,11 +5,13 @@
package net.terramodulus.void
+import com.cout970.math.vec3.ImmVec3d
+import com.cout970.math.vec3.Vec3d
+import com.cout970.math.vec3.plus
import net.terramodulus.engine.PhyBody
import net.terramodulus.engine.PhyEnv
import net.terramodulus.engine.PhyGeom
import net.terramodulus.engine.PhyGeomBox
-import net.terramodulus.engine.Vec3D
import net.terramodulus.util.logging.logger
import java.io.Closeable
import kotlin.properties.Delegates
@@ -25,7 +27,7 @@ class World(commander: Ymir) : Closeable {
private val env = PhyEnv()
private val world = env.createWorld()
- var gravity: Vec3D by world::gravity
+ var gravity: Vec3d by world::gravity
var frictionMode: FrictionMode by Delegates.observable(FrictionMode.Infinite) { _, _, new ->
when (new) {
FrictionMode.Zero -> world.setFriction(0.0)
@@ -47,7 +49,7 @@ class World(commander: Ymir) : Closeable {
val floor = world.createGeomPlane(doubleArrayOf(0.0, 1.0, 0.0, -100.0))
init {
- gravity = Vec3D(0.0, -9.81, 0.0)
+ gravity = ImmVec3d(0.0, -9.81, 0.0)
floor.setBits(1u, 1u.inv())
world.omitSpace(mainSpace)
// Spawn point
@@ -56,7 +58,7 @@ class World(commander: Ymir) : Closeable {
objects[ObjId.randomUnique(objects)] = commander.wrapChar(
world.newBody(PhyBody.Mass.SphereTotal(1.0, .5)).apply {
addGeom(createGeomSphere(.5))
- pos = Vec3D(0.0, 1.0, 0.0)
+ pos = ImmVec3d(0.0, 1.0, 0.0)
}
)
// Test Objects
@@ -92,7 +94,7 @@ class World(commander: Ymir) : Closeable {
interface VoidGeom {
fun render()
- val pos: Vec3D
+ val pos: Vec3d
}
interface EnvVoidGeom : VoidGeom {
@@ -111,12 +113,12 @@ class World(commander: Ymir) : Closeable {
val interval = 5.0
val max = 5 * 5 * 5 // 125 for each set
val directions = arrayOf(
- Vec3D(1.0, 0.0, 0.0),
- Vec3D(-1.0, 0.0, 0.0),
- Vec3D(0.0, 1.0, 0.0),
- Vec3D(0.0, -1.0, 0.0),
- Vec3D(0.0, 0.0, 1.0),
- Vec3D(0.0, 0.0, -1.0),
+ ImmVec3d(1.0, 0.0, 0.0),
+ ImmVec3d(-1.0, 0.0, 0.0),
+ ImmVec3d(0.0, 1.0, 0.0),
+ ImmVec3d(0.0, -1.0, 0.0),
+ ImmVec3d(0.0, 0.0, 1.0),
+ ImmVec3d(0.0, 0.0, -1.0),
)
for (x in 1..10) {
for (z in 1..10) {
@@ -125,10 +127,10 @@ class World(commander: Ymir) : Closeable {
logger.info { "Generating: ${++i}/$total" }
val xx = (if (xs) x else -x).toDouble() * interval
val zz = (if (zs) z else -z).toDouble() * interval
- val origin = Vec3D(xx, Random.nextInt(-3..3).toDouble(), zz)
- val visited = mutableSetOf(Vec3D(0.0, 0.0, 0.0))
- val heads = ArrayDeque()
- heads.addLast(Vec3D(0.0, 0.0, 0.0))
+ val origin = ImmVec3d(xx, Random.nextInt(-3..3).toDouble(), zz)
+ val visited = mutableSetOf(ImmVec3d(0.0, 0.0, 0.0))
+ val heads = ArrayDeque()
+ heads.addLast(ImmVec3d(0.0, 0.0, 0.0))
while (!heads.isEmpty()) {
val head = heads.removeFirst()
for (d in directions) {
@@ -152,8 +154,6 @@ class World(commander: Ymir) : Closeable {
return list
}
- private operator fun Vec3D.plus(other: Vec3D): Vec3D = Vec3D(x + other.x, y + other.y, z + other.z)
-
private fun createCube(x: Double, y: Double, z: Double): PhyGeomBox {
val cube = createGeomBox(1.0, 1.0, 1.0)
cube.setPosition(doubleArrayOf(x, y, z))
diff --git a/vector-math b/vector-math
new file mode 160000
index 00000000..d25db430
--- /dev/null
+++ b/vector-math
@@ -0,0 +1 @@
+Subproject commit d25db430d52bb0ce28d485b1d073db1acc31a99a