v0.2.0-dev-22 #47

Merged
altavir merged 158 commits from dev into master 2021-07-17 11:04:22 +03:00
34 changed files with 111 additions and 327 deletions
Showing only changes of commit 8f6c3822ff - Show all commits

View File

@ -2,13 +2,16 @@
## [Unreleased]
### Added
- Server module
### Changed
### Deprecated
### Removed
- Primary modules dependencies on UI
### Fixed
- Version conflicts
### Security

View File

@ -19,7 +19,7 @@ private class MMDemoApp : Application {
private val connection = HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer(Json { serializersModule = SolidManager.serialModule })
serializer = KotlinxSerializer(Json { serializersModule = SolidManager.serializersModule })
}
}

View File

@ -1,9 +1,7 @@
package ru.mipt.npm.muon.monitor
import hep.dataforge.js.requireJS
actual fun readResource(path: String): String {
return requireJS(path) as String
return kotlinext.js.require(path) as String
}
// TODO replace by resource

View File

@ -1,6 +1,5 @@
package hep.dataforge.vision.bootstrap
import hep.dataforge.js.requireJS
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.solid.SolidGroup
import hep.dataforge.vision.solid.SolidManager
@ -27,7 +26,7 @@ private fun saveData(event: Event, fileName: String, mimeType: String = "text/pl
event.stopPropagation();
event.preventDefault();
val fileSaver = requireJS("file-saver")
val fileSaver = kotlinext.js.require("file-saver")
val blob = Blob(arrayOf(dataBuilder()), BlobPropertyBag("$mimeType;charset=utf-8"))
fileSaver.saveAs(blob, fileName)
}

View File

@ -10,7 +10,7 @@ import org.w3c.dom.Element
import react.RBuilder
import react.dom.render
fun RBuilder.visionPropertyEditor(
public fun RBuilder.visionPropertyEditor(
item: Vision,
descriptor: NodeDescriptor? = item.descriptor,
default: Meta? = null,
@ -36,7 +36,7 @@ fun RBuilder.visionPropertyEditor(
}
}
fun Element.visionPropertyEditor(
public fun Element.visionPropertyEditor(
item: Vision,
descriptor: NodeDescriptor? = item.descriptor,
default: Meta? = null

View File

@ -1,7 +1,7 @@
package ringui
import ringui.header.HeaderProps
import react.RClass
import ringui.header.HeaderProps
@JsModule("@jetbrains/ring-ui")
external object RingUI {

View File

@ -1,10 +1,10 @@
package ringui.header
import ringui.RingUI
import react.RBuilder
import react.RClass
import react.RHandler
import react.dom.WithClassName
import ringui.RingUI
@JsModule("@jetbrains/ring-ui/components/header/header")
internal external object HeaderModule {

View File

@ -1,9 +1,9 @@
package ringui.header
import ringui.IconProps
import kotlinx.html.A
import react.RElementBuilder
import react.RHandler
import ringui.IconProps
import styled.StyledDOMBuilder
external interface HeaderLogoProps : IconProps

View File

@ -1,9 +1,9 @@
package ringui.header
import ringui.ButtonProps
import react.RElementBuilder
import react.RHandler
import react.dom.WithClassName
import ringui.ButtonProps
// https://github.com/JetBrains/ring-ui/blob/master/components/header/tray.js
external interface HeaderTrayProps : WithClassName

View File

@ -7,7 +7,6 @@ val kotlinWrappersVersion: String by rootProject.extra
kotlin {
sourceSets {
commonMain {
dependencies {

View File

@ -1,23 +0,0 @@
package hep.dataforge.properties
import hep.dataforge.meta.*
import hep.dataforge.names.Name
@DFExperimental
internal class ConfigProperty(val config: Config, val name: Name) : Property<MetaItem<*>?> {
override var value: MetaItem<*>?
get() = config[name]
set(value) {
config[name] = value
}
override fun onChange(owner: Any?, callback: (MetaItem<*>?) -> Unit) {
config.onChange(owner) { name, oldItem, newItem ->
if (name == this.name && oldItem != newItem) callback(newItem)
}
}
override fun removeChangeListener(owner: Any?) {
config.removeListener(owner)
}
}

View File

@ -1,60 +0,0 @@
package hep.dataforge.properties
import hep.dataforge.meta.DFExperimental
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
//TODO move to core
@DFExperimental
interface Property<T> {
var value: T
fun onChange(owner: Any? = null, callback: (T) -> Unit)
fun removeChangeListener(owner: Any? = null)
}
@DFExperimental
@OptIn(ExperimentalCoroutinesApi::class)
fun <T> Property<T>.flow() = callbackFlow<T> {
send(value)
onChange(this) {
//TODO add exception handler?
launch {
send(it)
}
}
awaitClose { removeChangeListener(this) }
}
/**
* Reflect all changes in the [source] property onto this property
*
* @return a mirroring job
*/
@DFExperimental
fun <T> Property<T>.mirror(source: Property<T>, scope: CoroutineScope): Job {
return scope.launch {
source.flow().collect {
value = it
}
}
}
/**
* Bi-directional connection between properties
*/
@DFExperimental
fun <T> Property<T>.bind(other: Property<T>) {
onChange(other) {
other.value = it
}
other.onChange {
this.value = it
}
}

View File

@ -6,6 +6,7 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.ValueType
import hep.dataforge.vision.Vision.Companion.STYLE_KEY
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
internal data class PropertyListener(
@ -13,7 +14,8 @@ internal data class PropertyListener(
val action: (name: Name) -> Unit
)
abstract class AbstractVision : Vision {
@Serializable
public open class AbstractVision : Vision {
@Transient
override var parent: VisionGroup? = null
@ -21,7 +23,7 @@ abstract class AbstractVision : Vision {
/**
* Object own properties excluding styles and inheritance
*/
abstract override var properties: Config?
override var properties: Config? = null
protected set
protected fun updateStyles(names: List<String>) {

View File

@ -9,6 +9,7 @@ import hep.dataforge.values.asValue
import hep.dataforge.vision.Vision.Companion.TYPE
import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY
import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
/**

View File

@ -1,22 +1,23 @@
package hep.dataforge.js
import kotlin.browser.document
import kotlin.dom.hasClass
import kotlinx.browser.document
import kotlinx.dom.hasClass
external val module: Module
external interface Hot {
val data: dynamic
public external val module: Module
fun accept()
fun accept(dependency: String, callback: () -> Unit)
fun accept(dependencies: Array<String>, callback: (updated: Array<String>) -> Unit)
public external interface Hot {
public val data: dynamic
fun dispose(callback: (data: dynamic) -> Unit)
public fun accept()
public fun accept(dependency: String, callback: () -> Unit)
public fun accept(dependencies: Array<String>, callback: (updated: Array<String>) -> Unit)
public fun dispose(callback: (data: dynamic) -> Unit)
}
external interface Module {
val hot: Hot?
public external interface Module {
public val hot: Hot?
}
/**
@ -24,21 +25,21 @@ external interface Module {
*
* Base interface for applications supporting Hot Module Replacement (HMR).
*/
interface Application {
public interface Application {
/**
* Starting point for an application.
* @param state Initial state between Hot Module Replacement (HMR).
*/
fun start(state: Map<String, Any>)
public fun start(state: Map<String, Any>)
/**
* Ending point for an application.
* @return final state for Hot Module Replacement (HMR).
*/
fun dispose(): Map<String, Any> = emptyMap()
public fun dispose(): Map<String, Any> = emptyMap()
}
fun startApplication(builder: () -> Application) {
public fun startApplication(builder: () -> Application) {
fun start(state: dynamic): Application? {
return if (document.body?.hasClass("application") == true) {
val application = builder()

View File

@ -1,33 +0,0 @@
package hep.dataforge.js
import hep.dataforge.meta.DFExperimental
import hep.dataforge.properties.Property
import org.w3c.dom.HTMLInputElement
@DFExperimental
fun HTMLInputElement.bindValue(property: Property<String>) {
if (this.onchange != null) error("Input element already bound")
this.onchange = {
property.value = this.value
Unit
}
property.onChange(this) {
if (value != it) {
value = it
}
}
}
@DFExperimental
fun HTMLInputElement.bindChecked(property: Property<Boolean>) {
if (this.onchange != null) error("Input element already bound")
this.onchange = {
property.value = this.checked
Unit
}
property.onChange(this) {
if (checked != it) {
checked = it
}
}
}

View File

@ -1,20 +0,0 @@
package hep.dataforge.js
@JsName("require")
external fun requireJS(name: String): dynamic
inline fun <T : Any> jsObject(builder: T.() -> Unit): T {
val obj: T = js("({})") as T
return obj.apply {
builder()
}
}
inline fun js(builder: dynamic.() -> Unit): dynamic = jsObject(builder)
fun toPlainObjectStripNull(obj: Any) = js {
for (key in Object.keys(obj)) {
val value = obj.asDynamic()[key]
if (value != null) this[key] = value
}
}

View File

@ -1,38 +1,38 @@
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
//import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
plugins {
id("ru.mipt.npm.mpp")
application
// application
}
val ktorVersion: String by rootProject.extra
kscience {
application()
}
//kscience {
// application()
//}
kotlin {
afterEvaluate {
val jsBrowserDistribution by tasks.getting
jvm {
withJava()
compilations[MAIN_COMPILATION_NAME]?.apply {
tasks.getByName<ProcessResources>(processResourcesTaskName) {
dependsOn(jsBrowserDistribution)
afterEvaluate {
from(jsBrowserDistribution)
}
}
}
}
}
// afterEvaluate {
// val jsBrowserDistribution by tasks.getting
//
// jvm {
// withJava()
// compilations[MAIN_COMPILATION_NAME]?.apply {
// tasks.getByName<ProcessResources>(processResourcesTaskName) {
// dependsOn(jsBrowserDistribution)
// afterEvaluate {
// from(jsBrowserDistribution)
// }
// }
// }
//
// }
// }
sourceSets {
commonMain {
dependencies {
implementation(project(":visionforge-solid"))
implementation(project(":visionforge-core"))
}
}
jvmMain {
@ -50,9 +50,6 @@ kotlin {
}
}
application {
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
}
//distributions {
// main {

View File

@ -0,0 +1,17 @@
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
}

View File

@ -18,13 +18,7 @@ public class Box(
public val xSize: Float,
public val ySize: Float,
public val zSize: Float
) : AbstractVision(), GeometrySolid {
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
override var properties: Config? = null
) : AbstractSolid(), GeometrySolid {
//TODO add helper for color configuration
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {

View File

@ -22,19 +22,13 @@ public class Composite(
public val compositeType: CompositeType,
public val first: Solid,
public val second: Solid
) : AbstractVision(), Solid, VisionGroup {
) : AbstractSolid(), Solid, VisionGroup {
init {
first.parent = this
second.parent = this
}
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
override var properties: Config? = null
override val children: Map<NameToken, Vision>
get() = mapOf(NameToken("first") to first, NameToken("second") to second)

View File

@ -24,13 +24,7 @@ public class ConeSegment(
public var upperRadius: Float,
public var startAngle: Float = 0f,
public var angle: Float = PI2
) : AbstractVision(), GeometrySolid {
override var properties: Config? = null
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
) : AbstractSolid(), GeometrySolid {
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val segments = detail ?: 8

View File

@ -13,16 +13,7 @@ import kotlinx.serialization.UseSerializers
@Serializable
@SerialName("solid.convex")
public class Convex(public val points: List<Point3D>) : AbstractVision(), Solid {
override var properties: Config? = null
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
}
public class Convex(public val points: List<Point3D>) : AbstractSolid(), Solid
public inline fun VisionContainerBuilder<Solid>.convex(name: String = "", action: ConvexBuilder.() -> Unit = {}): Convex =
ConvexBuilder().apply(action).build().also { set(name, it) }

View File

@ -44,13 +44,7 @@ public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Flo
public class Extruded(
public var shape: List<Point2D> = ArrayList(),
public var layers: MutableList<Layer> = ArrayList()
) : AbstractVision(), GeometrySolid {
override var properties: Config? = null
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
) : AbstractSolid(), GeometrySolid {
public fun shape(block: Shape2DBuilder.() -> Unit) {
this.shape = Shape2DBuilder().apply(block).build()

View File

@ -2,13 +2,10 @@
package hep.dataforge.vision.solid
import hep.dataforge.meta.Config
import hep.dataforge.meta.number
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.vision.AbstractVision
import hep.dataforge.vision.MutableVisionGroup
import hep.dataforge.vision.VisionContainerBuilder
import hep.dataforge.vision.set
import kotlinx.serialization.SerialName
@ -17,12 +14,7 @@ import kotlinx.serialization.UseSerializers
@Serializable
@SerialName("solid.line")
public class PolyLine(public var points: List<Point3D>) : AbstractVision(), Solid {
override var properties: Config? = null
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
public class PolyLine(public var points: List<Point3D>) : AbstractSolid(), Solid {
//var lineType by string()
public var thickness: Number by number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY)
@ -33,5 +25,9 @@ public class PolyLine(public var points: List<Point3D>) : AbstractVision(), Soli
}
public fun VisionContainerBuilder<Solid>.polyline(vararg points: Point3D, name: String = "", action: PolyLine.() -> Unit = {}): PolyLine =
public fun VisionContainerBuilder<Solid>.polyline(
vararg points: Point3D,
name: String = "",
action: PolyLine.() -> Unit = {},
): PolyLine =
PolyLine(points.toList()).apply(action).also { set(name, it) }

View File

@ -1,5 +1,3 @@
@file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vision.solid
import hep.dataforge.meta.*
@ -9,10 +7,9 @@ import hep.dataforge.vision.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers
import kotlin.collections.set
public abstract class AbstractProxy : AbstractVision(), VisionGroup {
public abstract class AbstractProxy : AbstractSolid(), VisionGroup {
public abstract val prototype: Vision
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
@ -54,19 +51,13 @@ public abstract class AbstractProxy : AbstractVision(), VisionGroup {
@Serializable
@SerialName("solid.proxy")
public class Proxy private constructor(
public val templateName: Name
public val templateName: Name,
) : AbstractProxy(), Solid {
public constructor(parent: SolidGroup, templateName: Name) : this(templateName) {
this.parent = parent
}
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
override var properties: Config? = null
/**
* Recursively search for defined template in the parent
*/
@ -155,7 +146,7 @@ public val Vision.prototype: Vision
*/
public fun SolidGroup.ref(
templateName: Name,
name: String = ""
name: String = "",
): Proxy = Proxy(this, templateName).also { set(name, it) }
/**
@ -164,7 +155,7 @@ public fun SolidGroup.ref(
public fun SolidGroup.proxy(
name: String,
obj: Solid,
templateName: Name = name.toName()
templateName: Name = name.toName(),
): Proxy {
val existing = getPrototype(templateName)
if (existing == null) {

View File

@ -1,5 +1,3 @@
@file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vision.solid
import hep.dataforge.meta.*
@ -75,7 +73,7 @@ public interface Solid : Vision {
item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial.descriptor)
enum<RotationOrder>(ROTATION_ORDER_KEY,default = RotationOrder.XYZ)
enum(ROTATION_ORDER_KEY,default = RotationOrder.XYZ)
}
}

View File

@ -1,7 +1,3 @@
@file:UseSerializers(
Point3DSerializer::class
)
package hep.dataforge.vision.solid
import hep.dataforge.meta.Config

View File

@ -1,26 +1,17 @@
@file:UseSerializers(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
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
@Serializable
@SerialName("solid.label")
public class SolidLabel(public var text: String, public var fontSize: Double, public var fontFamily: String) : AbstractVision(), Solid {
override var properties: Config? = null
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
}
public class SolidLabel(
public var text: String,
public var fontSize: Double,
public var fontFamily: String,
) : AbstractSolid(), Solid
public fun VisionContainerBuilder<Solid>.label(
text: String,

View File

@ -85,8 +85,8 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag
override fun content(target: String): Map<Name, Any> = when (target) {
VisionForm.TYPE -> serialModule.extractFactories().associateBy { it.name }
VISION_SERIAL_MODULE_TARGET -> mapOf(tag.name.toName() to serialModule)
VisionForm.TYPE -> serializersModule.extractFactories().associateBy { it.name }
VISION_SERIAL_MODULE_TARGET -> mapOf(tag.name.toName() to serializersModule)
else -> super.content(target)
}
@ -95,7 +95,7 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
override val type: KClass<out SolidManager> = SolidManager::class
override fun invoke(meta: Meta, context: Context): SolidManager = SolidManager(meta)
public val serialModule: SerializersModule = SerializersModule {
public val serializersModule: SerializersModule = SerializersModule {
contextual(Point3DSerializer)
contextual(Point2DSerializer)
@ -132,7 +132,7 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
useArrayPolymorphism = false
encodeDefaults = false
ignoreUnknownKeys = true
serializersModule = this@Companion.serialModule
serializersModule = this@Companion.serializersModule
}
}
}

View File

@ -1,15 +1,9 @@
@file:UseSerializers(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
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
@ -21,23 +15,18 @@ public class Sphere(
public var phiStart: Float = 0f,
public var phi: Float = PI2,
public var thetaStart: Float = 0f,
public var theta: Float = PI.toFloat()
) : AbstractVision(), GeometrySolid {
override var properties: Config? = null
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
public var theta: Float = PI.toFloat(),
) : AbstractSolid(), GeometrySolid {
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
fun point3DfromSphCoord(r: Float, theta: Float, phi: Float): Point3D {
// This transformation matches three.js sphere implementation
val y = r * cos(theta)
val z = r * sin(theta) * sin(phi)
val x = - r * sin(theta) * cos(phi)
val x = -r * sin(theta) * cos(phi)
return Point3D(x, y, z)
}
val segments = this.detail ?: 8
require(segments >= 4) { "The detail for sphere must be >= 4" }
val phiStep = phi / segments
@ -66,7 +55,7 @@ public inline fun VisionContainerBuilder<Solid>.sphere(
phi: Number = 2 * PI,
theta: Number = PI,
name: String = "",
action: Sphere.() -> Unit = {}
action: Sphere.() -> Unit = {},
): Sphere = Sphere(
radius.toFloat(),
phi = phi.toFloat(),

View File

@ -1,15 +1,9 @@
@file:UseSerializers(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
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
@ -24,14 +18,8 @@ public class Tube(
public var height: Float,
public var innerRadius: Float = 0f,
public var startAngle: Float = 0f,
public var angle: Float = PI2
) : AbstractVision(), GeometrySolid {
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
override var properties: Config? = null
public var angle: Float = PI2,
) : AbstractSolid(), GeometrySolid {
init {
require(radius > 0)
@ -137,7 +125,7 @@ public inline fun VisionContainerBuilder<Solid>.tube(
startAngle: Number = 0f,
angle: Number = 2 * PI,
name: String = "",
block: Tube.() -> Unit = {}
block: Tube.() -> Unit = {},
): Tube = Tube(
r.toFloat(),
height.toFloat(),

View File

@ -1,11 +1,12 @@
package hep.dataforge.vision.solid.three
import hep.dataforge.js.jsObject
import hep.dataforge.vision.solid.SolidLabel
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
import info.laht.threekt.core.Object3D
import info.laht.threekt.geometries.TextBufferGeometry
import info.laht.threekt.objects.Mesh
import kotlinext.js.jsObject
import kotlin.reflect.KClass
/**

View File

@ -1,32 +1,18 @@
@file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vision.solid.three
import hep.dataforge.meta.Config
import hep.dataforge.vision.AbstractVision
import hep.dataforge.vision.solid.Point3D
import hep.dataforge.vision.solid.Point3DSerializer
import hep.dataforge.vision.solid.AbstractSolid
import hep.dataforge.vision.solid.Solid
import info.laht.threekt.core.Object3D
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
/**
* A custom visual object that has its own Three.js renderer
*/
interface ThreeVision : Solid {
fun toObject3D(): Object3D
public interface ThreeVision : Solid {
public fun toObject3D(): Object3D
}
@Serializable
class CustomThreeVision(val threeFactory: ThreeFactory<Solid>) : AbstractVision(),
ThreeVision {
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
override var properties: Config? = null
public class CustomThreeVision(public val threeFactory: ThreeFactory<Solid>) : AbstractSolid(), ThreeVision {
override fun toObject3D(): Object3D = threeFactory(this)
}