Engine & Projector
IsometricEngine is the core rendering engine that projects 3D geometry to 2D screen space. It implements the SceneProjector interface, which decouples rendering from the concrete engine to enable testing and custom projections.
IsometricEngine Constructor
Section titled “IsometricEngine Constructor”class IsometricEngine( angle: Double = PI / 6, // 30 degrees scale: Double = 70.0, // pixels per world unit colorDifference: Double = 0.20,// lighting contrast lightColor: IsoColor = IsoColor.WHITE) : SceneProjector| Parameter | Type | Default | Description |
|---|---|---|---|
angle | Double | PI / 6 (30 degrees) | Isometric projection angle in radians. Must be finite. |
scale | Double | 70.0 | Pixels per world unit. Must be positive and finite. |
colorDifference | Double | 0.20 | Strength of directional lighting on faces. Higher values produce more contrast between lit and shadowed faces. Must be non-negative. |
lightColor | IsoColor | IsoColor.WHITE | The color of the light source. Tint this to simulate warm or cool lighting. |
Both angle and scale are mutable properties. Changing them at runtime rebuilds the internal projection matrix and increments projectionVersion.
Key Methods
Section titled “Key Methods”worldToScreen
Section titled “worldToScreen”Converts a 3D world point to 2D screen coordinates.
fun worldToScreen( point: Point, viewportWidth: Int, viewportHeight: Int): Point2DThe origin is placed at the horizontal center (width / 2) and near the bottom (height * 0.9) of the viewport.
screenToWorld
Section titled “screenToWorld”Unprojects a 2D screen point back to 3D world coordinates on a given Z plane. Because a screen point maps to a line in 3D, this returns the intersection with the horizontal plane at height z.
fun screenToWorld( screenPoint: Point2D, viewportWidth: Int, viewportHeight: Int, z: Double = 0.0): PointAdds geometry to the internal scene graph. Two overloads:
// Add all faces of a Shapefun add(shape: Shape, color: IsoColor)
// Add a single Path with optional metadatafun add( path: Path, color: IsoColor, originalShape: Shape? = null, id: String? = null, ownerNodeId: String? = null)Removes all items from the scene graph.
fun clear()projectScene
Section titled “projectScene”Projects the 3D scene to 2D, applies culling, lighting, and depth sorting, then returns a PreparedScene containing sorted RenderCommand entries.
fun projectScene( width: Int, height: Int, renderOptions: RenderOptions = RenderOptions.Default, lightDirection: Vector = DEFAULT_LIGHT_DIRECTION.normalize()): PreparedScenefindItemAt
Section titled “findItemAt”Hit tests a prepared scene at given screen coordinates. Returns the matching RenderCommand or null.
fun findItemAt( preparedScene: PreparedScene, x: Double, y: Double, order: HitOrder = HitOrder.FRONT_TO_BACK, touchRadius: Double = 0.0): RenderCommand?Tile Coordinate Helpers
Section titled “Tile Coordinate Helpers”Extension functions that bridge continuous 3D world coordinates and the discrete tile grid system.
screenToTile
Section titled “screenToTile”Extension function on IsometricEngine. Converts a screen tap point to the TileCoordinate
of the tile cell that contains it. Chains screenToWorld with Point.toTileCoordinate
internally.
fun IsometricEngine.screenToTile( screenX: Double, screenY: Double, viewportWidth: Int, viewportHeight: Int, tileSize: Double = 1.0, elevation: Double = 0.0, originOffset: Point = Point.ORIGIN): TileCoordinate| Parameter | Type | Default | Description |
|---|---|---|---|
screenX | Double | — | Screen x-coordinate of the tap (pixels). |
screenY | Double | — | Screen y-coordinate of the tap (pixels). |
viewportWidth | Int | — | Scene viewport width (pixels). |
viewportHeight | Int | — | Scene viewport height (pixels). |
tileSize | Double | 1.0 | World units per tile side. Must match TileGridConfig.tileSize. |
elevation | Double | 0.0 | Z-plane to intersect during inverse projection. Use the surface z of the tile layer. |
originOffset | Point | Point.ORIGIN | World position of the grid’s (0, 0) corner. Must match TileGridConfig.originOffset. |
For terrain where elevation varies per tile, use screenToWorld directly and call
Point.toTileCoordinate after determining the correct layer.
Point.toTileCoordinate
Section titled “Point.toTileCoordinate”Extension function on Point. Converts a continuous world point to the TileCoordinate of
the cell that contains it. Uses floor() division so negative coordinates map correctly.
fun Point.toTileCoordinate( tileSize: Double = 1.0, originOffset: Point = Point.ORIGIN): TileCoordinate| Parameter | Type | Default | Description |
|---|---|---|---|
tileSize | Double | 1.0 | World units per tile side. Must be positive and finite. |
originOffset | Point | Point.ORIGIN | World position of the grid’s (0, 0) corner. |
The z-coordinate of the receiver Point is ignored — only x and y determine the tile.
Point(3.7, 5.2, 0.0).toTileCoordinate() // TileCoordinate(3, 5)Point(-0.3, 0.0, 0.0).toTileCoordinate() // TileCoordinate(-1, 0) — floor, not truncationprojectionVersion
Section titled “projectionVersion”A Long counter that increments whenever angle or scale changes. Caches that depend on projected output can check this value to know when their data is stale.
val engine = IsometricEngine()val v1 = engine.projectionVersion // 0engine.scale = 100.0val v2 = engine.projectionVersion // 1SceneProjector Interface
Section titled “SceneProjector Interface”SceneProjector is the interface that IsometricEngine implements. Its purpose is dependency inversion: the Compose renderer depends on SceneProjector, not on IsometricEngine directly.
interface SceneProjector { val projectionVersion: Long fun add(shape: Shape, color: IsoColor) fun add(path: Path, color: IsoColor, originalShape: Shape?, id: String?, ownerNodeId: String?) fun clear() fun projectScene(width: Int, height: Int, renderOptions: RenderOptions, lightDirection: Vector): PreparedScene fun findItemAt(preparedScene: PreparedScene, x: Double, y: Double, order: HitOrder, touchRadius: Double): RenderCommand?}This abstraction enables:
- Testing — supply a fake projector that returns canned
PreparedScenedata without running real 3D math. - Custom projections — implement an alternative projection (e.g., oblique, dimetric) while reusing the Compose runtime.
Direct Usage Outside Compose
Section titled “Direct Usage Outside Compose”IsometricEngine can be used as a standalone projection library without the Compose runtime, for server-side rendering, screenshot generation, or non-Compose UI frameworks. See Advanced Patterns — Direct Engine Usage for a full worked example.