diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt index c9c1512f..283d31ef 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt @@ -1,6 +1,9 @@ package ru.mipt.npm.sat +import hep.dataforge.meta.set import hep.dataforge.vision.solid.* +import hep.dataforge.vision.style +import hep.dataforge.vision.useStyle import kotlin.math.PI internal fun visionOfSatellite( @@ -12,7 +15,18 @@ internal fun visionOfSatellite( ySegmentSize: Number = xSegmentSize, fiberDiameter: Number = 1.0, ): SolidGroup = SolidGroup { - opacity = 0.3 + val transparent by style { + this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3 + } + + val red by style { + this[SolidMaterial.MATERIAL_COLOR_KEY] = "red" + } + + val blue by style { + this[SolidMaterial.MATERIAL_COLOR_KEY] = "blue" + } + val totalXSize = xSegments * xSegmentSize.toDouble() val totalYSize = ySegments * ySegmentSize.toDouble() for (layer in 1..layers) { @@ -20,6 +34,7 @@ internal fun visionOfSatellite( for (i in 1..xSegments) { for (j in 1..ySegments) { box(xSegmentSize, ySegmentSize, layerHeight, name = "segment[$i,$j]") { + useStyle(transparent) z = (layer - 0.5) * layerHeight.toDouble() x = (i - 0.5) * xSegmentSize.toDouble() y = (j - 0.5) * ySegmentSize.toDouble() @@ -29,23 +44,21 @@ internal fun visionOfSatellite( group("fibers") { for (i in 1..xSegments) { cylinder(fiberDiameter, totalYSize) { + useStyle(red) rotationX = PI / 2 z = (layer - 1.0) * layerHeight.toDouble() + fiberDiameter.toDouble() x = (i - 0.5) * xSegmentSize.toDouble() - y = totalYSize/2 - - color("red") + y = totalYSize / 2 } } for (j in 1..ySegments) { cylinder(fiberDiameter, totalXSize) { + useStyle(blue) rotationY = PI / 2 z = (layer) * layerHeight.toDouble() - fiberDiameter.toDouble() y = (j - 0.5) * xSegmentSize.toDouble() - x = totalXSize/2 - - color("blue") + x = totalXSize / 2 } } } diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index a605e717..a1f0dca1 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -42,9 +42,9 @@ fun main() { val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName() val targetVision = sat[target] as Solid targetVision.color("red") - delay(300) + delay(1000) targetVision.color.clear() - delay(10) + delay(500) } } diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/ThreeCanvasComponent.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/ThreeCanvasComponent.kt index aa78a0fb..877ce5bc 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/ThreeCanvasComponent.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/ThreeCanvasComponent.kt @@ -37,8 +37,7 @@ public val ThreeCanvasComponent: FunctionalComponent = functio if (canvas == null) { val element = elementRef.current as? HTMLElement ?: error("Canvas element not found") val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin) - val newCanvas: ThreeCanvas = - three.createCanvas(element, props.options ?: Canvas3DOptions.empty()) + val newCanvas: ThreeCanvas = three.createCanvas(element, props.options ?: Canvas3DOptions.empty()) props.canvasCallback?.invoke(newCanvas) canvas = newCanvas } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleReference.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleReference.kt new file mode 100644 index 00000000..ac8da104 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleReference.kt @@ -0,0 +1,33 @@ +package hep.dataforge.vision + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import kotlin.properties.ReadOnlyProperty + +/** + * A reference to a style defined in a specific container + */ +public class StyleReference(public val owner: VisionGroup, public val name: String) + +private tailrec fun styleIsDefined(vision: Vision, reference: StyleReference): Boolean = when { + reference.owner === vision -> true + vision.parent == null -> false + else -> styleIsDefined(vision.parent!!, reference) +} + +@VisionBuilder +public fun Vision.useStyle(reference: StyleReference) { + //check that style is defined in a parent + //check(styleIsDefined(this, reference)) { "Style reference does not belong to a Vision parent" } + useStyle(reference.name) +} + +@DFExperimental +@VisionBuilder +public fun VisionGroup.style(builder: MetaBuilder.() -> Unit): ReadOnlyProperty = + ReadOnlyProperty { _, property -> + val styleName = property.name + styleSheet.define(styleName, Meta(builder)) + StyleReference(this, styleName) + } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt index d90fe655..a22ea6bb 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt @@ -110,4 +110,6 @@ public fun Vision.getStyleItems(name: Name): Sequence> { /** * Collect all styles for this object in a single laminate */ -public val Vision.allStyles: Laminate get() = Laminate(styles.mapNotNull(::getStyle)) \ No newline at end of file +public val Vision.allStyles: Laminate get() = Laminate(styles.mapNotNull(::getStyle)) + + diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index c0cc594a..2bfde782 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -63,7 +63,7 @@ private fun Vision.isolate(manager: VisionManager): Vision { } @Serializable -public class VisionChange( +public data class VisionChange( public val reset: Boolean = false, public val vision: Vision? = null, @Serializable(MetaSerializer::class) public val properties: Meta? = null, diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt new file mode 100644 index 00000000..f206f063 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt @@ -0,0 +1,18 @@ +package hep.dataforge.vision + +import hep.dataforge.meta.MetaItem +import hep.dataforge.names.Name + +/** + * Property containers are used to create a symmetric behaviors for vision properties and style builders + */ +public interface VisionPropertyContainer { + public fun getProperty( + name: Name, + inherit: Boolean = false, + includeStyles: Boolean = true, + includeDefaults: Boolean = true, + ): MetaItem<*>? + + public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true) +} \ No newline at end of file diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt index 36f0ee1f..11eaa0e1 100644 --- a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt @@ -138,7 +138,7 @@ public class VisionClient : AbstractPlugin() { renderVision(element, change.vision, outputMeta) } - logger.debug { "Got update $change for output with name $name" } + logger.info { "Got update ${change.toString()} for output with name $name" } visionMap[element]?.update(change) ?: console.info("Target vision for element $element with name $name not found") } else { 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 4ddfe548..b64074da 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 @@ -88,6 +88,7 @@ public fun VisionContainerBuilder.group( /** * Define a group with given [name], attach it to this parent and return it. */ +@VisionBuilder public fun VisionContainerBuilder.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup = SolidGroup().apply(action).also { set(name, it) } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt index 11fffe88..f5b679e5 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt @@ -130,7 +130,8 @@ public class ThreeCanvas( } } - public fun attach(element: Element) { + internal fun attach(element: Element) { + check(element.children.length == 0){"The element for Three canvas is not empty"} element.appendChild(canvas) updateSize() } @@ -189,8 +190,10 @@ public class ThreeCanvas( } public override fun render(vision: Solid) { - //clear old root - clear() + scene.children.find { it.name == "@root" }?.let { + //Throw error is something is already rendered here + error("Root object already is present in the canvas") + } val object3D = three.buildObject3D(vision) object3D.name = "@root" diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index a7a74ce3..eaadc56a 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -136,9 +136,19 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { return if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING } + public fun renderSolid( + element: Element, + vision: Solid, + options: Canvas3DOptions, + ): ThreeCanvas = createCanvas(element, options).apply { + render(vision) + } + override fun render(element: Element, vision: Vision, meta: Meta) { - createCanvas(element, Canvas3DOptions.read(meta)).render( - vision as? Solid ?: error("Solid expected but ${vision::class} is found") + renderSolid( + element, + vision as? Solid ?: error("Solid expected but ${vision::class} is found"), + Canvas3DOptions.read(meta) ) } @@ -153,7 +163,7 @@ public fun ThreePlugin.render( element: HTMLElement, obj: Solid, options: Canvas3DOptions.() -> Unit = {}, -): ThreeCanvas = createCanvas(element, Canvas3DOptions(options)).apply { render(obj) } +): ThreeCanvas = renderSolid(element, obj, Canvas3DOptions(options)) internal operator fun Object3D.set(token: NameToken, object3D: Object3D) { object3D.name = token.toString()