Skip to content

LinalgSerialization.kt

src/jvmMain/kotlin/afield/document/persistence/LinalgSerialization.kt · 174 lines

package afield.document.persistence

import afield.data.table.div
import afield.data.table.get
import afield.data.table.minus
import afield.data.table.plus
import afield.data.table.rem
import afield.data.table.times
import afield.runtime.execution.plus
import afield.runtime.lifecycle.plus
import afield.runtime.lifecycle.rem

import afield.audio.synth.*
import afield.graphics.vulkan.*
import afield.media.stream.*
import afield.media.video.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.FloatArraySerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonEncoder
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.float
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.contextual
import afield.math.linalg.DualQuaternionf
import afield.math.linalg.Quaternionf
import afield.math.linalg.Vector2f
import afield.math.linalg.Vector3f
import afield.math.linalg.Vector4f

JSON serializers for Field's vector and quaternion types, and the FieldJson instance that uses them.

Each serializer writes its value as a flat JSON array of floats (two, three or four for the vectors, four for Quaternionf, eight for DualQuaternionf) and rejects an array of the wrong length on the way back. FieldSerializersModule registers them as contextual serializers.

object Vector2fSerializer : KSerializer<Vector2f>

override val descriptor: SerialDescriptor = FloatArraySerializer().descriptor

override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: Vector2f)

    override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: Vector2f) {
        val jsonEncoder = encoder as? JsonEncoder
            ?: throw SerializationException("Vector2fSerializer only supports JSON")
        jsonEncoder.encodeJsonElement(JsonArray(listOf(JsonPrimitive(value.x), JsonPrimitive(value.y))))
    }

override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): Vector2f

    override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): Vector2f {
        val jsonDecoder = decoder as? JsonDecoder
            ?: throw SerializationException("Vector2fSerializer only supports JSON")
        val values = decodeFloatArray(jsonDecoder, descriptor.serialName, expectedSize = 2)
        return Vector2f(values[0], values[1])
    }
}

object Vector3fSerializer : KSerializer<Vector3f>

JSON serializer for Field's mutable Vector3f, encoded as [x, y, z] floats.

override val descriptor: SerialDescriptor = FloatArraySerializer().descriptor

override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: Vector3f)

    override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: Vector3f) {
        val jsonEncoder = encoder as? JsonEncoder
            ?: throw SerializationException("Vector3fSerializer only supports JSON")
        jsonEncoder.encodeJsonElement(JsonArray(listOf(JsonPrimitive(value.x), JsonPrimitive(value.y), JsonPrimitive(value.z))))
    }

override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): Vector3f

    override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): Vector3f {
        val jsonDecoder = decoder as? JsonDecoder
            ?: throw SerializationException("Vector3fSerializer only supports JSON")
        val values = decodeFloatArray(jsonDecoder, descriptor.serialName, expectedSize = 3)
        return Vector3f(values[0], values[1], values[2])
    }
}

object Vector4fSerializer : KSerializer<Vector4f>

override val descriptor: SerialDescriptor = FloatArraySerializer().descriptor

override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: Vector4f)

    override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: Vector4f) {
        val jsonEncoder = encoder as? JsonEncoder
            ?: throw SerializationException("Vector4fSerializer only supports JSON")
        jsonEncoder.encodeJsonElement(
            JsonArray(listOf(JsonPrimitive(value.x), JsonPrimitive(value.y), JsonPrimitive(value.z), JsonPrimitive(value.w)))
        )
    }

override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): Vector4f

    override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): Vector4f {
        val jsonDecoder = decoder as? JsonDecoder
            ?: throw SerializationException("Vector4fSerializer only supports JSON")
        val values = decodeFloatArray(jsonDecoder, descriptor.serialName, expectedSize = 4)
        return Vector4f(values[0], values[1], values[2], values[3])
    }
}

object QuaternionfSerializer : KSerializer<Quaternionf>

override val descriptor: SerialDescriptor = FloatArraySerializer().descriptor

override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: Quaternionf)

    override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: Quaternionf) {
        val jsonEncoder = encoder as? JsonEncoder
            ?: throw SerializationException("QuaternionfSerializer only supports JSON")
        jsonEncoder.encodeJsonElement(
            JsonArray(listOf(JsonPrimitive(value.x), JsonPrimitive(value.y), JsonPrimitive(value.z), JsonPrimitive(value.w)))
        )
    }

override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): Quaternionf

    override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): Quaternionf {
        val jsonDecoder = decoder as? JsonDecoder
            ?: throw SerializationException("QuaternionfSerializer only supports JSON")
        val values = decodeFloatArray(jsonDecoder, descriptor.serialName, expectedSize = 4)
        return Quaternionf(values[0], values[1], values[2], values[3])
    }
}

object DualQuaternionfSerializer : KSerializer<DualQuaternionf>

override val descriptor: SerialDescriptor = FloatArraySerializer().descriptor

override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: DualQuaternionf)

    override fun serialize(encoder: kotlinx.serialization.encoding.Encoder, value: DualQuaternionf) {
        val jsonEncoder = encoder as? JsonEncoder
            ?: throw SerializationException("DualQuaternionfSerializer only supports JSON")
        jsonEncoder.encodeJsonElement(
            JsonArray(
                listOf(
                    JsonPrimitive(value.real.x),
                    JsonPrimitive(value.real.y),
                    JsonPrimitive(value.real.z),
                    JsonPrimitive(value.real.w),
                    JsonPrimitive(value.dual.x),
                    JsonPrimitive(value.dual.y),
                    JsonPrimitive(value.dual.z),
                    JsonPrimitive(value.dual.w)
                )
            )
        )
    }

override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): DualQuaternionf

    override fun deserialize(decoder: kotlinx.serialization.encoding.Decoder): DualQuaternionf {
        val jsonDecoder = decoder as? JsonDecoder
            ?: throw SerializationException("DualQuaternionfSerializer only supports JSON")
        val values = decodeFloatArray(jsonDecoder, descriptor.serialName, expectedSize = 8)
        return DualQuaternionf(
            Quaternionf(values[0], values[1], values[2], values[3]),
            Quaternionf(values[4], values[5], values[6], values[7])
        ).normalize()
    }
}

val FieldSerializersModule

val FieldSerializersModule = SerializersModule {
    contextual(Vector2f::class, Vector2fSerializer)
    contextual(Vector3f::class, Vector3fSerializer)
    contextual(Vector4f::class, Vector4fSerializer)
    contextual(Quaternionf::class, QuaternionfSerializer)
    contextual(DualQuaternionf::class, DualQuaternionfSerializer)
}

val FieldJson

val FieldJson = Json {
    serializersModule = FieldSerializersModule
    encodeDefaults = true
    ignoreUnknownKeys = true
}

private fun decodeFloatArray(decoder: JsonDecoder, typeName: String, expectedSize: Int): FloatArray {
    val values = decoder.decodeJsonElement().jsonArray
    if (values.size != expectedSize) {
        throw SerializationException("Expected $expectedSize values for $typeName, got ${values.size}")
    }
    return FloatArray(expectedSize) { index -> values[index].jsonPrimitive.float }
}