Added satelite detector demo

This commit is contained in:
Alexander Nozik 2020-11-07 17:32:18 +03:00
parent 6b4bc6912f
commit 5b8d298ac6
40 changed files with 474 additions and 302 deletions

View File

@ -0,0 +1,24 @@
plugins {
id("ru.mipt.npm.mpp")
}
val kvisionVersion: String = "3.16.2"
kscience{
application()
}
kotlin {
sourceSets {
commonMain {
dependencies {
implementation(project(":visionforge-solid"))
}
}
jsMain {
dependencies {
}
}
}
}

View File

@ -0,0 +1,55 @@
package ru.mipt.npm.sat
import hep.dataforge.vision.solid.*
import kotlin.math.PI
internal fun visionOfSatellite(
layers: Int = 10,
layerHeight: Number = 10,
xSegments: Int = 3,
ySegments: Int = xSegments,
xSegmentSize: Number = 30,
ySegmentSize: Number = xSegmentSize,
fiberDiameter: Number = 1.0,
): SolidGroup = SolidGroup {
opacity = 0.3
val totalXSize = xSegments * xSegmentSize.toDouble()
val totalYSize = ySegments * ySegmentSize.toDouble()
for (layer in 1..layers) {
group("layer[$layer]") {
for (i in 1..xSegments) {
for (j in 1..ySegments) {
box(xSegmentSize, ySegmentSize, layerHeight, name = "segment[$i,$j]") {
z = (layer - 0.5) * layerHeight.toDouble()
x = (i - 0.5) * xSegmentSize.toDouble()
y = (j - 0.5) * ySegmentSize.toDouble()
}
}
}
group("fibers") {
for (i in 1..xSegments) {
cylinder(fiberDiameter, totalYSize) {
rotationX = PI / 2
z = (layer - 1.0) * layerHeight.toDouble() + fiberDiameter.toDouble()
x = (i - 0.5) * xSegmentSize.toDouble()
y = totalYSize/2
color("red")
}
}
for (j in 1..ySegments) {
cylinder(fiberDiameter, totalXSize) {
rotationY = PI / 2
z = (layer) * layerHeight.toDouble() - fiberDiameter.toDouble()
y = (j - 0.5) * xSegmentSize.toDouble()
x = totalXSize/2
color("blue")
}
}
}
}
}
}

View File

@ -0,0 +1,34 @@
package ru.mipt.npm.sat
import hep.dataforge.context.Global
import hep.dataforge.js.Application
import hep.dataforge.js.startApplication
import hep.dataforge.vision.solid.three.ThreePlugin
import hep.dataforge.vision.solid.three.render
import kotlinx.browser.document
import hep.dataforge.meta.invoke
import org.w3c.dom.HTMLElement
private class SatDemoApp : Application {
override fun start(state: Map<String, Any>) {
val element = document.getElementById("canvas") as? HTMLElement
?: error("Element with id 'canvas' not found on page")
val three = Global.plugins.fetch(ThreePlugin)
val sat = visionOfSatellite(
ySegments = 5,
)
three.render(element, sat){
minSize = 500
axes {
size = 500.0
visible = true
}
}
}
}
fun main() {
startApplication(::SatDemoApp)
}

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Three js demo for particle physics</title>
<script type="text/javascript" src="sat-demo.js"></script>
</head>
<body class="application">
<div id="canvas"></div>
</body>
</html>

View File

@ -6,6 +6,7 @@ plugins {
} }
kscience { kscience {
useCoroutines()
val fxVersion: String by rootProject.extra val fxVersion: String by rootProject.extra
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION) useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
application() application()

View File

@ -2,7 +2,14 @@ package hep.dataforge.vision.solid.demo
import hep.dataforge.js.Application import hep.dataforge.js.Application
import hep.dataforge.js.startApplication import hep.dataforge.js.startApplication
import kotlin.browser.document import hep.dataforge.vision.solid.x
import hep.dataforge.vision.solid.y
import kotlinx.browser.document
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.random.Random
private class ThreeDemoApp : Application { private class ThreeDemoApp : Application {
@ -13,27 +20,25 @@ private class ThreeDemoApp : Application {
ThreeDemoGrid(element).run { ThreeDemoGrid(element).run {
showcase() showcase()
showcaseCSG() showcaseCSG()
// demo("dynamicBox", "Dancing boxes") { demo("dynamicBox", "Dancing boxes") {
// val boxes = (-10..10).flatMap { i -> val boxes = (-10..10).flatMap { i ->
// (-10..10).map { j -> (-10..10).map { j ->
// varBox(10, 10, 0, name = "cell_${i}_${j}") { varBox(10, 10, 0, name = "cell_${i}_${j}") {
// x = i * 10 x = i * 10
// y = j * 10 y = j * 10
// value = 128 value = 128
// setProperty(EDGES_ENABLED_KEY, false) }
// setProperty(WIREFRAME_ENABLED_KEY, false) }
// } }
// } GlobalScope.launch {
// } while (isActive) {
// GlobalScope.launch { delay(500)
// while (isActive) { boxes.forEach { box ->
// delay(500) box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(1..255)
// boxes.forEach { box -> }
// box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(0..255) }
// } }
// } }
// }
// }
} }

View File

@ -11,6 +11,8 @@ import hep.dataforge.vision.Vision
import hep.dataforge.vision.solid.three.ThreeCanvas import hep.dataforge.vision.solid.three.ThreeCanvas
import hep.dataforge.vision.solid.three.ThreePlugin import hep.dataforge.vision.solid.three.ThreePlugin
import hep.dataforge.vision.solid.three.output import hep.dataforge.vision.solid.three.output
import kotlinx.browser.document
import kotlinx.dom.clear
import kotlinx.html.dom.append import kotlinx.html.dom.append
import kotlinx.html.dom.create import kotlinx.html.dom.create
import kotlinx.html.h2 import kotlinx.html.h2
@ -19,8 +21,6 @@ import kotlinx.html.id
import kotlinx.html.js.div import kotlinx.html.js.div
import kotlinx.html.span import kotlinx.html.span
import org.w3c.dom.Element import org.w3c.dom.Element
import kotlin.browser.document
import kotlin.dom.clear
import kotlin.reflect.KClass import kotlin.reflect.KClass
class ThreeDemoGrid(element: Element, meta: Meta = Meta.EMPTY) : OutputManager { class ThreeDemoGrid(element: Element, meta: Meta = Meta.EMPTY) : OutputManager {

View File

@ -4,6 +4,7 @@ package hep.dataforge.vision.solid.demo
import hep.dataforge.meta.int import hep.dataforge.meta.int
import hep.dataforge.meta.number import hep.dataforge.meta.number
import hep.dataforge.meta.set
import hep.dataforge.meta.setItem import hep.dataforge.meta.setItem
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
@ -12,7 +13,6 @@ import hep.dataforge.vision.getProperty
import hep.dataforge.vision.set import hep.dataforge.vision.set
import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.*
import hep.dataforge.vision.solid.Solid.Companion.GEOMETRY_KEY import hep.dataforge.vision.solid.Solid.Companion.GEOMETRY_KEY
import hep.dataforge.vision.solid.demo.VariableBoxThreeFactory.Z_SIZE_KEY
import hep.dataforge.vision.solid.three.* import hep.dataforge.vision.solid.three.*
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
@ -21,86 +21,89 @@ import info.laht.threekt.geometries.BoxBufferGeometry
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlin.math.max import kotlin.math.max
import kotlin.reflect.KClass
internal var Solid.variableZSize: Number internal fun SolidGroup.varBox(
get() = getProperty(Z_SIZE_KEY, false).number ?: 0f
set(value) {
setItem(Z_SIZE_KEY, value.asValue())
}
internal var Solid.value: Int
get() = getProperty("value", false).int ?: 0
set(value) {
setItem("value", value.asValue())
val size = value.toFloat() / 255f * 20f
scaleZ = size
z = -size / 2
val b = max(0, 255 - value)
val r = max(0, value - 255)
val g = 255 - b - r
color(r.toUByte(), g.toUByte(), b.toUByte())
}
fun SolidGroup.varBox(
xSize: Number, xSize: Number,
ySize: Number, ySize: Number,
zSize: Number, zSize: Number,
name: String = "", name: String = "",
action: Solid.() -> Unit = {} action: VariableBox.() -> Unit = {},
) = CustomThreeVision(VariableBoxThreeFactory).apply { ): VariableBox = VariableBox(xSize, ySize, zSize).apply(action).also { set(name, it) }
scaleX = xSize
scaleY = ySize
scaleZ = zSize
}.apply(action).also { set(name, it) }
private object VariableBoxThreeFactory : ThreeFactory<Solid> { internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeVision() {
val X_SIZE_KEY = GEOMETRY_KEY + "xSize" init {
val Y_SIZE_KEY = GEOMETRY_KEY + "ySize" scaleX = xSize
val Z_SIZE_KEY = GEOMETRY_KEY + "zSize" scaleY = ySize
scaleZ = zSize
config[MeshThreeFactory.EDGES_ENABLED_KEY] = false
config[MeshThreeFactory.WIREFRAME_ENABLED_KEY] = false
}
override val type: KClass<in Solid> get() = Solid::class override fun render(): Object3D {
val xSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
override fun invoke(obj: Solid): Object3D { val ySize = getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
val xSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0 val zSize = getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
val ySize = obj.getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
val zSize = obj.getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
val geometry = BoxBufferGeometry(1, 1, 1) val geometry = BoxBufferGeometry(1, 1, 1)
//JS sometimes tries to pass Geometry as BufferGeometry //JS sometimes tries to pass Geometry as BufferGeometry
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
val mesh = Mesh(geometry, getMaterial(obj,true)).apply { val mesh = Mesh(geometry, getMaterial(this@VariableBox, true)).apply {
applyEdges(obj) applyEdges(this@VariableBox)
applyWireFrame(obj) applyWireFrame(this@VariableBox)
//set position for mesh //set position for mesh
updatePosition(obj) updatePosition(this@VariableBox)
layers.enable(obj.layer) layers.enable(this@VariableBox.layer)
children.forEach { children.forEach {
it.layers.enable(obj.layer) it.layers.enable(this@VariableBox.layer)
} }
} }
mesh.scale.set(xSize, ySize, zSize) mesh.scale.set(xSize, ySize, zSize)
//add listener to object properties //add listener to object properties
obj.onPropertyChange(this) { name -> onPropertyChange(mesh) { name ->
when { when {
// name.startsWith(GEOMETRY_KEY) -> { name.startsWith(GEOMETRY_KEY) -> {
// val newXSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0 val newXSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
// val newYSize = obj.getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0 val newYSize = getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
// val newZSize = obj.getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0 val newZSize = getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
// mesh.scale.set(newXSize, newYSize, newZSize) mesh.scale.set(newXSize, newYSize, newZSize)
// mesh.updateMatrix() mesh.updateMatrix()
// } }
name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(obj) name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(this@VariableBox)
name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(obj) name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox)
else -> mesh.updateProperty(obj, name) else -> mesh.updateProperty(this@VariableBox, name)
} }
} }
return mesh return mesh
} }
var variableZSize: Number
get() = getProperty(Z_SIZE_KEY, false).number ?: 0f
set(value) {
setItem(Z_SIZE_KEY, value.asValue())
}
var value: Int
get() = getProperty("value", false).int ?: 0
set(value) {
setItem("value", value.asValue())
val size = value.toFloat() / 255f * 20f
scaleZ = size
z = size / 2
val b = max(0, 128 - value)
val r = max(0, value - 128)
val g = 255 - b - r
color(r.toUByte(), g.toUByte(), b.toUByte())
}
companion object{
private val X_SIZE_KEY = GEOMETRY_KEY + "xSize"
private val Y_SIZE_KEY = GEOMETRY_KEY + "ySize"
private val Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
}
} }

View File

@ -43,5 +43,6 @@ include(
":demo:spatial-showcase", ":demo:spatial-showcase",
":demo:gdml", ":demo:gdml",
":demo:muon-monitor", ":demo:muon-monitor",
":demo:sat-demo",
":playground" ":playground"
) )

View File

@ -1,6 +1,5 @@
package hep.dataforge.vision.bootstrap package hep.dataforge.vision.bootstrap
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.solid.SolidGroup import hep.dataforge.vision.solid.SolidGroup
import hep.dataforge.vision.solid.SolidManager import hep.dataforge.vision.solid.SolidManager
import hep.dataforge.vision.solid.three.ThreeCanvas import hep.dataforge.vision.solid.three.ThreeCanvas
@ -15,8 +14,7 @@ import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
import org.w3c.files.Blob import org.w3c.files.Blob
import org.w3c.files.BlobPropertyBag import org.w3c.files.BlobPropertyBag
import react.RBuilder import react.*
import react.ReactElement
import react.dom.button import react.dom.button
import react.dom.div import react.dom.div
import react.dom.input import react.dom.input
@ -31,37 +29,51 @@ private fun saveData(event: Event, fileName: String, mimeType: String = "text/pl
fileSaver.saveAs(blob, fileName) fileSaver.saveAs(blob, fileName)
} }
@OptIn(DFExperimental::class) public fun RBuilder.canvasControls(canvas: ThreeCanvas): ReactElement {
public fun RBuilder.canvasControls(canvas: ThreeCanvas): ReactElement = accordion("controls") { return child(CanvasControls){
entry("Settings") { attrs{
div("row") { this.canvas = canvas
div("col-2") { }
label("checkbox-inline") { }
input(type = InputType.checkBox) { }
attrs {
defaultChecked = canvas.axes.visible public external interface CanvasControlsProps : RProps {
onChangeFunction = { public var canvas: ThreeCanvas
canvas.axes.visible = (it.target as HTMLInputElement).checked }
public val CanvasControls: FunctionalComponent<CanvasControlsProps> = functionalComponent ("CanvasControls") { props ->
val visionManager = useMemo(
{ props.canvas.context.plugins.fetch(SolidManager).visionManager },
arrayOf(props.canvas)
)
accordion("controls") {
entry("Settings") {
div("row") {
div("col-2") {
label("checkbox-inline") {
input(type = InputType.checkBox) {
attrs {
defaultChecked = props.canvas.axes.visible
onChangeFunction = {
props.canvas.axes.visible = (it.target as HTMLInputElement).checked
}
} }
} }
+"Axes"
} }
+"Axes"
} }
} div("col-1") {
div("col-1") { button {
button { +"Export"
+"Export" attrs {
attrs { onClickFunction = {
onClickFunction = { val json = (props.canvas.content as? SolidGroup)?.let { group ->
val json = (canvas.content as? SolidGroup)?.let { group -> visionManager.encodeToString(group)
SolidManager.jsonForSolids.encodeToString( }
SolidGroup.serializer(), if (json != null) {
group saveData(it, "object.json", "text/json") {
) json
} }
if (json != null) {
saveData(it, "object.json", "text/json") {
json
} }
} }
} }
@ -69,22 +81,22 @@ public fun RBuilder.canvasControls(canvas: ThreeCanvas): ReactElement = accordio
} }
} }
} }
} entry("Layers") {
entry("Layers") { div("row") {
div("row") { (0..11).forEach { layer ->
(0..11).forEach { layer -> div("col-1") {
div("col-1") { label { +layer.toString() }
label { +layer.toString() } input(type = InputType.checkBox) {
input(type = InputType.checkBox) { attrs {
attrs { if (layer == 0) {
if (layer == 0) { defaultChecked = true
defaultChecked = true }
} onChangeFunction = {
onChangeFunction = { if ((it.target as HTMLInputElement).checked) {
if ((it.target as HTMLInputElement).checked) { props.canvas.camera.layers.enable(layer)
canvas.camera.layers.enable(layer) } else {
} else { props.canvas.camera.layers.disable(layer)
canvas.camera.layers.disable(layer) }
} }
} }
} }
@ -118,10 +130,8 @@ public fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer
+"Export" +"Export"
onClickFunction = { onClickFunction = {
val json = (canvas.content as? SolidGroup)?.let { group -> val json = (canvas.content as? SolidGroup)?.let { group ->
SolidManager.jsonForSolids.encodeToString( val visionManager = canvas.context.plugins.fetch(SolidManager).visionManager
SolidGroup.serializer(), visionManager.encodeToString(group)
group
)
} }
if (json != null) { if (json != null) {
saveData(it, "object.json", "text/json") { saveData(it, "object.json", "text/json") {

View File

@ -1,5 +1,8 @@
package hep.dataforge.vision package hep.dataforge.vision
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.node
import hep.dataforge.names.* import hep.dataforge.names.*
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
@ -7,7 +10,7 @@ import kotlinx.serialization.Transient
/** /**
* Abstract implementation of mutable group of [Vision] * Abstract implementation of mutable group of [Vision]
*/ */
abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup { public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
//protected abstract val _children: MutableMap<NameToken, T> //protected abstract val _children: MutableMap<NameToken, T>
@ -22,30 +25,31 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
/** /**
* Update or create stylesheet * Update or create stylesheet
*/ */
fun styleSheet(block: StyleSheet.() -> Unit) { public fun styleSheet(block: StyleSheet.() -> Unit) {
val res = styleSheet ?: StyleSheet(this).also { styleSheet = it } if (styleSheet == null) {
res.block() styleSheet = StyleSheet(this@AbstractVisionGroup)
}
styleSheet!!.block()
} }
override fun propertyChanged(name: Name) { override fun propertyChanged(name: Name) {
super.propertyChanged(name) super.propertyChanged(name)
for(obj in this) { for (obj in this) {
obj.propertyChanged(name) obj.propertyChanged(name)
} }
} }
// TODO Consider renaming to `StructureChangeListener` (singular) private data class StructureChangeListener(val owner: Any?, val callback: (NameToken, Vision?) -> Unit)
private data class StructureChangeListeners(val owner: Any?, val callback: (Name, Vision?) -> Unit)
@Transient @Transient
private val structureChangeListeners = HashSet<StructureChangeListeners>() private val structureChangeListeners = HashSet<StructureChangeListener>()
/** /**
* Add listener for children change * Add listener for children change
*/ */
override fun onChildrenChange(owner: Any?, action: (Name, Vision?) -> Unit) { override fun onChildrenChange(owner: Any?, action: (NameToken, Vision?) -> Unit) {
structureChangeListeners.add( structureChangeListeners.add(
StructureChangeListeners( StructureChangeListener(
owner, owner,
action action
) )
@ -62,7 +66,7 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
/** /**
* Propagate children change event upwards * Propagate children change event upwards
*/ */
protected fun childrenChanged(name: Name, child: Vision?) { protected fun childrenChanged(name: NameToken, child: Vision?) {
structureChangeListeners.forEach { it.callback(name, child) } structureChangeListeners.forEach { it.callback(name, child) }
} }
@ -77,10 +81,11 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
protected abstract fun setChild(token: NameToken, child: Vision) protected abstract fun setChild(token: NameToken, child: Vision)
/** /**
* Add a static child. Statics could not be found by name, removed or replaced * Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events.
*/ */
protected open fun addStatic(child: Vision) = protected open fun addStatic(child: Vision): Unit {
set(NameToken("@static(${child.hashCode()})").asName(), child) setChild(NameToken("@static", index = child.hashCode().toString()), child)
}
protected abstract fun createGroup(): AbstractVisionGroup protected abstract fun createGroup(): AbstractVisionGroup
@ -124,6 +129,7 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
when { when {
name.isEmpty() -> { name.isEmpty() -> {
if (child != null) { if (child != null) {
attach(child)
addStatic(child) addStatic(child)
} }
} }
@ -135,6 +141,7 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
attach(child) attach(child)
setChild(token, child) setChild(token, child)
} }
childrenChanged(token, child)
} }
else -> { else -> {
//TODO add safety check //TODO add safety check
@ -142,7 +149,13 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
parent[name.tokens.last().asName()] = child parent[name.tokens.last().asName()] = child
} }
} }
childrenChanged(name, child)
} }
override fun update(meta: Meta) {
val styleMeta = meta.get("styleSheet").node
if (styleMeta != null) {
this.styleSheet?.update(styleMeta)
}
super.update(meta)
}
} }

View File

@ -235,12 +235,12 @@ public object Colors {
} }
} }
/** // /**
* Convert three bytes representing color to Meta // * Convert three bytes representing color to Meta
*/ // */
fun rgbToMeta(r: UByte, g: UByte, b: UByte): Meta = Meta { // fun rgbToMeta(r: UByte, g: UByte, b: UByte): Meta = Meta {
RED_KEY put r.toInt() // RED_KEY put r.toInt()
GREEN_KEY put g.toInt() // GREEN_KEY put g.toInt()
BLUE_KEY put b.toInt() // BLUE_KEY put b.toInt()
} // }
} }

View File

@ -8,13 +8,10 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
@SerialName("group") @SerialName("group")
class SimpleVisionGroup : AbstractVisionGroup() { public class SimpleVisionGroup : AbstractVisionGroup() {
override var styleSheet: StyleSheet? = null override var styleSheet: StyleSheet? = null
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
override var properties: Config? = null
@SerialName("children") @SerialName("children")
private val _children = HashMap<NameToken, Vision>() private val _children = HashMap<NameToken, Vision>()
override val children: Map<NameToken, Vision> get() = _children override val children: Map<NameToken, Vision> get() = _children

View File

@ -16,11 +16,11 @@ import kotlinx.serialization.encoding.Encoder
* A container for styles * A container for styles
*/ */
@Serializable @Serializable
public class StyleSheet private constructor(private val styleMap: MutableMap<String, Meta> = LinkedHashMap()) { public class StyleSheet private constructor(private val styleMap: MutableMap<String, Meta>) {
@Transient @Transient
internal var owner: Vision? = null internal var owner: Vision? = null
public constructor(owner: Vision) : this() { public constructor(owner: Vision) : this(LinkedHashMap()) {
this.owner = owner this.owner = owner
} }
@ -91,6 +91,10 @@ public class StyleSheet private constructor(private val styleMap: MutableMap<Str
} }
} }
public fun StyleSheet.update(styleMeta: Meta){
TODO()
}
/** /**
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment. * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.

View File

@ -69,7 +69,7 @@ public interface Vision : Configurable {
} }
/** /**
* Update this vision using external meta. * Update this vision using external meta. Children are not updated.
*/ */
public fun update(meta: Meta) public fun update(meta: Meta)

View File

@ -0,0 +1,50 @@
package hep.dataforge.vision
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.set
import hep.dataforge.names.NameToken
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
//public class VisionChange(public val properties: Meta, public val childrenChange: Map<NameToken, VisionChange>)
public class VisionChangeCollector(
public val manager: VisionManager,
public val scope: CoroutineScope,
public val vision: Vision,
public val lock: Mutex = Mutex()
) {
private val collector: Config = Config()
private val childrenCollectors = HashMap<NameToken, VisionChangeCollector>()
init {
vision.onPropertyChange(this) { propertyName ->
collector[propertyName] = vision.properties?.get(propertyName)
}
if (vision is VisionGroup) {
vision.children.forEach { (token, child) ->
childrenCollectors[token] = VisionChangeCollector(manager, scope, child, lock)
}
}
if (vision is MutableVisionGroup) {
TODO("Tread vision structure change")
// vision.onChildrenChange(this) { childName, child ->
// if(child == null){
// childrenCollectors[childName] = null
// } else {
// childrenCollectors[childName] = manager.encodeToMeta(child)
// }
// }
}
}
}
//
//fun collectUpdates(manager: VisionManager, scope: CoroutineScope, vision: Vision): Flow<Meta> {
//
//
// vision.
//}

View File

@ -87,7 +87,7 @@ public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision
* @param owner the handler to properly remove listeners * @param owner the handler to properly remove listeners
* @param action First argument of the action is the name of changed child. Second argument is the new value of the object. * @param action First argument of the action is the name of changed child. Second argument is the new value of the object.
*/ */
public fun onChildrenChange(owner: Any?, action: (Name, Vision?) -> Unit) public fun onChildrenChange(owner: Any?, action: (NameToken, Vision?) -> Unit)
/** /**
* Remove children change listener * Remove children change listener

View File

@ -3,6 +3,10 @@ package hep.dataforge.vision
import hep.dataforge.context.* import hep.dataforge.context.*
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.asName
import hep.dataforge.values.Null
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
@ -10,7 +14,6 @@ import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass import kotlinx.serialization.modules.subclass
import kotlin.reflect.KClass import kotlin.reflect.KClass
@DFExperimental
public class VisionManager(meta: Meta) : AbstractPlugin(meta) { public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
@ -50,36 +53,30 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
?: error("Expected node, but value found. Check your serializer!") ?: error("Expected node, but value found. Check your serializer!")
public fun updateVision(vision: Vision, meta: Meta) { public fun updateVision(vision: Vision, meta: Meta) {
vision.update(meta)
if (vision is MutableVisionGroup) {
val children by meta.node()
children?.items?.forEach { (token, item) ->
when {
item.value == Null -> vision[token] = null //Null means removal
item.node != null -> {
val node = item.node!!
val type by node.string()
if (type != null) {
//If the type is present considering it as new node, not an update
vision[token.asName()] = decodeFromMeta(node)
} else {
val existing = vision.children[token]
if (existing != null) {
updateVision(existing, node)
}
}
}
}
}
}
} }
// public fun <T : Vision> VisionForm<T>.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 <reified T : Vision> buildSpecificVision(meta: Meta): T {
// val factory = resolveVisionForm(T::class)
// return factory.buildVision(meta)
// }
//
// public fun <T : Vision> 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<VisionManager> { public companion object : PluginFactory<VisionManager> {
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
override val type: KClass<out VisionManager> = VisionManager::class override val type: KClass<out VisionManager> = VisionManager::class

View File

@ -57,7 +57,7 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder {
override var scale: Point3D? = null override var scale: Point3D? = null
@SerialName("children") @SerialName("children")
private val _children = HashMap<NameToken, Vision>() private val _children = LinkedHashMap<NameToken, Vision>()
override val children: Map<NameToken, Vision> get() = _children override val children: Map<NameToken, Vision> get() = _children
override fun attachChildren() { override fun attachChildren() {
@ -122,7 +122,7 @@ internal class Prototypes(
override fun removeChild(token: NameToken) { override fun removeChild(token: NameToken) {
children.remove(token) children.remove(token)
childrenChanged(token.asName(), null) childrenChanged(token, null)
} }
override fun setChild(token: NameToken, child: Vision) { override fun setChild(token: NameToken, child: Vision) {

View File

@ -16,43 +16,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.* import kotlinx.serialization.modules.*
import kotlin.reflect.KClass import kotlin.reflect.KClass
//@OptIn(ExperimentalSerializationApi::class)
//@DFExperimental
//private fun SerializersModule.extractFactories(): List<SolidForm<*>> {
// val list = ArrayList<SolidForm<*>>()
//
// val collector = object : SerializersModuleCollector {
// override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>) {
// //Do nothing
// }
//
// override fun <Base : Any, Sub : Base> polymorphic(
// baseClass: KClass<Base>,
// actualClass: KClass<Sub>,
// actualSerializer: KSerializer<Sub>,
// ) {
// if (baseClass == Vision::class) {
// @Suppress("UNCHECKED_CAST") val factory: SolidForm<Solid> = SolidForm(
// actualClass as KClass<Solid>,
// actualSerializer as KSerializer<Solid>
// )
// list.add(factory)
// }
// }
//
// override fun <Base : Any> polymorphicDefault(
// baseClass: KClass<Base>,
// defaultSerializerProvider: (className: String?) -> DeserializationStrategy<out Base>?,
// ) {
// TODO("Not yet implemented")
// }
//
// }
// dumpTo(collector)
// return list
//}
@DFExperimental
public class SolidManager(meta: Meta) : AbstractPlugin(meta) { public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
public val visionManager: VisionManager by require(VisionManager) public val visionManager: VisionManager by require(VisionManager)
@ -96,18 +60,16 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
} }
} }
public val jsonForSolids = Json{ internal val jsonForSolids: Json = Json{
prettyPrint = true prettyPrint = true
useArrayPolymorphism = false useArrayPolymorphism = false
encodeDefaults = false encodeDefaults = false
ignoreUnknownKeys = true ignoreUnknownKeys = true
serializersModule = SolidManager.serializersModuleForSolids serializersModule = serializersModuleForSolids
} }
@OptIn(DFExperimental::class)
public fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(Vision.serializer(), solid) 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 { public fun decodeFromString(str: String): Vision = jsonForSolids.decodeFromString(Vision.serializer(), str).also {
if(it is VisionGroup){ if(it is VisionGroup){
it.attachChildren() it.attachChildren()

View File

@ -91,7 +91,7 @@ public fun Solid.color(rgb: Int) {
public fun Solid.color(r: UByte, g: UByte, b: UByte): Unit = setItem( public fun Solid.color(r: UByte, g: UByte, b: UByte): Unit = setItem(
MATERIAL_COLOR_KEY, MATERIAL_COLOR_KEY,
Colors.rgbToMeta(r, g, b) Colors.rgbToString(r, g, b).asValue()
) )
/** /**

View File

@ -1,15 +1,20 @@
package hep.dataforge.vision.solid.specifications package hep.dataforge.vision.solid.specifications
import hep.dataforge.meta.Scheme import hep.dataforge.meta.*
import hep.dataforge.meta.SchemeSpec
import hep.dataforge.meta.int
import hep.dataforge.meta.spec
public class Canvas3DOptions : Scheme() { public class Canvas3DOptions : Scheme() {
public var axes: Axes by spec(Axes, Axes.empty()) public var axes: Axes by spec(Axes, Axes.empty())
public var camera: Camera by spec(Camera, Camera.empty()) public var camera: Camera by spec(Camera, Camera.empty())
public var controls: Controls by spec(Controls, Controls.empty()) public var controls: Controls by spec(Controls, Controls.empty())
public var minSize: Int by int(300) public var minSize: Int by int(300)
public var minWith: Number by number { minSize }
public var minHeight: Number by number { minSize }
public var maxSize: Int by int(Int.MAX_VALUE)
public var maxWith: Number by number { maxSize }
public var maxHeight: Number by number { maxSize }
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions)
} }

View File

@ -4,6 +4,6 @@ import hep.dataforge.meta.Scheme
import hep.dataforge.meta.SchemeSpec import hep.dataforge.meta.SchemeSpec
class Controls : Scheme() { public class Controls : Scheme() {
companion object : SchemeSpec<Controls>(::Controls) public companion object : SchemeSpec<Controls>(::Controls)
} }

View File

@ -35,7 +35,7 @@ internal fun mergeChild(parent: VisionGroup, child: Vision): Vision {
} }
@DFExperimental @DFExperimental
object RemoveSingleChild : VisualTreeTransform<SolidGroup>() { internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
override fun SolidGroup.transformInPlace() { override fun SolidGroup.transformInPlace() {
fun MutableVisionGroup.replaceChildren() { fun MutableVisionGroup.replaceChildren() {

View File

@ -9,7 +9,7 @@ import hep.dataforge.vision.solid.Proxy
import hep.dataforge.vision.solid.SolidGroup import hep.dataforge.vision.solid.SolidGroup
@DFExperimental @DFExperimental
object UnRef : VisualTreeTransform<SolidGroup>() { internal object UnRef : VisualTreeTransform<SolidGroup>() {
private fun VisionGroup.countRefs(): Map<Name, Int> { private fun VisionGroup.countRefs(): Map<Name, Int> {
return children.values.fold(HashMap()) { reducer, obj -> return children.values.fold(HashMap()) { reducer, obj ->
if (obj is VisionGroup) { if (obj is VisionGroup) {

View File

@ -5,11 +5,11 @@ import hep.dataforge.vision.Vision
/** /**
* A root class for [Vision] tree optimization * A root class for [Vision] tree optimization
*/ */
abstract class VisualTreeTransform<T : Vision> { public abstract class VisualTreeTransform<T : Vision> {
protected abstract fun T.transformInPlace() protected abstract fun T.transformInPlace()
protected abstract fun T.clone(): T protected abstract fun T.clone(): T
operator fun invoke(source: T, inPlace: Boolean = true): T { public operator fun invoke(source: T, inPlace: Boolean = true): T {
val newSource = if (inPlace) { val newSource = if (inPlace) {
source source
} else { } else {
@ -21,7 +21,7 @@ abstract class VisualTreeTransform<T : Vision> {
} }
} }
fun <T : Vision> T.transform(vararg transform: VisualTreeTransform<T>): T { public fun <T : Vision> T.transform(vararg transform: VisualTreeTransform<T>): T {
var res = this var res = this
transform.forEach { transform.forEach {
res = it(res) res = it(res)
@ -29,6 +29,6 @@ fun <T : Vision> T.transform(vararg transform: VisualTreeTransform<T>): T {
return res return res
} }
fun <T : Vision> T.transformInPlace(vararg transform: VisualTreeTransform<in T>) { public fun <T : Vision> T.transformInPlace(vararg transform: VisualTreeTransform<in T>) {
transform.forEach { it(this) } transform.forEach { it(this) }
} }

View File

@ -2,6 +2,7 @@ package hep.dataforge.vision.solid.three
import hep.dataforge.meta.boolean import hep.dataforge.meta.boolean
import hep.dataforge.meta.node import hep.dataforge.meta.node
import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
@ -19,13 +20,13 @@ import kotlin.reflect.KClass
/** /**
* Basic geometry-based factory * Basic geometry-based factory
*/ */
abstract class MeshThreeFactory<in T : Solid>( public abstract class MeshThreeFactory<in T : Solid>(
override val type: KClass<in T> override val type: KClass<in T>
) : ThreeFactory<T> { ) : ThreeFactory<T> {
/** /**
* Build a geometry for an object * Build a geometry for an object
*/ */
abstract fun buildGeometry(obj: T): BufferGeometry public abstract fun buildGeometry(obj: T): BufferGeometry
override fun invoke(obj: T): Mesh { override fun invoke(obj: T): Mesh {
val geometry = buildGeometry(obj) val geometry = buildGeometry(obj)
@ -60,14 +61,14 @@ abstract class MeshThreeFactory<in T : Solid>(
return mesh return mesh
} }
companion object { public companion object {
val EDGES_KEY = "edges".asName() public val EDGES_KEY: Name = "edges".asName()
val WIREFRAME_KEY = "wireframe".asName() public val WIREFRAME_KEY: Name = "wireframe".asName()
val ENABLED_KEY = "enabled".asName() public val ENABLED_KEY: Name = "enabled".asName()
val EDGES_ENABLED_KEY = EDGES_KEY + ENABLED_KEY public val EDGES_ENABLED_KEY: Name = EDGES_KEY + ENABLED_KEY
val EDGES_MATERIAL_KEY = EDGES_KEY + SolidMaterial.MATERIAL_KEY public val EDGES_MATERIAL_KEY: Name = EDGES_KEY + SolidMaterial.MATERIAL_KEY
val WIREFRAME_ENABLED_KEY = WIREFRAME_KEY + ENABLED_KEY public val WIREFRAME_ENABLED_KEY: Name = WIREFRAME_KEY + ENABLED_KEY
val WIREFRAME_MATERIAL_KEY = WIREFRAME_KEY + SolidMaterial.MATERIAL_KEY public val WIREFRAME_MATERIAL_KEY: Name = WIREFRAME_KEY + SolidMaterial.MATERIAL_KEY
} }
} }

View File

@ -4,8 +4,8 @@ import hep.dataforge.vision.solid.Box
import hep.dataforge.vision.solid.detail import hep.dataforge.vision.solid.detail
import info.laht.threekt.geometries.BoxBufferGeometry import info.laht.threekt.geometries.BoxBufferGeometry
object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) { public object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
override fun buildGeometry(obj: Box) = override fun buildGeometry(obj: Box): BoxBufferGeometry =
obj.detail?.let { detail -> obj.detail?.let { detail ->
BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail) BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail)
} ?: BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize) } ?: BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize)

View File

@ -35,7 +35,6 @@ import org.w3c.dom.HTMLElement
import org.w3c.dom.Node import org.w3c.dom.Node
import org.w3c.dom.events.MouseEvent import org.w3c.dom.events.MouseEvent
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.max
import kotlin.math.sin import kotlin.math.sin
/** /**
@ -112,7 +111,10 @@ public class ThreeCanvas(
element.appendChild(renderer.domElement) element.appendChild(renderer.domElement)
renderer.setSize(max(options.minSize, element.clientWidth), max(options.minSize, element.clientWidth)) renderer.setSize(
element.clientWidth.coerceIn(options.minWith.toInt()..options.maxWith.toInt()),
element.clientHeight.coerceIn(options.minHeight.toInt()..options.maxHeight.toInt())
)
window.onresize = { window.onresize = {
renderer.setSize(element.clientWidth, element.clientWidth) renderer.setSize(element.clientWidth, element.clientWidth)
@ -238,7 +240,7 @@ public class ThreeCanvas(
} }
public companion object { public companion object {
public const val DO_NOT_HIGHLIGHT_TAG = "doNotHighlight" public const val DO_NOT_HIGHLIGHT_TAG: String = "doNotHighlight"
private const val HIGHLIGHT_NAME = "@highlight" private const val HIGHLIGHT_NAME = "@highlight"
private const val SELECT_NAME = "@select" private const val SELECT_NAME = "@select"
} }
@ -251,7 +253,8 @@ public fun ThreePlugin.output(
): ThreeCanvas = ThreeCanvas(element, this, spec, onClick) ): ThreeCanvas = ThreeCanvas(element, this, spec, onClick)
public fun ThreePlugin.render( public fun ThreePlugin.render(
element: HTMLElement, obj: Solid, element: HTMLElement,
spec: Canvas3DOptions = Canvas3DOptions.empty(), obj: Solid,
onClick: ((Name?) -> Unit)? = null, onSelect: ((Name?) -> Unit)? = null,
): Unit = output(element, spec, onClick).render(obj) options: Canvas3DOptions.() -> Unit = {},
): Unit = output(element, Canvas3DOptions(options), onSelect).render(obj)

View File

@ -9,17 +9,17 @@ import info.laht.threekt.geometries.PlaneBufferGeometry
import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.materials.MeshBasicMaterial
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import info.laht.threekt.textures.Texture import info.laht.threekt.textures.Texture
import kotlinx.browser.document
import org.w3c.dom.CanvasRenderingContext2D import org.w3c.dom.CanvasRenderingContext2D
import org.w3c.dom.CanvasTextBaseline import org.w3c.dom.CanvasTextBaseline
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.MIDDLE import org.w3c.dom.MIDDLE
import kotlin.browser.document
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* Using example from http://stemkoski.github.io/Three.js/Texture-From-Canvas.html * Using example from http://stemkoski.github.io/Three.js/Texture-From-Canvas.html
*/ */
object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> { public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
override val type: KClass<in SolidLabel> get() = SolidLabel::class override val type: KClass<in SolidLabel> get() = SolidLabel::class
override fun invoke(obj: SolidLabel): Object3D { override fun invoke(obj: SolidLabel): Object3D {

View File

@ -8,7 +8,7 @@ import info.laht.threekt.objects.Mesh
/** /**
* This should be inner, because it uses object builder * This should be inner, because it uses object builder
*/ */
class ThreeCompositeFactory(val three: ThreePlugin) : MeshThreeFactory<Composite>(Composite::class) { public class ThreeCompositeFactory(public val three: ThreePlugin) : MeshThreeFactory<Composite>(Composite::class) {
override fun buildGeometry(obj: Composite): BufferGeometry { override fun buildGeometry(obj: Composite): BufferGeometry {
val first = three.buildObject3D(obj.first) as? Mesh ?: error("First part of composite is not a mesh") val first = three.buildObject3D(obj.first) as? Mesh ?: error("First part of composite is not a mesh")

View File

@ -4,7 +4,7 @@ import hep.dataforge.vision.solid.Convex
import info.laht.threekt.external.geometries.ConvexBufferGeometry import info.laht.threekt.external.geometries.ConvexBufferGeometry
import info.laht.threekt.math.Vector3 import info.laht.threekt.math.Vector3
object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) { public object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
override fun buildGeometry(obj: Convex): ConvexBufferGeometry { override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
@Suppress("USELESS_CAST") val vectors = obj.points.toTypedArray() as Array<Vector3> @Suppress("USELESS_CAST") val vectors = obj.points.toTypedArray() as Array<Vector3>
return ConvexBufferGeometry(vectors) return ConvexBufferGeometry(vectors)

View File

@ -7,7 +7,7 @@ import info.laht.threekt.geometries.CylinderBufferGeometry
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.pow import kotlin.math.pow
object ThreeCylinderFactory : MeshThreeFactory<ConeSegment>(ConeSegment::class) { public object ThreeCylinderFactory : MeshThreeFactory<ConeSegment>(ConeSegment::class) {
override fun buildGeometry(obj: ConeSegment): BufferGeometry { override fun buildGeometry(obj: ConeSegment): BufferGeometry {
val cylinder = obj.detail?.let { val cylinder = obj.detail?.let {
val segments = it.toDouble().pow(0.5).toInt() val segments = it.toDouble().pow(0.5).toInt()

View File

@ -18,21 +18,21 @@ import kotlin.reflect.KClass
* Builder and updater for three.js object * Builder and updater for three.js object
*/ */
@Type(TYPE) @Type(TYPE)
interface ThreeFactory<in T : Vision> { public interface ThreeFactory<in T : Vision> {
val type: KClass<in T> public val type: KClass<in T>
operator fun invoke(obj: T): Object3D public operator fun invoke(obj: T): Object3D
companion object { public companion object {
const val TYPE = "threeFactory" public const val TYPE: String = "threeFactory"
} }
} }
/** /**
* Update position, rotation and visibility * Update position, rotation and visibility
*/ */
fun Object3D.updatePosition(obj: Vision) { public fun Object3D.updatePosition(obj: Vision) {
visible = obj.visible ?: true visible = obj.visible ?: true
if(obj is Solid) { if(obj is Solid) {
position.set(obj.x, obj.y, obj.z) position.set(obj.x, obj.y, obj.z)
@ -45,7 +45,7 @@ fun Object3D.updatePosition(obj: Vision) {
/** /**
* Update non-position non-geometry property * Update non-position non-geometry property
*/ */
fun Object3D.updateProperty(source: Vision, propertyName: Name) { public fun Object3D.updateProperty(source: Vision, propertyName: Name) {
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) { if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
this.material = getMaterial(source, false) this.material = getMaterial(source, false)
} else if ( } else if (
@ -63,7 +63,7 @@ fun Object3D.updateProperty(source: Vision, propertyName: Name) {
/** /**
* Generic factory for elements which provide inside geometry builder * Generic factory for elements which provide inside geometry builder
*/ */
object ThreeShapeFactory : MeshThreeFactory<GeometrySolid>(GeometrySolid::class) { public object ThreeShapeFactory : MeshThreeFactory<GeometrySolid>(GeometrySolid::class) {
override fun buildGeometry(obj: GeometrySolid): BufferGeometry { override fun buildGeometry(obj: GeometrySolid): BufferGeometry {
return obj.run { return obj.run {
ThreeGeometryBuilder().apply { toGeometry(this) }.build() ThreeGeometryBuilder().apply { toGeometry(this) }.build()

View File

@ -12,7 +12,7 @@ import kotlin.reflect.KClass
/** /**
* *
*/ */
object ThreeLabelFactory : ThreeFactory<SolidLabel> { public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
override val type: KClass<in SolidLabel> get() = SolidLabel::class override val type: KClass<in SolidLabel> get() = SolidLabel::class
override fun invoke(obj: SolidLabel): Object3D { override fun invoke(obj: SolidLabel): Object3D {

View File

@ -10,7 +10,7 @@ import info.laht.threekt.math.Color
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import kotlin.reflect.KClass import kotlin.reflect.KClass
object ThreeLineFactory : ThreeFactory<PolyLine> { public object ThreeLineFactory : ThreeFactory<PolyLine> {
override val type: KClass<PolyLine> get() = PolyLine::class override val type: KClass<PolyLine> get() = PolyLine::class
override fun invoke(obj: PolyLine): Object3D { override fun invoke(obj: PolyLine): Object3D {

View File

@ -11,7 +11,7 @@ import kotlin.collections.set
import kotlin.reflect.KClass import kotlin.reflect.KClass
import info.laht.threekt.objects.Group as ThreeGroup import info.laht.threekt.objects.Group as ThreeGroup
class ThreePlugin : AbstractPlugin() { public class ThreePlugin : AbstractPlugin() {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
private val objectFactories = HashMap<KClass<out Solid>, ThreeFactory<*>>() private val objectFactories = HashMap<KClass<out Solid>, ThreeFactory<*>>()
@ -35,9 +35,9 @@ class ThreePlugin : AbstractPlugin() {
as ThreeFactory<Solid>? as ThreeFactory<Solid>?
} }
fun buildObject3D(obj: Solid): Object3D { public fun buildObject3D(obj: Solid): Object3D {
return when (obj) { return when (obj) {
is ThreeVision -> obj.toObject3D() is ThreeVision -> obj.render()
is Proxy -> proxyFactory(obj) is Proxy -> proxyFactory(obj)
is SolidGroup -> { is SolidGroup -> {
val group = ThreeGroup() val group = ThreeGroup()
@ -69,17 +69,17 @@ class ThreePlugin : AbstractPlugin() {
} }
} }
obj.onChildrenChange(this) { name, child -> obj.onChildrenChange(this) { nameToken, child ->
if (name.isEmpty()) { // if (name.isEmpty()) {
logger.error { "Children change with empty name on $group" } // logger.error { "Children change with empty name on $group" }
return@onChildrenChange // return@onChildrenChange
} // }
// val parentName = name.cutLast() // val parentName = name.cutLast()
// val childName = name.last()!! // val childName = name.last()!!
//removing old object //removing old object
findChild(name)?.let { oldChild -> findChild(nameToken.asName())?.let { oldChild ->
oldChild.parent?.remove(oldChild) oldChild.parent?.remove(oldChild)
} }
@ -87,7 +87,7 @@ class ThreePlugin : AbstractPlugin() {
if (child != null && child is Solid) { if (child != null && child is Solid) {
try { try {
val object3D = buildObject3D(child) val object3D = buildObject3D(child)
set(name, object3D) set(nameToken, object3D)
} catch (ex: Throwable) { } catch (ex: Throwable) {
logger.error(ex) { "Failed to render $child" } logger.error(ex) { "Failed to render $child" }
} }
@ -108,10 +108,10 @@ class ThreePlugin : AbstractPlugin() {
} }
} }
companion object : PluginFactory<ThreePlugin> { public companion object : PluginFactory<ThreePlugin> {
override val tag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP)
override val type = ThreePlugin::class override val type: KClass<ThreePlugin> = ThreePlugin::class
override fun invoke(meta: Meta, context: Context) = ThreePlugin() override fun invoke(meta: Meta, context: Context): ThreePlugin = ThreePlugin()
} }
} }

View File

@ -11,7 +11,7 @@ import info.laht.threekt.core.Object3D
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import kotlin.reflect.KClass import kotlin.reflect.KClass
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> { public class ThreeProxyFactory(public val three: ThreePlugin) : ThreeFactory<Proxy> {
private val cache = HashMap<Solid, Object3D>() private val cache = HashMap<Solid, Object3D>()
override val type: KClass<Proxy> = Proxy::class override val type: KClass<Proxy> = Proxy::class

View File

@ -5,7 +5,7 @@ import hep.dataforge.vision.solid.detail
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.SphereBufferGeometry import info.laht.threekt.geometries.SphereBufferGeometry
object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) { public object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) {
override fun buildGeometry(obj: Sphere): BufferGeometry { override fun buildGeometry(obj: Sphere): BufferGeometry {
return obj.detail?.let {detail -> return obj.detail?.let {detail ->
SphereBufferGeometry( SphereBufferGeometry(

View File

@ -8,11 +8,6 @@ import kotlinx.serialization.Serializable
/** /**
* A custom visual object that has its own Three.js renderer * A custom visual object that has its own Three.js renderer
*/ */
public interface ThreeVision : Solid { public abstract class ThreeVision : BasicSolid() {
public fun toObject3D(): Object3D public abstract fun render(): Object3D
}
@Serializable
public class CustomThreeVision(public val threeFactory: ThreeFactory<Solid>) : BasicSolid(), ThreeVision {
override fun toObject3D(): Object3D = threeFactory(this)
} }