New primitives #76

Merged
altavir merged 4 commits from teldufalsari/visionforge:teldufalsari/rootToSolid into dev 2023-10-10 08:16:50 +03:00
40 changed files with 357 additions and 213 deletions
Showing only changes of commit 8749c0bda7 - Show all commits

View File

@ -4,6 +4,7 @@
### Added ### Added
- Context receivers flag - Context receivers flag
- MeshLine for thick lines - MeshLine for thick lines
- Custom client-side events and thier processing in VisionServer
### Changed ### Changed
- Color accessor property is now `colorProperty`. Color uses `invoke` instead of `set` - Color accessor property is now `colorProperty`. Color uses `invoke` instead of `set`

View File

@ -3,6 +3,7 @@ package ru.mipt.npm.root
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.set
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.parseAsName import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
@ -12,7 +13,6 @@ import space.kscience.kmath.geometry.fromRotationMatrix
import space.kscience.kmath.linear.VirtualMatrix import space.kscience.kmath.linear.VirtualMatrix
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.isEmpty import space.kscience.visionforge.isEmpty
import space.kscience.visionforge.set
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
import kotlin.math.PI import kotlin.math.PI
@ -188,7 +188,7 @@ private fun SolidGroup.addShape(
} }
"TGeoPgon" -> { "TGeoPgon" -> {
//TODO add a inner polygone layer
val fDphi by shape.meta.double(0.0) val fDphi by shape.meta.double(0.0)
val fNz by shape.meta.int(2) val fNz by shape.meta.int(2)
val fPhi1 by shape.meta.double(360.0) val fPhi1 by shape.meta.double(360.0)
@ -201,19 +201,26 @@ private fun SolidGroup.addShape(
val startphi = degToRad(fPhi1) val startphi = degToRad(fPhi1)
val deltaphi = degToRad(fDphi) val deltaphi = degToRad(fDphi)
extruded(name) { fun Shape2DBuilder.pGon(radius: Double){
//getting the radius of first
require(fNz > 1) { "The polyhedron geometry requires at least two planes" }
val baseRadius = fRmax[0]
shape {
(0..<fNedges).forEach { (0..<fNedges).forEach {
val phi = deltaphi / fNedges * it + startphi val phi = deltaphi / fNedges * it + startphi
point(baseRadius * cos(phi), baseRadius * sin(phi)) point(radius * cos(phi), radius * sin(phi))
} }
} }
(0 until fNz).forEach { index ->
//scaling all radii relative to first layer radius surface(name) {
layer(fZ[index], scale = fRmax[index] / baseRadius) //getting the radius of first
require(fNz > 1) { "The polyhedron geometry requires at least two planes" }
for (index in 0 until fNz){
layer(
fZ[index],
innerBuilder = {
pGon(fRmin[index])
},
outerBuilder = {
pGon(fRmax[index])
}
)
} }
}.apply(block) }.apply(block)
} }

View File

@ -12,6 +12,7 @@ import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass import kotlinx.serialization.modules.subclass
@Suppress("UNUSED_PARAMETER")
private fun <T> jsonRootDeserializer( private fun <T> jsonRootDeserializer(
tSerializer: KSerializer<T>, tSerializer: KSerializer<T>,
builder: (JsonElement) -> T, builder: (JsonElement) -> T,
@ -83,7 +84,7 @@ private object RootDecoder {
return ref.getOrPutValue { return ref.getOrPutValue {
// println("Decoding $it") // println("Decoding $it")
val actualTypeName = it.jsonObject["_typename"]?.jsonPrimitive?.content // val actualTypeName = it.jsonObject["_typename"]?.jsonPrimitive?.content
input.json.decodeFromJsonElement(tSerializer, it) input.json.decodeFromJsonElement(tSerializer, it)
} }
} }

View File

@ -34,6 +34,6 @@ class GDMLVisionTest {
val child = cubes[Name.of("composite-000","segment-0")] val child = cubes[Name.of("composite-000","segment-0")]
assertNotNull(child) assertNotNull(child)
child.properties.setValue(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) child.properties.setValue(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
assertEquals("red", child.properties.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string) assertEquals("red", child.properties.getMeta(SolidMaterial.MATERIAL_COLOR_KEY).string)
} }
} }

View File

@ -69,7 +69,7 @@ class Model(val manager: VisionManager) {
fun reset() { fun reset() {
map.values.forEach { map.values.forEach {
it.properties.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, null) it.properties.setMeta(SolidMaterial.MATERIAL_COLOR_KEY, null)
} }
tracks.children.clear() tracks.children.clear()
} }

View File

@ -75,7 +75,7 @@ fun main() = serve {
val incRot = Quaternion.fromRotation(30.degrees, Euclidean3DSpace.zAxis) val incRot = Quaternion.fromRotation(30.degrees, Euclidean3DSpace.zAxis)
val rotationJob = context.launch { context.launch {
var time: Long = 0L var time: Long = 0L
while (isActive) { while (isActive) {
with(QuaternionField) { with(QuaternionField) {

View File

@ -12,10 +12,11 @@ fun main() = makeVisionFile {
extruded("extruded") { extruded("extruded") {
shape{ shape{
polygon(8, 100) polygon(8, 100)
}
layer(-30) layer(-30)
layer(0, x = 10, y = 10)
layer(30) layer(30)
} }
} }
} }
} }
}

View File

@ -0,0 +1,19 @@
package space.kscience.visionforge.examples
import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.polygon
import space.kscience.visionforge.solid.solid
import space.kscience.visionforge.solid.surface
fun main() = makeVisionFile {
vision("canvas") {
solid {
ambientLight()
surface("surface") {
layer(0, { polygon(8, 10) }, { polygon(8, 20) })
layer(10, { polygon(8, 20) }, { polygon(8, 30) })
layer(20, { polygon(8, 10) }, { polygon(8, 20) })
}
}
}
}

View File

@ -1,7 +1,7 @@
kotlin.code.style=official kotlin.code.style=official
kotlin.mpp.stability.nowarn=true kotlin.mpp.stability.nowarn=true
kotlin.js.compiler=ir kotlin.js.compiler=ir
kotlin.incremental.js.ir=true #kotlin.incremental.js.ir=true
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.jvmargs=-Xmx4G org.gradle.jvmargs=-Xmx4G

View File

@ -3,7 +3,7 @@ package space.kscience.visionforge
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor

View File

@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
@JvmInline @JvmInline
public value class StyleSheet(private val owner: Vision) { public value class StyleSheet(private val owner: Vision) {
private val styleNode: Meta get() = owner.properties.getProperty(STYLESHEET_KEY) private val styleNode: Meta get() = owner.properties.getMeta(STYLESHEET_KEY)
public val items: Map<NameToken, Meta> get() = styleNode.items public val items: Map<NameToken, Meta> get() = styleNode.items
@ -23,7 +23,7 @@ public value class StyleSheet(private val owner: Vision) {
* Define a style without notifying owner * Define a style without notifying owner
*/ */
public fun define(key: String, style: Meta?) { public fun define(key: String, style: Meta?) {
owner.properties.setProperty(STYLESHEET_KEY + key, style) owner.properties.setMeta(STYLESHEET_KEY + key, style)
} }
/** /**

View File

@ -37,6 +37,9 @@ public interface Vision : Described {
* Update this vision using a dif represented by [VisionChange]. * Update this vision using a dif represented by [VisionChange].
*/ */
public fun update(change: VisionChange) { public fun update(change: VisionChange) {
if (change.children?.isNotEmpty() == true) {
error("Vision is not a group")
}
change.properties?.let { change.properties?.let {
updateProperties(it, Name.EMPTY) updateProperties(it, Name.EMPTY)
} }
@ -67,7 +70,7 @@ public var Vision.visible: Boolean?
*/ */
public fun Vision.onPropertyChange( public fun Vision.onPropertyChange(
scope: CoroutineScope? = manager?.context, scope: CoroutineScope? = manager?.context,
callback: suspend (Name) -> Unit callback: suspend (Name) -> Unit,
): Job = properties.changes.onEach { ): Job = properties.changes.onEach {
callback(it) callback(it)
}.launchIn(scope ?: error("Orphan Vision can't observe properties")) }.launchIn(scope ?: error("Orphan Vision can't observe properties"))

View File

@ -1,15 +1,15 @@
package space.kscience.visionforge package space.kscience.visionforge
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
@ -27,22 +27,6 @@ private fun Vision.deepCopy(manager: VisionManager): Vision {
return manager.decodeFromJson(json) return manager.decodeFromJson(json)
} }
/**
* A vision used only in change propagation and showing that the target should be removed
*/
@Serializable
public object NullVision : Vision {
override var parent: Vision?
get() = null
set(_) {
error("Can't set parent for null vision")
}
override val properties: MutableVisionProperties get() = error("Can't get properties of `NullVision`")
override val descriptor: MetaDescriptor? = null
}
/** /**
* An update for a [Vision] * An update for a [Vision]
@ -111,18 +95,6 @@ public class VisionChangeBuilder : MutableVisionContainer<Vision> {
) )
} }
/**
* @param vision a new value for vision content. If the Vision is to be removed should be [NullVision]
* @param properties updated properties
* @param children a map of children changed in ths [VisionChange]. If a child to be removed, set [delete] flag to true.
*/
@Serializable
public data class VisionChange(
public val vision: Vision? = null,
public val properties: Meta? = null,
public val children: Map<Name, VisionChange>? = null,
)
public inline fun VisionManager.VisionChange(block: VisionChangeBuilder.() -> Unit): VisionChange = public inline fun VisionManager.VisionChange(block: VisionChangeBuilder.() -> Unit): VisionChange =
VisionChangeBuilder().apply(block).deepCopy(this) VisionChangeBuilder().apply(block).deepCopy(this)

View File

@ -0,0 +1,52 @@
package space.kscience.visionforge
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.names.Name
/**
* An event propagated from client to a server
*/
@Serializable
public sealed interface VisionEvent
/**
* An event that consists of custom meta
*/
@Serializable
@SerialName("meta")
public class VisionMetaEvent(public val targetName: Name, public val meta: Meta) : VisionEvent
/**
* A vision used only in change propagation and showing that the target should be removed
*/
@Serializable
@SerialName("null")
public object NullVision : Vision {
override var parent: Vision?
get() = null
set(_) {
error("Can't set parent for null vision")
}
override val properties: MutableVisionProperties get() = error("Can't get properties of `NullVision`")
override val descriptor: MetaDescriptor? = null
}
/**
* @param vision a new value for vision content. If the Vision is to be removed should be [NullVision]
* @param properties updated properties
* @param children a map of children changed in ths [VisionChange].
*/
@Serializable
@SerialName("change")
public data class VisionChange(
public val vision: Vision? = null,
public val properties: Meta? = null,
public val children: Map<Name, VisionChange>? = null,
) : VisionEvent

View File

@ -10,11 +10,25 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.parseAsName import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
import space.kscience.visionforge.Vision.Companion.STYLE_KEY import space.kscience.visionforge.Vision.Companion.STYLE_KEY
public interface VisionGroup : Vision { public interface VisionGroup : Vision {
public val children: VisionChildren public val children: VisionChildren
override fun update(change: VisionChange) {
change.children?.forEach { (name, change) ->
if (change.vision != null || change.vision == NullVision) {
error("VisionGroup is read-only")
} else {
children.getChild(name)?.update(change)
}
}
change.properties?.let {
updateProperties(it, Name.EMPTY)
}
}
} }
public interface MutableVisionGroup : VisionGroup { public interface MutableVisionGroup : VisionGroup {
@ -22,15 +36,6 @@ public interface MutableVisionGroup : VisionGroup {
override val children: MutableVisionChildren override val children: MutableVisionChildren
public fun createGroup(): MutableVisionGroup public fun createGroup(): MutableVisionGroup
}
public val Vision.children: VisionChildren? get() = (this as? VisionGroup)?.children
/**
* A full base implementation for a [Vision]
*/
@Serializable
public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
override fun update(change: VisionChange) { override fun update(change: VisionChange) {
change.children?.forEach { (name, change) -> change.children?.forEach { (name, change) ->
@ -44,6 +49,15 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
updateProperties(it, Name.EMPTY) updateProperties(it, Name.EMPTY)
} }
} }
}
public val Vision.children: VisionChildren? get() = (this as? VisionGroup)?.children
/**
* A full base implementation for a [Vision]
*/
@Serializable
public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
@SerialName("children") @SerialName("children")
protected var childrenInternal: MutableMap<NameToken, Vision>? = null protected var childrenInternal: MutableMap<NameToken, Vision>? = null

View File

@ -12,7 +12,7 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
public interface VisionProperties { public interface VisionProperties : MetaProvider {
/** /**
* Raw Visions own properties without styles, defaults, etc. * Raw Visions own properties without styles, defaults, etc.
@ -23,21 +23,26 @@ public interface VisionProperties {
public fun getValue( public fun getValue(
name: Name, name: Name,
inherit: Boolean? = null, inherit: Boolean?,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
): Value? ): Value?
override fun getValue(name: Name): Value? = getValue(name, null, null)
/** /**
* Get property with given layer flags. * Get property with given layer flags.
* @param inherit toggles parent node property lookup. Null means inference from descriptor. * @param inherit toggles parent node property lookup. Null means inference from descriptor.
* @param includeStyles toggles inclusion of properties from styles. * @param includeStyles toggles inclusion of properties from styles.
*/ */
public fun getProperty( public fun getMeta(
name: Name, name: Name,
inherit: Boolean? = null, inherit: Boolean?,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
): Meta ): Meta
override fun getMeta(name: Name): Meta? = getMeta(name, null, null)
public val changes: Flow<Name> public val changes: Flow<Name>
/** /**
@ -47,9 +52,9 @@ public interface VisionProperties {
public fun invalidate(propertyName: Name) public fun invalidate(propertyName: Name)
} }
public interface MutableVisionProperties : VisionProperties { public interface MutableVisionProperties : VisionProperties, MutableMetaProvider {
override fun getProperty( override fun getMeta(
name: Name, name: Name,
inherit: Boolean?, inherit: Boolean?,
includeStyles: Boolean?, includeStyles: Boolean?,
@ -60,21 +65,31 @@ public interface MutableVisionProperties : VisionProperties {
includeStyles, includeStyles,
) )
public fun setProperty( public fun setMeta(
name: Name, name: Name,
node: Meta?, node: Meta?,
notify: Boolean = true, notify: Boolean,
) )
public fun setValue( public fun setValue(
name: Name, name: Name,
value: Value?, value: Value?,
notify: Boolean = true, notify: Boolean,
) )
override fun getMeta(name: Name): MutableMeta = getMeta(name, null, null)
override fun setMeta(name: Name, node: Meta?) {
setMeta(name, node, true)
}
override fun setValue(name: Name, value: Value?) {
setValue(name, value, true)
}
} }
public fun MutableVisionProperties.remove(name: Name) { public fun MutableVisionProperties.remove(name: Name) {
setProperty(name, null) setMeta(name, null)
} }
public fun MutableVisionProperties.remove(name: String) { public fun MutableVisionProperties.remove(name: String) {
@ -134,7 +149,7 @@ private class VisionPropertiesItem(
) )
override fun setMeta(name: Name, node: Meta?) { override fun setMeta(name: Name, node: Meta?) {
properties.setProperty(nodeName + name, node) properties.setMeta(nodeName + name, node)
} }
override fun toString(): String = Meta.toString(this) override fun toString(): String = Meta.toString(this)
@ -187,7 +202,7 @@ public abstract class AbstractVisionProperties(
return descriptor?.defaultValue return descriptor?.defaultValue
} }
override fun setProperty(name: Name, node: Meta?, notify: Boolean) { override fun setMeta(name: Name, node: Meta?, notify: Boolean) {
//ignore if the value is the same as existing //ignore if the value is the same as existing
if (own?.getMeta(name) == node) return if (own?.getMeta(name) == node) return
@ -257,11 +272,11 @@ public fun VisionProperties.getValue(
/** /**
* Get [Vision] property using key as a String * Get [Vision] property using key as a String
*/ */
public fun VisionProperties.getProperty( public fun VisionProperties.getMeta(
name: String, name: String,
inherit: Boolean? = null, inherit: Boolean? = null,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
): Meta = getProperty(name.parseAsName(), inherit, includeStyles) ): Meta = getMeta(name.parseAsName(), inherit, includeStyles)
/** /**
* The root property node with given inheritance and style flags * The root property node with given inheritance and style flags
@ -271,33 +286,33 @@ public fun VisionProperties.getProperty(
public fun MutableVisionProperties.root( public fun MutableVisionProperties.root(
inherit: Boolean? = null, inherit: Boolean? = null,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
): MutableMeta = getProperty(Name.EMPTY, inherit, includeStyles) ): MutableMeta = getMeta(Name.EMPTY, inherit, includeStyles)
/** /**
* Get [Vision] property using key as a String * Get [Vision] property using key as a String
*/ */
public fun MutableVisionProperties.getProperty( public fun MutableVisionProperties.getMeta(
name: String, name: String,
inherit: Boolean? = null, inherit: Boolean? = null,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
): MutableMeta = getProperty(name.parseAsName(), inherit, includeStyles) ): MutableMeta = getMeta(name.parseAsName(), inherit, includeStyles)
//
public operator fun MutableVisionProperties.set(name: Name, value: Number): Unit = //public operator fun MutableVisionProperties.set(name: Name, value: Number): Unit =
setValue(name, value.asValue()) // setValue(name, value.asValue())
//
public operator fun MutableVisionProperties.set(name: String, value: Number): Unit = //public operator fun MutableVisionProperties.set(name: String, value: Number): Unit =
set(name.parseAsName(), value) // set(name.parseAsName(), value)
//
public operator fun MutableVisionProperties.set(name: Name, value: Boolean): Unit = //public operator fun MutableVisionProperties.set(name: Name, value: Boolean): Unit =
setValue(name, value.asValue()) // setValue(name, value.asValue())
//
public operator fun MutableVisionProperties.set(name: String, value: Boolean): Unit = //public operator fun MutableVisionProperties.set(name: String, value: Boolean): Unit =
set(name.parseAsName(), value) // set(name.parseAsName(), value)
//
public operator fun MutableVisionProperties.set(name: Name, value: String): Unit = //public operator fun MutableVisionProperties.set(name: Name, value: String): Unit =
setValue(name, value.asValue()) // setValue(name, value.asValue())
//
public operator fun MutableVisionProperties.set(name: String, value: String): Unit = //public operator fun MutableVisionProperties.set(name: String, value: String): Unit =
set(name.parseAsName(), value) // set(name.parseAsName(), value)

View File

@ -17,10 +17,10 @@ public fun Vision.flowProperty(
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
): Flow<Meta> = flow { ): Flow<Meta> = flow {
//Pass initial value. //Pass initial value.
emit(properties.getProperty(propertyName, inherit, includeStyles)) emit(properties.getMeta(propertyName, inherit, includeStyles))
properties.changes.collect { name -> properties.changes.collect { name ->
if (name.startsWith(propertyName)) { if (name.startsWith(propertyName)) {
emit(properties.getProperty(propertyName, inherit, includeStyles)) emit(properties.getMeta(propertyName, inherit, includeStyles))
} }
} }
} }

View File

@ -93,7 +93,6 @@ public fun FlowContent.visionFragment(
updatesUrl: String? = null, updatesUrl: String? = null,
onVisionRendered: (Name, Vision) -> Unit = { _, _ -> }, onVisionRendered: (Name, Vision) -> Unit = { _, _ -> },
idPrefix: String? = null, idPrefix: String? = null,
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
): Unit = consumer.visionFragment( ): Unit = consumer.visionFragment(
visionManager = visionManager, visionManager = visionManager,

View File

@ -6,10 +6,11 @@ import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.number import space.kscience.dataforge.meta.number
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.* import space.kscience.visionforge.AbstractVision
import space.kscience.visionforge.Vision
//TODO replace by something //TODO replace by something
internal val Vision.mutableProperties get() = properties.getProperty(Name.EMPTY, false, false) internal val Vision.mutableProperties get() = properties.getMeta(Name.EMPTY, false, false)
@Serializable @Serializable
public abstract class VisionOfHtmlInput : AbstractVision() { public abstract class VisionOfHtmlInput : AbstractVision() {

View File

@ -23,10 +23,10 @@ public fun Vision.useProperty(
callback: (Meta) -> Unit, callback: (Meta) -> Unit,
): Job { ): Job {
//Pass initial value. //Pass initial value.
callback(properties.getProperty(propertyName, inherit, includeStyles)) callback(properties.getMeta(propertyName, inherit, includeStyles))
return properties.changes.onEach { name -> return properties.changes.onEach { name ->
if (name.startsWith(propertyName)) { if (name.startsWith(propertyName)) {
callback(properties.getProperty(propertyName, inherit, includeStyles)) callback(properties.getMeta(propertyName, inherit, includeStyles))
} }
}.launchIn(scope ?: error("Orphan Vision can't observe properties")) }.launchIn(scope ?: error("Orphan Vision can't observe properties"))
} }

View File

@ -4,6 +4,7 @@ import kotlinx.html.*
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.set
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.* import space.kscience.visionforge.*

View File

@ -42,7 +42,7 @@ internal class VisionPropertyTest {
@Test @Test
fun testPropertyEdit() { fun testPropertyEdit() {
val vision = manager.group() val vision = manager.group()
vision.properties.getProperty("fff.ddd").apply { vision.properties.getMeta("fff.ddd").apply {
value = 2.asValue() value = 2.asValue()
} }
assertEquals(2, vision.properties.getValue("fff.ddd")?.int) assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
@ -52,7 +52,7 @@ internal class VisionPropertyTest {
@Test @Test
fun testPropertyUpdate() { fun testPropertyUpdate() {
val vision = manager.group() val vision = manager.group()
vision.properties.getProperty("fff").updateWith(TestScheme) { vision.properties.getMeta("fff").updateWith(TestScheme) {
ddd = 2 ddd = 2
} }
assertEquals(2, vision.properties.getValue("fff.ddd")?.int) assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
@ -87,7 +87,7 @@ internal class VisionPropertyTest {
child.properties.remove("test") child.properties.remove("test")
assertEquals(11, child.properties.getProperty("test", inherit = true).int) assertEquals(11, child.properties.getMeta("test", inherit = true).int)
// assertEquals(11, deferred.await()?.int) // assertEquals(11, deferred.await()?.int)
// assertEquals(2, callCounter) // assertEquals(2, callCounter)
subscription.cancel() subscription.cancel()

View File

@ -4,6 +4,7 @@ import kotlinx.browser.document
import kotlinx.browser.window import kotlinx.browser.window
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
@ -11,10 +12,7 @@ import kotlinx.coroutines.sync.withLock
import org.w3c.dom.* import org.w3c.dom.*
import org.w3c.dom.url.URL import org.w3c.dom.url.URL
import space.kscience.dataforge.context.* import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.parseAsName import space.kscience.dataforge.names.parseAsName
import space.kscience.visionforge.html.VisionTagConsumer import space.kscience.visionforge.html.VisionTagConsumer
@ -68,7 +66,7 @@ public class VisionClient : AbstractPlugin() {
/** /**
* Communicate vision property changed from rendering engine to model * Communicate vision property changed from rendering engine to model
*/ */
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: Meta?) { public fun notifyPropertyChanged(visionName: Name, propertyName: Name, item: Meta?) {
context.launch { context.launch {
mutex.withLock { mutex.withLock {
changeCollector.propertyChanged(visionName, propertyName, item) changeCollector.propertyChanged(visionName, propertyName, item)
@ -76,9 +74,17 @@ public class VisionClient : AbstractPlugin() {
} }
} }
// public fun visionChanged(name: Name?, child: Vision?) { private val eventCollector by lazy {
// changeCollector.setChild(name, child) MutableSharedFlow<VisionEvent>(meta["feedback.eventCache"].int ?: 100)
// } }
/**
* Send a custom feedback event
*/
public suspend fun sendEvent(event: VisionEvent) {
eventCollector.emit(event)
}
private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) { private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) {
vision.setAsRoot(visionManager) vision.setAsRoot(visionManager)
@ -103,7 +109,7 @@ public class VisionClient : AbstractPlugin() {
logger.info { "Updating vision data from $wsUrl" } logger.info { "Updating vision data from $wsUrl" }
//Individual websocket for this element //Individual websocket for this vision
WebSocket(wsUrl.toString()).apply { WebSocket(wsUrl.toString()).apply {
onmessage = { messageEvent -> onmessage = { messageEvent ->
val stringData: String? = messageEvent.data as? String val stringData: String? = messageEvent.data as? String
@ -131,7 +137,7 @@ public class VisionClient : AbstractPlugin() {
var feedbackJob: Job? = null var feedbackJob: Job? = null
//Feedback changes aggregation time in milliseconds //Feedback changes aggregation time in milliseconds
val feedbackAggregationTime = meta["aggregationTime"]?.int ?: 300 val feedbackAggregationTime = meta["feedback.aggregationTime"]?.int ?: 300
onopen = { onopen = {
feedbackJob = visionManager.context.launch { feedbackJob = visionManager.context.launch {
@ -144,18 +150,24 @@ public class VisionClient : AbstractPlugin() {
change.reset() change.reset()
} }
} }
// // take channel for given vision name
// eventCollector[name]?.let { channel ->
// for (e in channel) {
// send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), e))
// }
// }
} }
} }
logger.info { "WebSocket update channel established for output '$name'" } logger.info { "WebSocket feedback channel established for output '$name'" }
} }
onclose = { onclose = {
feedbackJob?.cancel() feedbackJob?.cancel()
logger.info { "WebSocket update channel closed for output '$name'" } logger.info { "WebSocket feedback channel closed for output '$name'" }
} }
onerror = { onerror = {
feedbackJob?.cancel() feedbackJob?.cancel()
logger.error { "WebSocket update channel error for output '$name'" } logger.error { "WebSocket feedback channel error for output '$name'" }
} }
} }
} }
@ -248,20 +260,26 @@ public class VisionClient : AbstractPlugin() {
} }
} }
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Meta?) { public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Meta?) {
visionPropertyChanged(visionName, propertyName.parseAsName(true), item) notifyPropertyChanged(visionName, propertyName.parseAsName(true), item)
} }
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Number) { public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Number) {
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item)) notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
} }
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: String) { public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: String) {
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item)) notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
} }
public fun VisionClient.visionPropertyChanged(visionName: Name, propertyName: String, item: Boolean) { public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Boolean) {
visionPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item)) notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.sendEvent(visionName: Name, event: MetaRepr): Unit {
context.launch {
sendEvent(VisionMetaEvent(visionName, event.toMeta()))
}
} }
private fun whenDocumentLoaded(block: Document.() -> Unit): Unit { private fun whenDocumentLoaded(block: Document.() -> Unit): Unit {

View File

@ -36,7 +36,7 @@ internal fun textVisionRenderer(
value = it ?: "" value = it ?: ""
} }
onChangeFunction = { onChangeFunction = {
client.visionPropertyChanged(name, VisionOfTextField::text.name, value) client.notifyPropertyChanged(name, VisionOfTextField::text.name, value)
} }
} }
} }
@ -58,7 +58,7 @@ internal fun numberVisionRenderer(
value = it?.toDouble() ?: 0.0 value = it?.toDouble() ?: 0.0
} }
onChangeFunction = { onChangeFunction = {
client.visionPropertyChanged(name, VisionOfNumberField::value.name, value) client.notifyPropertyChanged(name, VisionOfNumberField::value.name, value)
} }
} }
} }
@ -106,7 +106,7 @@ internal fun formVisionRenderer(
form.onsubmit = { event -> form.onsubmit = { event ->
event.preventDefault() event.preventDefault()
val formData = FormData(form).toMeta() val formData = FormData(form).toMeta()
client.visionPropertyChanged(name, VisionOfHtmlForm::values.name, formData) client.notifyPropertyChanged(name, VisionOfHtmlForm::values.name, formData)
console.info("Sent: ${formData.toMap()}") console.info("Sent: ${formData.toMap()}")
false false
} }

View File

@ -10,7 +10,6 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.plotly.Plot import space.kscience.plotly.Plot
import space.kscience.plotly.Plotly import space.kscience.plotly.Plotly
@ -34,7 +33,7 @@ public class VisionOfPlotly private constructor(
@Transient @Transient
override val properties: MutableVisionProperties = object : MutableVisionProperties { override val properties: MutableVisionProperties = object : MutableVisionProperties {
override fun setProperty(name: Name, node: Meta?, notify: Boolean) { override fun setMeta(name: Name, node: Meta?, notify: Boolean) {
meta.setMeta(name, node) meta.setMeta(name, node)
} }
@ -46,7 +45,7 @@ public class VisionOfPlotly private constructor(
override val descriptor: MetaDescriptor? get() = this@VisionOfPlotly.descriptor override val descriptor: MetaDescriptor? get() = this@VisionOfPlotly.descriptor
override fun getProperty( override fun getMeta(
name: Name, name: Name,
inherit: Boolean?, inherit: Boolean?,
includeStyles: Boolean?, includeStyles: Boolean?,

View File

@ -23,10 +23,7 @@ import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision import space.kscience.visionforge.*
import space.kscience.visionforge.VisionChange
import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.flowChanges
import space.kscience.visionforge.html.* import space.kscience.visionforge.html.*
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
@ -77,6 +74,11 @@ public class VisionRoute(
*/ */
public fun Application.serveVisionData( public fun Application.serveVisionData(
configuration: VisionRoute, configuration: VisionRoute,
onEvent: suspend Vision.(VisionEvent) -> Unit = { event ->
if (event is VisionChange) {
update(event)
}
},
resolveVision: (Name) -> Vision?, resolveVision: (Name) -> Vision?,
) { ) {
require(WebSockets) require(WebSockets)
@ -96,11 +98,11 @@ public fun Application.serveVisionData(
launch { launch {
for (frame in incoming) { for (frame in incoming) {
val data = frame.data.decodeToString() val data = frame.data.decodeToString()
application.log.debug("Received update for $name: \n$data") application.log.debug("Received event for $name: \n$data")
val change = configuration.visionManager.jsonFormat.decodeFromString( val event = configuration.visionManager.jsonFormat.decodeFromString(
VisionChange.serializer(), data VisionEvent.serializer(), data
) )
vision.update(change) vision.onEvent(event)
} }
} }

View File

@ -3,7 +3,10 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.* import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild
import space.kscience.visionforge.static
public enum class CompositeType { public enum class CompositeType {
GROUP, // Dumb sum of meshes GROUP, // Dumb sum of meshes
@ -33,7 +36,7 @@ public inline fun MutableVisionContainer<Solid>.composite(
} }
val res = Composite(type, children[0], children[1]) val res = Composite(type, children[0], children[1])
res.properties.setProperty(Name.EMPTY, group.properties.own) res.properties.setMeta(Name.EMPTY, group.properties.own)
setChild(name, res) setChild(name, res)
return res return res

View File

@ -9,37 +9,8 @@ import space.kscience.kmath.geometry.component2
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild import space.kscience.visionforge.setChild
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
public typealias Shape2D = List<Float32Vector2D>
@Serializable
public class Shape2DBuilder(private val points: ArrayList<Float32Vector2D> = ArrayList()) {
public fun point(x: Number, y: Number) {
points.add(Float32Vector2D(x, y))
}
public fun build(): Shape2D = points
}
public fun Shape2DBuilder.polygon(vertices: Int, radius: Number) {
require(vertices > 2) { "Polygon must have more than 2 vertices" }
val angle = 2 * PI / vertices
for (i in 0 until vertices) {
point(radius.toDouble() * cos(angle * i), radius.toDouble() * sin(angle * i))
}
}
/**
* A layer for extruded shape
*/
@Serializable
public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Float)
/** /**
* An extruded shape with the same number of points on each layer. * An extruded shape with the same number of points on each layer.
*/ */
@ -50,6 +21,12 @@ public class Extruded(
public val layers: List<Layer>, public val layers: List<Layer>,
) : SolidBase<Extruded>(), GeometrySolid { ) : SolidBase<Extruded>(), GeometrySolid {
/**
* A layer for extruded shape
*/
@Serializable
public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Float)
init { init {
require(shape.size > 2) { "Extruded shape requires more than 2 points per layer" } require(shape.size > 2) { "Extruded shape requires more than 2 points per layer" }
} }
@ -72,6 +49,8 @@ public class Extruded(
var lowerLayer = layers.first() var lowerLayer = layers.first()
var upperLayer: List<Float32Vector3D> var upperLayer: List<Float32Vector3D>
geometryBuilder.cap(layers.first().reversed())
for (i in (1 until layers.size)) { for (i in (1 until layers.size)) {
upperLayer = layers[i] upperLayer = layers[i]
for (j in (0 until shape.size - 1)) { for (j in (0 until shape.size - 1)) {
@ -93,7 +72,7 @@ public class Extruded(
) )
lowerLayer = upperLayer lowerLayer = upperLayer
} }
geometryBuilder.cap(layers.first().reversed())
geometryBuilder.cap(layers.last()) geometryBuilder.cap(layers.last())
} }
@ -102,16 +81,18 @@ public class Extruded(
public var layers: MutableList<Layer> = ArrayList(), public var layers: MutableList<Layer> = ArrayList(),
public val properties: MutableMeta = MutableMeta(), public val properties: MutableMeta = MutableMeta(),
) { ) {
@VisionBuilder
public fun shape(block: Shape2DBuilder.() -> Unit) { public fun shape(block: Shape2DBuilder.() -> Unit) {
this.shape = Shape2DBuilder().apply(block).build() this.shape = Shape2DBuilder().apply(block).build()
} }
@VisionBuilder
public fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) { public fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) {
layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat())) layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat()))
} }
internal fun build(): Extruded = Extruded(shape, layers).apply { internal fun build(): Extruded = Extruded(shape, layers).apply {
this.properties.setProperty(Name.EMPTY, this@Builder.properties) this.properties.setMeta(Name.EMPTY, this@Builder.properties)
} }
} }

View File

@ -13,7 +13,10 @@ import space.kscience.visionforge.setChild
public class PolyLine(public val points: List<Float32Vector3D>) : SolidBase<PolyLine>() { public class PolyLine(public val points: List<Float32Vector3D>) : SolidBase<PolyLine>() {
//var lineType by string() //var lineType by string()
public var thickness: Number by properties.root(inherit = false, includeStyles = true).number { DEFAULT_THICKNESS } public var thickness: Number by properties.root(
inherit = false,
includeStyles = true
).number { DEFAULT_THICKNESS }
public companion object { public companion object {
public const val DEFAULT_THICKNESS: Double = 1.0 public const val DEFAULT_THICKNESS: Double = 1.0

View File

@ -0,0 +1,26 @@
package space.kscience.visionforge.solid
import kotlinx.serialization.Serializable
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
public typealias Shape2D = List<Float32Vector2D>
@Serializable
public class Shape2DBuilder(private val points: ArrayList<Float32Vector2D> = ArrayList()) {
public fun point(x: Number, y: Number) {
points.add(Float32Vector2D(x, y))
}
public fun build(): Shape2D = points
}
public fun Shape2DBuilder.polygon(vertices: Int, radius: Number) {
require(vertices > 2) { "Polygon must have more than 2 vertices" }
val angle = 2 * PI / vertices
for (i in 0 until vertices) {
point(radius.toDouble() * cos(angle * i), radius.toDouble() * sin(angle * i))
}
}

View File

@ -11,8 +11,10 @@ import space.kscience.dataforge.names.plus
import space.kscience.kmath.complex.Quaternion import space.kscience.kmath.complex.Quaternion
import space.kscience.kmath.complex.QuaternionField import space.kscience.kmath.complex.QuaternionField
import space.kscience.kmath.geometry.* import space.kscience.kmath.geometry.*
import space.kscience.visionforge.* import space.kscience.visionforge.Vision
import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY
import space.kscience.visionforge.hide
import space.kscience.visionforge.inherited
import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY
import space.kscience.visionforge.solid.Solid.Companion.IGNORE_KEY import space.kscience.visionforge.solid.Solid.Companion.IGNORE_KEY
import space.kscience.visionforge.solid.Solid.Companion.LAYER_KEY import space.kscience.visionforge.solid.Solid.Companion.LAYER_KEY
@ -182,7 +184,7 @@ internal fun point(
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Float32Vector3D?) { override fun setValue(thisRef: Solid, property: KProperty<*>, value: Float32Vector3D?) {
if (value == null) { if (value == null) {
thisRef.properties.setProperty(name, null) thisRef.properties.setMeta(name, null)
} else { } else {
thisRef.properties[name + X_KEY] = value.x thisRef.properties[name + X_KEY] = value.x
thisRef.properties[name + Y_KEY] = value.y thisRef.properties[name + Y_KEY] = value.y

View File

@ -107,12 +107,12 @@ public val Solid.color: ColorAccessor
get() = ColorAccessor(properties.root(true), MATERIAL_COLOR_KEY) get() = ColorAccessor(properties.root(true), MATERIAL_COLOR_KEY)
public var Solid.material: SolidMaterial? public var Solid.material: SolidMaterial?
get() = SolidMaterial.read(properties.getProperty(MATERIAL_KEY)) get() = SolidMaterial.read(properties.getMeta(MATERIAL_KEY))
set(value) = properties.setProperty(MATERIAL_KEY, value?.meta) set(value) = properties.setMeta(MATERIAL_KEY, value?.meta)
@VisionBuilder @VisionBuilder
public fun Solid.material(builder: SolidMaterial.() -> Unit) { public fun Solid.material(builder: SolidMaterial.() -> Unit) {
properties.getProperty(MATERIAL_KEY).updateWith(SolidMaterial, builder) properties.getMeta(MATERIAL_KEY).updateWith(SolidMaterial, builder)
} }
public var Solid.opacity: Number? public var Solid.opacity: Number?
@ -125,5 +125,5 @@ public var Solid.opacity: Number?
@VisionBuilder @VisionBuilder
public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit = {}) { public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit = {}) {
properties[SolidMaterial.EDGES_ENABLED_KEY] = enabled properties[SolidMaterial.EDGES_ENABLED_KEY] = enabled
SolidMaterial.write(properties.getProperty(SolidMaterial.EDGES_MATERIAL_KEY)).apply(block) SolidMaterial.write(properties.getMeta(SolidMaterial.EDGES_MATERIAL_KEY)).apply(block)
} }

View File

@ -14,7 +14,6 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
import space.kscience.visionforge.solid.SolidReference.Companion.REFERENCE_CHILD_PROPERTY_PREFIX import space.kscience.visionforge.solid.SolidReference.Companion.REFERENCE_CHILD_PROPERTY_PREFIX
@ -162,7 +161,7 @@ internal class SolidReferenceChild(
override val properties: MutableVisionProperties = object : MutableVisionProperties { override val properties: MutableVisionProperties = object : MutableVisionProperties {
override val descriptor: MetaDescriptor get() = this@SolidReferenceChild.descriptor override val descriptor: MetaDescriptor get() = this@SolidReferenceChild.descriptor
override val own: MutableMeta by lazy { owner.properties.getProperty(childToken(childName).asName()) } override val own: MutableMeta by lazy { owner.properties.getMeta(childToken(childName).asName()) }
override fun getValue( override fun getValue(
name: Name, name: Name,
@ -170,7 +169,7 @@ internal class SolidReferenceChild(
includeStyles: Boolean?, includeStyles: Boolean?,
): Value? = own.getValue(name) ?: prototype.properties.getValue(name, inherit, includeStyles) ): Value? = own.getValue(name) ?: prototype.properties.getValue(name, inherit, includeStyles)
override fun setProperty(name: Name, node: Meta?, notify: Boolean) { override fun setMeta(name: Name, node: Meta?, notify: Boolean) {
own.setMeta(name, node) own.setMeta(name, node)
} }
@ -185,20 +184,6 @@ internal class SolidReferenceChild(
} }
} }
override fun update(change: VisionChange) {
change.children?.forEach { (name, change) ->
when {
change.vision == NullVision -> error("Deleting children inside ref is not allowed.")
change.vision != null -> error("Updating content of the ref is not allowed")
else -> children.getChild(name)?.update(change)
}
}
change.properties?.let {
updateProperties(it, Name.EMPTY)
}
}
override val children: VisionChildren = object : VisionChildren { override val children: VisionChildren = object : VisionChildren {
override val parent: Vision get() = this@SolidReferenceChild override val parent: Vision get() = this@SolidReferenceChild

View File

@ -41,6 +41,7 @@ public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Sol
subclass(ConeSurface.serializer()) subclass(ConeSurface.serializer())
subclass(Convex.serializer()) subclass(Convex.serializer())
subclass(Extruded.serializer()) subclass(Extruded.serializer())
subclass(Surface.serializer())
subclass(PolyLine.serializer()) subclass(PolyLine.serializer())
subclass(SolidLabel.serializer()) subclass(SolidLabel.serializer())
subclass(Sphere.serializer()) subclass(Sphere.serializer())

View File

@ -2,9 +2,14 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.names.Name
import space.kscience.kmath.geometry.component1 import space.kscience.kmath.geometry.component1
import space.kscience.kmath.geometry.component2 import space.kscience.kmath.geometry.component2
import space.kscience.kmath.structures.Float32 import space.kscience.kmath.structures.Float32
import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild
private inline fun <T> Iterable<T>.sumOf(selector: (T) -> Float32): Float32 { private inline fun <T> Iterable<T>.sumOf(selector: (T) -> Float32): Float32 {
@ -20,9 +25,9 @@ private inline fun <T> Iterable<T>.sumOf(selector: (T) -> Float32): Float32 {
*/ */
@Serializable @Serializable
@SerialName("solid.surface") @SerialName("solid.surface")
public class LayersSurface( public class Surface(
public val layers: List<Layer>, public val layers: List<Layer>,
) : SolidBase<Extruded>(), GeometrySolid { ) : SolidBase<Surface>(), GeometrySolid {
@Serializable @Serializable
public data class Layer(val z: Float32, val outer: Shape2D, val inner: Shape2D?) { public data class Layer(val z: Float32, val outer: Shape2D, val inner: Shape2D?) {
@ -130,7 +135,39 @@ public class LayersSurface(
} }
} }
public class Builder(
public var layers: MutableList<Layer> = ArrayList(),
public val properties: MutableMeta = MutableMeta(),
) {
public fun layer(
z: Number,
innerBuilder: (Shape2DBuilder.() -> Unit)? = null,
outerBuilder: Shape2DBuilder.() -> Unit,
) {
layers.add(
Layer(
z.toFloat(),
outer = Shape2DBuilder().apply(outerBuilder).build(),
inner = innerBuilder?.let { Shape2DBuilder().apply(innerBuilder).build() }
)
)
}
internal fun build(): Surface = Surface(layers).apply {
properties.setMeta(Name.EMPTY, this@Builder.properties)
}
}
public companion object { public companion object {
public const val TYPE: String = "solid.surface" public const val TYPE: String = "solid.surface"
} }
} }
@VisionBuilder
public fun MutableVisionContainer<Solid>.surface(
name: String? = null,
action: Surface.Builder.() -> Unit = {},
): Surface = Surface.Builder().apply(action).build().also { setChild(name, it) }

View File

@ -19,7 +19,7 @@ internal fun Solid.updateFrom(other: Solid): Solid {
scaleX *= other.scaleX scaleX *= other.scaleX
scaleY *= other.scaleY scaleY *= other.scaleY
scaleZ *= other.scaleZ scaleZ *= other.scaleZ
properties.setProperty(Name.EMPTY, other.properties.root()) properties.setMeta(Name.EMPTY, other.properties.root())
return this return this
} }

View File

@ -5,6 +5,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.set
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.* import space.kscience.visionforge.*

View File

@ -24,7 +24,7 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
} }
val material = ThreeMaterials.getLineMaterial( val material = ThreeMaterials.getLineMaterial(
vision.properties.getProperty(SolidMaterial.MATERIAL_KEY), vision.properties.getMeta(SolidMaterial.MATERIAL_KEY),
false false
) )

View File

@ -83,7 +83,7 @@ public object ThreeMaterials {
private val visionMaterialCache = HashMap<Vision, Material>() private val visionMaterialCache = HashMap<Vision, Material>()
internal fun cacheMaterial(vision: Vision): Material = visionMaterialCache.getOrPut(vision) { internal fun cacheMaterial(vision: Vision): Material = visionMaterialCache.getOrPut(vision) {
buildMaterial(vision.properties.getProperty(SolidMaterial.MATERIAL_KEY)).apply { buildMaterial(vision.properties.getMeta(SolidMaterial.MATERIAL_KEY)).apply {
cached = true cached = true
} }
} }
@ -133,11 +133,11 @@ public fun Mesh.setMaterial(vision: Vision) {
} else { } else {
material = vision.parent?.let { parent -> material = vision.parent?.let { parent ->
//TODO cache parent material //TODO cache parent material
ThreeMaterials.buildMaterial(parent.properties.getProperty(SolidMaterial.MATERIAL_KEY)) ThreeMaterials.buildMaterial(parent.properties.getMeta(SolidMaterial.MATERIAL_KEY))
} ?: ThreeMaterials.cacheMaterial(vision) } ?: ThreeMaterials.cacheMaterial(vision)
} }
} else { } else {
material = ThreeMaterials.buildMaterial(vision.properties.getProperty(SolidMaterial.MATERIAL_KEY)) material = ThreeMaterials.buildMaterial(vision.properties.getMeta(SolidMaterial.MATERIAL_KEY))
} }
} }
@ -153,18 +153,18 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
when (propertyName) { when (propertyName) {
SolidMaterial.MATERIAL_COLOR_KEY -> { SolidMaterial.MATERIAL_COLOR_KEY -> {
material.asDynamic().color = material.asDynamic().color =
vision.properties.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).threeColor() vision.properties.getMeta(SolidMaterial.MATERIAL_COLOR_KEY).threeColor()
?: ThreeMaterials.DEFAULT_COLOR ?: ThreeMaterials.DEFAULT_COLOR
} }
SolidMaterial.SPECULAR_COLOR_KEY -> { SolidMaterial.SPECULAR_COLOR_KEY -> {
material.asDynamic().specular = material.asDynamic().specular =
vision.properties.getProperty(SolidMaterial.SPECULAR_COLOR_KEY).threeColor() vision.properties.getMeta(SolidMaterial.SPECULAR_COLOR_KEY).threeColor()
?: ThreeMaterials.DEFAULT_COLOR ?: ThreeMaterials.DEFAULT_COLOR
} }
SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY -> { SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY -> {
material.asDynamic().emissive = vision.properties.getProperty(SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY) material.asDynamic().emissive = vision.properties.getMeta(SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY)
.threeColor() .threeColor()
?: ThreeMaterials.BLACK_COLOR ?: ThreeMaterials.BLACK_COLOR
} }

View File

@ -76,7 +76,7 @@ public fun Mesh.applyEdges(vision: Solid) {
val edges = children.find { it.name == EDGES_OBJECT_NAME } as? LineSegments val edges = children.find { it.name == EDGES_OBJECT_NAME } as? LineSegments
//inherited edges definition, enabled by default //inherited edges definition, enabled by default
if (vision.properties.getValue(EDGES_ENABLED_KEY, inherit = false)?.boolean != false) { if (vision.properties.getValue(EDGES_ENABLED_KEY, inherit = false)?.boolean != false) {
val material = ThreeMaterials.getLineMaterial(vision.properties.getProperty(EDGES_MATERIAL_KEY), true) val material = ThreeMaterials.getLineMaterial(vision.properties.getMeta(EDGES_MATERIAL_KEY), true)
if (edges == null) { if (edges == null) {
add( add(
LineSegments( LineSegments(