Skip to content

ThreePartAppearance.kt

src/jvmMain/kotlin/afield/graphics/three/ThreePartAppearance.kt · 51 lines

package afield.graphics.three

import afield.geometry.path.minus
import afield.geometry.path.plus
import afield.geometry.path.times
import afield.lang.remotejs.JsFun

import afield.geometry.path.div
import kotlinx.serialization.Serializable

Presentation overrides for a named part of a three-dimensional view.

Colours are RGB in 0..1, and a part with no x-ray opacity of its own follows the viewer's slider. None of it changes geometry, picking, assembly groups or review identity: the styles travel beside the geometry rather than baked into it, so restyling a part leaves every note on it attached.

data class ThreePartAppearance

One part's overrides, as the viewer receives them.

@Serializable
data class ThreePartAppearance(
    /** Red, green and blue, each a finite value in 0..1; anything else is refused. */
    val color: List<Double>,
    /** Draw the part at its own colour, with neither shading nor tone mapping. */
    val unlit: Boolean = false,
    /** This part's x-ray opacity in 0..1, or null to follow the viewer's slider. */
    val xrayOpacity: Double? = null,
) {

internal fun validate()

    internal fun validate() {
        require(color.size == 3 && color.all { it.isFinite() && it in 0.0..1.0 }) { "Colour must contain three finite RGB values in 0..1" }
        require(xrayOpacity == null || xrayOpacity.isFinite() && xrayOpacity in 0.0..1.0) { "X-ray opacity must be in 0..1" }
    }
}

internal fun applyPartAppearance(payload: ThreeGeometryPayload, styles: Map<String, ThreePartAppearance>): ThreeGeometryPayload

What styles do to a payload's drawables: a solid vertex colour, and the style's unlit and xrayOpacity.

The viewer does this itself (partAppearancePayload in threejs_viewer.html), since the server sends the styles beside the geometry rather than baked into it. This is the definition it is held to, by ThreeGeometryWireTest.

internal fun applyPartAppearance(payload: ThreeGeometryPayload, styles: Map<String, ThreePartAppearance>): ThreeGeometryPayload =
    payload.copy(drawables = payload.drawables.map { drawable ->
        val style = styles[drawable.id] ?: return@map drawable
        style.validate()
        val vertices = drawable.geometry.attributes["position"]?.array?.size?.div(3) ?: return@map drawable
        drawable.copy(geometry = drawable.geometry.copy(attributes = drawable.geometry.attributes +
            ("color" to ThreeAttribute(itemSize = 3, array = FloatArray(vertices * 3) { style.color[it % 3].toFloat() }))),
            style = drawable.style.copy(unlit = style.unlit, xrayOpacity = style.xrayOpacity?.toFloat()))
    })