From 6b4bc6912f7fd799e79f9a157c3a0a970d31d58a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 31 Oct 2020 19:03:55 +0300 Subject: [PATCH] Refactor serialization --- .../vision/gdml/demo/GDMLAppComponent.kt | 4 +- .../vision/gdml/demo/GdmlFxDemoApp.kt | 5 +- .../dataforge/vision/gdml/demo/readFile.kt | 12 +- .../dataforge/vision/gdml/demo/saveToJson.kt | 6 +- .../vision/solid/FileSerializationTest.kt | 2 +- .../ru/mipt/npm/muon/monitor/MMDemoApp.kt | 2 +- .../mipt/npm/muon/monitor/server/MMServer.kt | 8 +- .../hep/dataforge/vision/AbstractVision.kt | 4 + .../kotlin/hep/dataforge/vision/Vision.kt | 9 +- .../hep/dataforge/vision/VisionManager.kt | 149 +++++++---------- .../kotlin/hep/dataforge/vision/misc.kt | 4 +- .../dataforge/vision/gdml/TestConvertor.kt | 8 +- .../dataforge/vision/solid/AbstractSolid.kt | 17 -- .../hep/dataforge/vision/solid/BasicSolid.kt | 34 ++++ .../kotlin/hep/dataforge/vision/solid/Box.kt | 5 +- .../hep/dataforge/vision/solid/Composite.kt | 3 +- .../hep/dataforge/vision/solid/ConeSegment.kt | 5 +- .../hep/dataforge/vision/solid/Convex.kt | 5 +- .../hep/dataforge/vision/solid/Extruded.kt | 5 +- .../hep/dataforge/vision/solid/PolyLine.kt | 2 +- .../hep/dataforge/vision/solid/Proxy.kt | 2 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 11 +- .../hep/dataforge/vision/solid/SolidLabel.kt | 2 +- .../dataforge/vision/solid/SolidManager.kt | 151 ++++++++---------- .../hep/dataforge/vision/solid/Sphere.kt | 2 +- .../kotlin/hep/dataforge/vision/solid/Tube.kt | 2 +- .../dataforge/vision/solid/serialization.kt | 10 -- .../vision/solid/SerializationTest.kt | 9 +- .../dataforge/vision/solid/SolidPluginTest.kt | 6 +- .../vision/solid/three/ThreeVision.kt | 4 +- 30 files changed, 221 insertions(+), 267 deletions(-) delete mode 100644 visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/AbstractSolid.kt create mode 100644 visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/BasicSolid.kt diff --git a/demo/gdml/src/jsMain/kotlin/hep/dataforge/vision/gdml/demo/GDMLAppComponent.kt b/demo/gdml/src/jsMain/kotlin/hep/dataforge/vision/gdml/demo/GDMLAppComponent.kt index f5704cb7..828641d9 100644 --- a/demo/gdml/src/jsMain/kotlin/hep/dataforge/vision/gdml/demo/GDMLAppComponent.kt +++ b/demo/gdml/src/jsMain/kotlin/hep/dataforge/vision/gdml/demo/GDMLAppComponent.kt @@ -10,6 +10,7 @@ import hep.dataforge.vision.gdml.toVision import hep.dataforge.vision.react.objectTree import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.SolidGroup +import hep.dataforge.vision.solid.SolidManager import hep.dataforge.vision.solid.three.ThreeCanvas import hep.dataforge.vision.solid.three.ThreeCanvasComponent import kotlinx.browser.window @@ -50,12 +51,13 @@ val GDMLApp = functionalComponent("GDMLApp") { props -> } fun loadData(name: String, data: String) { + val visionManager = props.context.plugins.fetch(SolidManager).visionManager val parsedVision = when { name.endsWith(".gdml") || name.endsWith(".xml") -> { val gdml = GDML.decodeFromString(data) gdml.toVision() } - name.endsWith(".json") -> SolidGroup.decodeFromString(data) + name.endsWith(".json") -> visionManager.decodeFromString(data) else -> { window.alert("File extension is not recognized: $name") error("File extension is not recognized: $name") diff --git a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt index c69682b8..0ccf3c70 100644 --- a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt +++ b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt @@ -4,6 +4,8 @@ import hep.dataforge.context.Global import hep.dataforge.vision.editor.VisualObjectEditorFragment import hep.dataforge.vision.editor.VisualObjectTreeFragment import hep.dataforge.vision.gdml.toVision +import hep.dataforge.vision.VisionManager +import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.SolidManager import hep.dataforge.vision.solid.SolidMaterial import hep.dataforge.vision.solid.fx.FX3DPlugin @@ -17,6 +19,7 @@ class GDMLDemoApp : App(GDMLView::class) class GDMLView : View() { private val fx3d = Global.plugins.fetch(FX3DPlugin) + private val visionManager = Global.plugins.fetch(VisionManager) private val canvas = FXCanvas3D(fx3d) private val treeFragment = VisualObjectTreeFragment().apply { @@ -39,7 +42,7 @@ class GDMLView : View() { val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull() if(file!= null) { runAsync { - SolidManager.readFile(file) + visionManager.readFile(file) as Solid } ui { canvas.render(it) } diff --git a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/readFile.kt b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/readFile.kt index e4344795..da958c46 100644 --- a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/readFile.kt +++ b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/readFile.kt @@ -8,13 +8,15 @@ import hep.dataforge.vision.gdml.toVision import hep.dataforge.vision.solid.SolidGroup import hep.dataforge.vision.solid.SolidManager import hep.dataforge.vision.solid.SolidMaterial +import hep.dataforge.vision.Vision +import hep.dataforge.vision.VisionManager import kscience.gdml.GDML import java.io.File import java.util.zip.GZIPInputStream import java.util.zip.ZipInputStream @OptIn(DFExperimental::class) -fun SolidManager.Companion.readFile(file: File): SolidGroup = when { +fun VisionManager.readFile(file: File): Vision = when { file.extension == "gdml" || file.extension == "xml" -> { GDML.readFile(file.toPath()).toVision { // lUnit = LUnit.CM @@ -31,23 +33,23 @@ fun SolidManager.Companion.readFile(file: File): SolidGroup = when { // } } } - file.extension == "json" -> SolidGroup.decodeFromString(file.readText()) + file.extension == "json" -> decodeFromString(file.readText()) file.name.endsWith("json.zip") -> { file.inputStream().use { val unzip = ZipInputStream(it, Charsets.UTF_8) val text = unzip.readBytes().decodeToString() - SolidGroup.decodeFromString(text) + decodeFromString(text) } } file.name.endsWith("json.gz") -> { file.inputStream().use { val unzip = GZIPInputStream(it) val text = unzip.readBytes().decodeToString() - SolidGroup.decodeFromString(text) + decodeFromString(text) } } else -> error("Unknown extension ${file.extension}") } @OptIn(DFExperimental::class) -fun SolidManager.Companion.readFile(fileName: String): SolidGroup = readFile(File(fileName)) \ No newline at end of file +fun VisionManager.readFile(fileName: String): Vision = readFile(File(fileName)) \ No newline at end of file diff --git a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/saveToJson.kt b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/saveToJson.kt index 798fd8b7..ccc7da85 100644 --- a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/saveToJson.kt +++ b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/saveToJson.kt @@ -2,7 +2,7 @@ package hep.dataforge.vision.gdml.demo import hep.dataforge.vision.gdml.readFile import hep.dataforge.vision.gdml.toVision -import hep.dataforge.vision.solid.encodeToString +import hep.dataforge.vision.solid.SolidManager import kscience.gdml.GDML import kscience.gdml.LUnit import java.io.File @@ -17,11 +17,11 @@ fun main(args: Array) { val gdml = GDML.readFile(Paths.get(inputFileName)) //GDML.readFile(Paths.get("D:\\Work\\Projects\\visionforge\\visionforge-spatial-gdml\\src\\jvmTest\\resources\\gdml\\simple1.gdml")) - val visual = gdml.toVision { + val vision = gdml.toVision { lUnit = LUnit.CM } - val json = visual.encodeToString() + val json = SolidManager.encodeToString(vision) println(json) File(outputFileName).writeText(json) //File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.json").writeText(json) diff --git a/demo/gdml/src/jvmTest/kotlin/hep/dataforge/vision/solid/FileSerializationTest.kt b/demo/gdml/src/jvmTest/kotlin/hep/dataforge/vision/solid/FileSerializationTest.kt index a1f9bab9..4fe68578 100644 --- a/demo/gdml/src/jvmTest/kotlin/hep/dataforge/vision/solid/FileSerializationTest.kt +++ b/demo/gdml/src/jvmTest/kotlin/hep/dataforge/vision/solid/FileSerializationTest.kt @@ -9,7 +9,7 @@ class FileSerializationTest { @Ignore fun testFileRead(){ val text = this::class.java.getResourceAsStream("/cubes.json").readBytes().decodeToString() - val visual = SolidGroup.decodeFromString(text) + val visual = SolidManager.decodeFromString(text) as SolidGroup visual["composite_001".asName()] } } \ No newline at end of file diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt index 62b538ab..282050d2 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt @@ -19,7 +19,7 @@ private class MMDemoApp : Application { private val connection = HttpClient { install(JsonFeature) { - serializer = KotlinxSerializer(Json { serializersModule = SolidManager.serializersModule }) + serializer = KotlinxSerializer(Json { serializersModule = SolidManager.serializersModuleForSolids }) } } diff --git a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt index 71d6b53a..b4a55ad5 100644 --- a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt +++ b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt @@ -1,6 +1,8 @@ package ru.mipt.npm.muon.monitor.server +import hep.dataforge.context.Context +import hep.dataforge.context.Global import hep.dataforge.meta.DFExperimental import hep.dataforge.vision.solid.SolidManager import io.ktor.application.Application @@ -32,15 +34,15 @@ import java.net.URI private val generator = Cos2TrackGenerator(JDKRandomGenerator(223)) @OptIn(DFExperimental::class) -fun Application.module() { +fun Application.module(context: Context = Global) { val currentDir = File(".").absoluteFile environment.log.info("Current directory: $currentDir") - + val solidManager = context.plugins.load(SolidManager) install(DefaultHeaders) install(CallLogging) install(ContentNegotiation) { - json(Json { serializersModule = SolidManager.serialModule }, ContentType.Application.Json) + json(solidManager.visionManager.jsonFormat, ContentType.Application.Json) } install(Routing) { get("/event") { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVision.kt index 909d021d..50bcc9b7 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVision.kt @@ -94,6 +94,10 @@ public open class AbstractVision : Vision { properties = null } + override fun update(meta: Meta) { + meta[Vision::properties.name].node?.let { configure(it) } + } + public companion object { public val descriptor: NodeDescriptor = NodeDescriptor { value(STYLE_KEY) { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 01f33d9b..2301ce54 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -68,13 +68,16 @@ public interface Vision : Configurable { config[STYLE_KEY] = value } + /** + * Update this vision using external meta. + */ + public fun update(meta: Meta) + public companion object { public const val TYPE: String = "vision" public val STYLE_KEY: Name = "@style".asName() - private val VISION_SERIALIZER = PolymorphicSerializer(Vision::class) - - public fun serializer(): PolymorphicSerializer = VISION_SERIALIZER + public fun serializer(): PolymorphicSerializer = PolymorphicSerializer(Vision::class) public val VISIBLE_KEY: Name = "visible".asName() } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt index 39f7096f..a1e5170c 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt @@ -3,80 +3,21 @@ package hep.dataforge.vision import hep.dataforge.context.* import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.names.Name -import hep.dataforge.names.toName -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass import kotlin.reflect.KClass - -@DFExperimental -public interface VisionForm { - public val type: KClass - public val serializer: KSerializer - - public val name: Name - get() = serializer.descriptor.serialName.toName() - - /** - * Apply a patch to given [Vision] - */ - public fun patch(obj: T, meta: Meta) - - public companion object { - public const val TYPE: String = "visionForm" - } -} - -@DFExperimental -public object SimpleGroupForm : VisionForm { - override val type: KClass = SimpleVisionGroup::class - override val serializer: KSerializer = SimpleVisionGroup.serializer() - - override fun patch(obj: SimpleVisionGroup, meta: Meta) { - TODO("Not yet implemented") - } - -} - -@DFExperimental -public fun VisionForm.visionToMeta( - vision: T, - module: SerializersModule, - descriptor: NodeDescriptor? = null, -): Meta { - val engine = Json(VisionManager.jsonConfiguration) { serializersModule = module } - val json = engine.encodeToJsonElement(serializer, vision) - return json.toMetaItem(descriptor).node!! -} - -@DFExperimental -public fun VisionForm.buildVision( - meta: Meta, - module: SerializersModule, - descriptor: NodeDescriptor? = null, -): T { - val engine = Json(VisionManager.jsonConfiguration) { serializersModule = module } - val json = meta.toJson(descriptor) - return engine.decodeFromJsonElement(serializer, json) -} - @DFExperimental public class VisionManager(meta: Meta) : AbstractPlugin(meta) { override val tag: PluginTag get() = Companion.tag /** - * Create a list of factories on first call and cache it + * Combined [SerializersModule] for all registered visions */ - private val forms by lazy { - context.gather>(VisionForm.TYPE).mapKeys { it.value.type } - } - - public val visionSerialModule: SerializersModule + public val serializersModule: SerializersModule get() = SerializersModule { include(defaultSerialModule) context.gather(VISION_SERIAL_MODULE_TARGET).values.forEach { @@ -84,33 +25,60 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) { } } - @Suppress("UNCHECKED_CAST") - public fun resolveVisionForm(type: KClass): VisionForm = - forms[type] as VisionForm + public val jsonFormat: Json + get() = Json { + prettyPrint = true + useArrayPolymorphism = false + encodeDefaults = false + ignoreUnknownKeys = true + serializersModule = this@VisionManager.serializersModule + } + + public fun decodeFromString(string: String): Vision = jsonFormat.decodeFromString(Vision.serializer(), string) + public fun encodeToString(vision: Vision): String = jsonFormat.encodeToString(Vision.serializer(), vision) + + public fun decodeFromJson(json: JsonElement): Vision = jsonFormat.decodeFromJsonElement(Vision.serializer(), json) + public fun encodeToJsonElement(vision: Vision): JsonElement = + jsonFormat.encodeToJsonElement(Vision.serializer(), vision) + + //TODO remove double transformation with dedicated Meta serial format + public fun decodeFromMeta(meta: Meta, descriptor: NodeDescriptor? = null): Vision = + decodeFromJson(meta.toJson(descriptor)) + + public fun encodeToMeta(vision: Vision, descriptor: NodeDescriptor? = null): Meta = + encodeToJsonElement(vision).toMetaItem(descriptor).node + ?: error("Expected node, but value found. Check your serializer!") + + public fun updateVision(vision: Vision, meta: Meta) { - public inline fun buildSpecificVision(meta: Meta): T { - val factory = resolveVisionForm(T::class) ?: error("Could not resolve a form for ${meta["type"].string}") - return factory.buildVision(meta, visionSerialModule) } - @OptIn(ExperimentalSerializationApi::class) - public fun buildVision(meta: Meta): Vision { - val type = meta["type"].string ?: Vision.serializer().descriptor.serialName - val form = forms.values.find { it.name.toString() == type } ?: error("Could not resolve a form for type $type") - return form.buildVision(meta, visionSerialModule) - } - - public fun writeVisionToMeta(vision: T): Meta { - val form = resolveVisionForm(vision::class) ?: error("Could not resolve a form for $vision") - val engine = Json(jsonConfiguration) { serializersModule = visionSerialModule } - val json = engine.encodeToJsonElement(form.serializer, vision) - return json.toMetaItem().node!! - } - - public fun patchVision(vision: Vision, meta: Meta) { - val form = resolveVisionForm(vision::class) ?: error("Could not resolve a form for $vision") - form.patch(vision, meta) - } +// public fun VisionForm.buildVision(meta: Meta, descriptor: NodeDescriptor? = null): T { +// val json = meta.toJson(descriptor) +// return jsonFormat.decodeFromJsonElement(serializer, json) +// } +// +// @OptIn(ExperimentalSerializationApi::class) +// public fun buildVision(meta: Meta): Vision { +// val type = meta["type"].string ?: Vision.serializer().descriptor.serialName +// val form = forms.values.find { it.name.toString() == type } ?: error("Could not resolve a form for type $type") +// return form.buildVision(meta) +// } +// +// public inline fun buildSpecificVision(meta: Meta): T { +// val factory = resolveVisionForm(T::class) +// return factory.buildVision(meta) +// } +// +// public fun writeVisionToMeta(vision: T): Meta { +// val form = resolveVisionForm(vision::class) ?: error("Could not resolve a form for $vision") +// val json = jsonFormat.encodeToJsonElement(form.serializer, vision) +// return json.toMetaItem().node!! +// } +// +// public fun updateVision(vision: Vision, meta: Meta) { +// resolveVisionForm(vision::class).patch(vision, meta) +// } public companion object : PluginFactory { override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP) @@ -120,14 +88,7 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) { override fun invoke(meta: Meta, context: Context): VisionManager = VisionManager(meta) - public val jsonConfiguration: Json = Json { - prettyPrint = true - useArrayPolymorphism = false - encodeDefaults = false - ignoreUnknownKeys = true - } - - public val defaultSerialModule: SerializersModule = SerializersModule { + private val defaultSerialModule: SerializersModule = SerializersModule { polymorphic(Vision::class) { subclass(SimpleVisionGroup.serializer()) } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt index 6b2d5fc7..54e939a2 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt @@ -8,7 +8,7 @@ import hep.dataforge.names.Name import hep.dataforge.values.ValueType import hep.dataforge.values.asValue -fun Sequence?>.merge(): MetaItem<*>? { +public fun Sequence?>.merge(): MetaItem<*>? { return when (val first = firstOrNull { it != null }) { null -> null is MetaItem.ValueItem -> first //fast search for first entry if it is value @@ -20,7 +20,7 @@ fun Sequence?>.merge(): MetaItem<*>? { } } -inline fun > NodeDescriptor.enum(key: Name, default: E?) = value(key) { +public inline fun > NodeDescriptor.enum(key: Name, default: E?): Unit = value(key) { type(ValueType.STRING) default?.let { default(default) diff --git a/visionforge-gdml/src/jvmTest/kotlin/hep/dataforge/vision/gdml/TestConvertor.kt b/visionforge-gdml/src/jvmTest/kotlin/hep/dataforge/vision/gdml/TestConvertor.kt index 2dac5a76..3f4df637 100644 --- a/visionforge-gdml/src/jvmTest/kotlin/hep/dataforge/vision/gdml/TestConvertor.kt +++ b/visionforge-gdml/src/jvmTest/kotlin/hep/dataforge/vision/gdml/TestConvertor.kt @@ -1,6 +1,6 @@ package hep.dataforge.vision.gdml -import hep.dataforge.vision.solid.encodeToString +import hep.dataforge.vision.solid.SolidManager import kscience.gdml.GDML import nl.adaptivity.xmlutil.StAXReader import org.junit.jupiter.api.Test @@ -13,7 +13,7 @@ class TestConvertor { val xmlReader = StAXReader(stream, "UTF-8") val xml = GDML.format.parse(GDML.serializer(), xmlReader) val vision = xml.toVision() - println(vision.encodeToString()) + println(SolidManager.encodeToString(vision)) } @Test @@ -32,7 +32,7 @@ class TestConvertor { val xmlReader = StAXReader(stream, "UTF-8") val xml = GDML.format.parse(GDML.serializer(), xmlReader) - val visual = xml.toVision() - println(visual.encodeToString()) + val vision = xml.toVision() + println(SolidManager.encodeToString(vision)) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/AbstractSolid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/AbstractSolid.kt deleted file mode 100644 index 109ec39e..00000000 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/AbstractSolid.kt +++ /dev/null @@ -1,17 +0,0 @@ -package hep.dataforge.vision.solid - -import hep.dataforge.vision.AbstractVision -import kotlinx.serialization.Serializable -import kotlinx.serialization.UseSerializers - -@Serializable -public open class AbstractSolid: AbstractVision(), Solid { - @Serializable(Point3DSerializer::class) - override var position: Point3D? = null - - @Serializable(Point3DSerializer::class) - override var rotation: Point3D? = null - - @Serializable(Point3DSerializer::class) - override var scale: Point3D? = null -} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/BasicSolid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/BasicSolid.kt new file mode 100644 index 00000000..7cd198bb --- /dev/null +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/BasicSolid.kt @@ -0,0 +1,34 @@ +package hep.dataforge.vision.solid + +import hep.dataforge.meta.* +import hep.dataforge.vision.AbstractVision +import hep.dataforge.vision.Vision +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers + +@Serializable +@SerialName("solid") +public open class BasicSolid: AbstractVision(), Solid { + @Serializable(Point3DSerializer::class) + override var position: Point3D? = null + + @Serializable(Point3DSerializer::class) + override var rotation: Point3D? = null + + @Serializable(Point3DSerializer::class) + override var scale: Point3D? = null + + override fun update(meta: Meta) { + fun Meta.toVector(default: Float = 0f) = Point3D( + this[Solid.X_KEY].float ?: default, + this[Solid.Y_KEY].float ?: default, + this[Solid.Z_KEY].float ?: default + ) + + meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it } + meta[Solid.ROTATION].node?.toVector()?.let { rotation = it } + meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it } + super.update(meta) + } +} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Box.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Box.kt index aa2e42f0..31eca267 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Box.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Box.kt @@ -2,9 +2,6 @@ package hep.dataforge.vision.solid -import hep.dataforge.meta.Config -import hep.dataforge.vision.AbstractVision -import hep.dataforge.vision.MutableVisionGroup import hep.dataforge.vision.VisionContainerBuilder import hep.dataforge.vision.set import hep.dataforge.vision.solid.Solid.Companion.solidEquals @@ -18,7 +15,7 @@ public class Box( public val xSize: Float, public val ySize: Float, public val zSize: Float -) : AbstractSolid(), GeometrySolid { +) : BasicSolid(), GeometrySolid { //TODO add helper for color configuration override fun toGeometry(geometryBuilder: GeometryBuilder) { diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt index a019e368..a293dc09 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt @@ -2,7 +2,6 @@ package hep.dataforge.vision.solid -import hep.dataforge.meta.Config import hep.dataforge.meta.update import hep.dataforge.names.NameToken import hep.dataforge.vision.* @@ -22,7 +21,7 @@ public class Composite( public val compositeType: CompositeType, public val first: Solid, public val second: Solid -) : AbstractSolid(), Solid, VisionGroup { +) : BasicSolid(), Solid, VisionGroup { init { first.parent = this diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ConeSegment.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ConeSegment.kt index 084f2f8e..31ca2786 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ConeSegment.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ConeSegment.kt @@ -2,9 +2,6 @@ package hep.dataforge.vision.solid -import hep.dataforge.meta.Config -import hep.dataforge.vision.AbstractVision -import hep.dataforge.vision.MutableVisionGroup import hep.dataforge.vision.VisionContainerBuilder import hep.dataforge.vision.set import kotlinx.serialization.SerialName @@ -24,7 +21,7 @@ public class ConeSegment( public var upperRadius: Float, public var startAngle: Float = 0f, public var angle: Float = PI2 -) : AbstractSolid(), GeometrySolid { +) : BasicSolid(), GeometrySolid { override fun toGeometry(geometryBuilder: GeometryBuilder) { val segments = detail ?: 8 diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Convex.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Convex.kt index a96cc8bb..6a0b9de9 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Convex.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Convex.kt @@ -2,9 +2,6 @@ package hep.dataforge.vision.solid -import hep.dataforge.meta.Config -import hep.dataforge.vision.AbstractVision -import hep.dataforge.vision.MutableVisionGroup import hep.dataforge.vision.VisionContainerBuilder import hep.dataforge.vision.set import kotlinx.serialization.SerialName @@ -13,7 +10,7 @@ import kotlinx.serialization.UseSerializers @Serializable @SerialName("solid.convex") -public class Convex(public val points: List) : AbstractSolid(), Solid +public class Convex(public val points: List) : BasicSolid(), Solid public inline fun VisionContainerBuilder.convex(name: String = "", action: ConvexBuilder.() -> Unit = {}): Convex = ConvexBuilder().apply(action).build().also { set(name, it) } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Extruded.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Extruded.kt index afab61f9..f0f22814 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Extruded.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Extruded.kt @@ -1,9 +1,6 @@ @file:UseSerializers(Point2DSerializer::class, Point3DSerializer::class) package hep.dataforge.vision.solid -import hep.dataforge.meta.Config -import hep.dataforge.vision.AbstractVision -import hep.dataforge.vision.MutableVisionGroup import hep.dataforge.vision.VisionContainerBuilder import hep.dataforge.vision.set import kotlinx.serialization.SerialName @@ -44,7 +41,7 @@ public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Flo public class Extruded( public var shape: List = ArrayList(), public var layers: MutableList = ArrayList() -) : AbstractSolid(), GeometrySolid { +) : BasicSolid(), GeometrySolid { public fun shape(block: Shape2DBuilder.() -> Unit) { this.shape = Shape2DBuilder().apply(block).build() diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt index aac82df5..1a6769b8 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt @@ -14,7 +14,7 @@ import kotlinx.serialization.UseSerializers @Serializable @SerialName("solid.line") -public class PolyLine(public var points: List) : AbstractSolid(), Solid { +public class PolyLine(public var points: List) : BasicSolid(), Solid { //var lineType by string() public var thickness: Number by number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Proxy.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Proxy.kt index 2c75b669..9f58912b 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Proxy.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Proxy.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlin.collections.set -public abstract class AbstractProxy : AbstractSolid(), VisionGroup { +public abstract class AbstractProxy : BasicSolid(), VisionGroup { public abstract val prototype: Vision override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index 7c9f16fc..6d787bb5 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -8,7 +8,9 @@ import hep.dataforge.names.asName import hep.dataforge.vision.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.Serializer import kotlinx.serialization.UseSerializers +import kotlinx.serialization.json.Json import kotlin.collections.set public interface PrototypeHolder { @@ -45,8 +47,13 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder { //FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed override var properties: Config? = null + @Serializable(Point3DSerializer::class) override var position: Point3D? = null + + @Serializable(Point3DSerializer::class) override var rotation: Point3D? = null + + @Serializable(Point3DSerializer::class) override var scale: Point3D? = null @SerialName("children") @@ -77,10 +84,6 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder { public companion object { // val PROTOTYPES_KEY = NameToken("@prototypes") - - @OptIn(DFExperimental::class) - public fun decodeFromString(json: String): SolidGroup = - SolidManager.jsonForSolids.decodeFromString(serializer(), json).also { it.attachChildren() } } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidLabel.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidLabel.kt index a9740cb0..bb033215 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidLabel.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidLabel.kt @@ -11,7 +11,7 @@ public class SolidLabel( public var text: String, public var fontSize: Double, public var fontFamily: String, -) : AbstractSolid(), Solid +) : BasicSolid(), Solid public fun VisionContainerBuilder.label( text: String, diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt index bdd83669..dd070481 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt @@ -9,73 +9,48 @@ import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.vision.SimpleVisionGroup import hep.dataforge.vision.Vision -import hep.dataforge.vision.VisionForm +import hep.dataforge.vision.VisionGroup import hep.dataforge.vision.VisionManager import hep.dataforge.vision.VisionManager.Companion.VISION_SERIAL_MODULE_TARGET -import kotlinx.serialization.* import kotlinx.serialization.json.Json import kotlinx.serialization.modules.* import kotlin.reflect.KClass -@DFExperimental -private class SolidForm( - override val type: KClass, - override val serializer: KSerializer, -) : VisionForm { - - private fun Solid.update(meta: Meta) { - fun Meta.toVector(default: Float = 0f) = Point3D( - this[Solid.X_KEY].float ?: default, - this[Solid.Y_KEY].float ?: default, - this[Solid.Z_KEY].float ?: default - ) - - meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it } - meta[Solid.ROTATION].node?.toVector()?.let { rotation = it } - meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it } - meta["properties"].node?.let { configure(it) } - } - - override fun patch(obj: T, meta: Meta) { - TODO("Not yet implemented") - } -} - -@OptIn(ExperimentalSerializationApi::class) -@DFExperimental -private fun SerializersModule.extractFactories(): List> { - val list = ArrayList>() - - val collector = object : SerializersModuleCollector { - override fun contextual(kClass: KClass, serializer: KSerializer) { - //Do nothing - } - - override fun polymorphic( - baseClass: KClass, - actualClass: KClass, - actualSerializer: KSerializer, - ) { - if (baseClass == Vision::class) { - @Suppress("UNCHECKED_CAST") val factory = SolidForm( - actualClass as KClass, - actualSerializer as KSerializer - ) - list.add(factory) - } - } - - override fun polymorphicDefault( - baseClass: KClass, - defaultSerializerProvider: (className: String?) -> DeserializationStrategy?, - ) { - TODO("Not yet implemented") - } - - } - dumpTo(collector) - return list -} +//@OptIn(ExperimentalSerializationApi::class) +//@DFExperimental +//private fun SerializersModule.extractFactories(): List> { +// val list = ArrayList>() +// +// val collector = object : SerializersModuleCollector { +// override fun contextual(kClass: KClass, serializer: KSerializer) { +// //Do nothing +// } +// +// override fun polymorphic( +// baseClass: KClass, +// actualClass: KClass, +// actualSerializer: KSerializer, +// ) { +// if (baseClass == Vision::class) { +// @Suppress("UNCHECKED_CAST") val factory: SolidForm = SolidForm( +// actualClass as KClass, +// actualSerializer as KSerializer +// ) +// list.add(factory) +// } +// } +// +// override fun polymorphicDefault( +// baseClass: KClass, +// defaultSerializerProvider: (className: String?) -> DeserializationStrategy?, +// ) { +// TODO("Not yet implemented") +// } +// +// } +// dumpTo(collector) +// return list +//} @DFExperimental public class SolidManager(meta: Meta) : AbstractPlugin(meta) { @@ -85,8 +60,7 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) { override val tag: PluginTag get() = Companion.tag override fun content(target: String): Map = when (target) { - VisionForm.TYPE -> serializersModule.extractFactories().associateBy { it.name } - VISION_SERIAL_MODULE_TARGET -> mapOf(tag.name.toName() to serializersModule) + VISION_SERIAL_MODULE_TARGET -> mapOf(tag.name.toName() to serializersModuleForSolids) else -> super.content(target) } @@ -95,44 +69,49 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) { override val type: KClass = SolidManager::class override fun invoke(meta: Meta, context: Context): SolidManager = SolidManager(meta) - public val serializersModule: SerializersModule = SerializersModule { + private fun PolymorphicModuleBuilder.solids() { + subclass(SolidGroup.serializer()) + subclass(Proxy.serializer()) + subclass(Composite.serializer()) + subclass(Tube.serializer()) + subclass(Box.serializer()) + subclass(Convex.serializer()) + subclass(Extruded.serializer()) + subclass(PolyLine.serializer()) + subclass(SolidLabel.serializer()) + subclass(Sphere.serializer()) + } + + public val serializersModuleForSolids: SerializersModule = SerializersModule { contextual(Point3DSerializer) contextual(Point2DSerializer) polymorphic(Vision::class) { subclass(SimpleVisionGroup.serializer()) - subclass(SolidGroup.serializer()) - subclass(Proxy.serializer()) - subclass(Composite.serializer()) - subclass(Tube.serializer()) - subclass(Box.serializer()) - subclass(Convex.serializer()) - subclass(Extruded.serializer()) - subclass(PolyLine.serializer()) - subclass(SolidLabel.serializer()) - subclass(Sphere.serializer()) + solids() } polymorphic(Solid::class) { - subclass(SolidGroup.serializer()) - subclass(Proxy.serializer()) - subclass(Composite.serializer()) - subclass(Tube.serializer()) - subclass(Box.serializer()) - subclass(Convex.serializer()) - subclass(Extruded.serializer()) - subclass(PolyLine.serializer()) - subclass(SolidLabel.serializer()) - subclass(Sphere.serializer()) + solids() } } - val jsonForSolids = Json { + public val jsonForSolids = Json{ prettyPrint = true useArrayPolymorphism = false encodeDefaults = false ignoreUnknownKeys = true - serializersModule = this@Companion.serializersModule + serializersModule = SolidManager.serializersModuleForSolids + } + + @OptIn(DFExperimental::class) + public fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(Vision.serializer(), solid) + + @OptIn(DFExperimental::class) + public fun decodeFromString(str: String): Vision = jsonForSolids.decodeFromString(Vision.serializer(), str).also { + if(it is VisionGroup){ + it.attachChildren() + } } } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Sphere.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Sphere.kt index 9a647155..7a122c44 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Sphere.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Sphere.kt @@ -16,7 +16,7 @@ public class Sphere( public var phi: Float = PI2, public var thetaStart: Float = 0f, public var theta: Float = PI.toFloat(), -) : AbstractSolid(), GeometrySolid { +) : BasicSolid(), GeometrySolid { override fun toGeometry(geometryBuilder: GeometryBuilder) { fun point3DfromSphCoord(r: Float, theta: Float, phi: Float): Point3D { diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Tube.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Tube.kt index d03dfa98..6d486da4 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Tube.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Tube.kt @@ -19,7 +19,7 @@ public class Tube( public var innerRadius: Float = 0f, public var startAngle: Float = 0f, public var angle: Float = PI2, -) : AbstractSolid(), GeometrySolid { +) : BasicSolid(), GeometrySolid { init { require(radius > 0) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/serialization.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/serialization.kt index 25724fab..e24ccc30 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/serialization.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/serialization.kt @@ -105,14 +105,4 @@ internal object PrototypesSerializer : KSerializer { override fun serialize(encoder: Encoder, value: MutableVisionGroup) { mapSerializer.serialize(encoder, value.children) } -} - -@OptIn(DFExperimental::class) -public fun Vision.encodeToString(): String = SolidManager.jsonForSolids.encodeToString(Vision.serializer(), this) - -@OptIn(DFExperimental::class) -public fun Vision.Companion.decodeFromString(str: String): Vision = SolidManager.jsonForSolids.decodeFromString(serializer(), str).also { - if(it is VisionGroup){ - it.attachChildren() - } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt index 57b94441..c9abfce1 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt @@ -8,6 +8,7 @@ import hep.dataforge.vision.get import kotlin.test.Test import kotlin.test.assertEquals + /** * Create and attach new proxied group */ @@ -29,9 +30,9 @@ class SerializationTest { x = 100 z = -100 } - val string = cube.encodeToString() + val string = SolidManager.encodeToString(cube) println(string) - val newCube = Vision.decodeFromString(string) + val newCube = SolidManager.decodeFromString(string) assertEquals(cube.config, newCube.config) } @@ -50,9 +51,9 @@ class SerializationTest { } } } - val string = group.encodeToString() + val string = SolidManager.encodeToString(group) println(string) - val reconstructed = SolidGroup.decodeFromString(string) + val reconstructed = SolidManager.decodeFromString(string) as SolidGroup assertEquals(group["cube"]?.config, reconstructed["cube"]?.config) } diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SolidPluginTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SolidPluginTest.kt index 7bbb5f4e..1361a2c7 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SolidPluginTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SolidPluginTest.kt @@ -18,10 +18,10 @@ class SolidPluginTest { @DFExperimental @Test fun testPluginConverter(){ - val plugin = Global.plugins.fetch(SolidManager).visionManager - val meta = plugin.writeVisionToMeta(vision) + val visionManager = Global.plugins.fetch(SolidManager).visionManager + val meta = visionManager.encodeToMeta(vision) - val reconstructed = plugin.buildSpecificVision(meta) + val reconstructed = visionManager.decodeFromMeta(meta) as SolidGroup assertEquals(vision["aBox"],reconstructed["aBox"]) } diff --git a/visionforge-solid/src/jsMain/kotlin/hep/dataforge/vision/solid/three/ThreeVision.kt b/visionforge-solid/src/jsMain/kotlin/hep/dataforge/vision/solid/three/ThreeVision.kt index fe3d1039..a96e39e4 100644 --- a/visionforge-solid/src/jsMain/kotlin/hep/dataforge/vision/solid/three/ThreeVision.kt +++ b/visionforge-solid/src/jsMain/kotlin/hep/dataforge/vision/solid/three/ThreeVision.kt @@ -1,6 +1,6 @@ package hep.dataforge.vision.solid.three -import hep.dataforge.vision.solid.AbstractSolid +import hep.dataforge.vision.solid.BasicSolid import hep.dataforge.vision.solid.Solid import info.laht.threekt.core.Object3D import kotlinx.serialization.Serializable @@ -13,6 +13,6 @@ public interface ThreeVision : Solid { } @Serializable -public class CustomThreeVision(public val threeFactory: ThreeFactory) : AbstractSolid(), ThreeVision { +public class CustomThreeVision(public val threeFactory: ThreeFactory) : BasicSolid(), ThreeVision { override fun toObject3D(): Object3D = threeFactory(this) } \ No newline at end of file