Readme update #25 #26

Merged
pklimai merged 5 commits from readme-update into dev 2020-05-08 13:27:50 +03:00
136 changed files with 8053 additions and 2094 deletions
Showing only changes of commit dfd8cc5d54 - Show all commits

View File

@ -1,13 +1,10 @@
import scientifik.useSerialization import scientifik.fx
import scientifik.serialization
val dataforgeVersion by extra("0.1.5-dev-6") val dataforgeVersion by extra("0.1.7")
plugins { plugins {
val kotlinVersion = "1.3.61" val toolsVersion = "0.4.2"
val toolsVersion = "0.3.2"
kotlin("jvm") version kotlinVersion apply false
id("kotlin-dce-js") version kotlinVersion apply false
id("scientifik.mpp") version toolsVersion apply false id("scientifik.mpp") version toolsVersion apply false
id("scientifik.jvm") version toolsVersion apply false id("scientifik.jvm") version toolsVersion apply false
id("scientifik.js") version toolsVersion apply false id("scientifik.js") version toolsVersion apply false
@ -21,21 +18,23 @@ allprojects {
maven("https://dl.bintray.com/pdvrieze/maven") maven("https://dl.bintray.com/pdvrieze/maven")
maven("http://maven.jzy3d.org/releases") maven("http://maven.jzy3d.org/releases")
maven("https://kotlin.bintray.com/js-externals") maven("https://kotlin.bintray.com/js-externals")
maven("https://kotlin.bintray.com/kotlin-js-wrappers/")
// maven("https://dl.bintray.com/gbaldeck/kotlin") // maven("https://dl.bintray.com/gbaldeck/kotlin")
// maven("https://dl.bintray.com/rjaros/kotlin") // maven("https://dl.bintray.com/rjaros/kotlin")
} }
group = "hep.dataforge" group = "hep.dataforge"
version = "0.1.1-dev" version = "0.1.3-dev"
}
subprojects{
this.useSerialization()
} }
val githubProject by extra("dataforge-vis") val githubProject by extra("dataforge-vis")
val bintrayRepo by extra("dataforge") val bintrayRepo by extra("dataforge")
val fxVersion by extra("14")
subprojects { subprojects {
apply(plugin = "scientifik.publish") apply(plugin = "scientifik.publish")
serialization()
afterEvaluate {
fx(scientifik.FXModule.CONTROLS, version = fxVersion)
}
} }

View File

@ -1,50 +1,47 @@
import org.openjfx.gradle.JavaFXOptions
import scientifik.useSerialization
plugins { plugins {
id("scientifik.mpp") id("scientifik.mpp")
id("org.openjfx.javafxplugin")
} }
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
//val kvisionVersion: String by rootProject.extra("2.0.0-M1") //val kvisionVersion: String by rootProject.extra("2.0.0-M1")
useSerialization()
kotlin { kotlin {
jvm{
withJava()
}
sourceSets { sourceSets {
commonMain{ commonMain {
dependencies { dependencies {
api("hep.dataforge:dataforge-output:$dataforgeVersion") api("hep.dataforge:dataforge-output:$dataforgeVersion")
} }
} }
jvmMain{ jvmMain {
dependencies { dependencies {
api("no.tornado:tornadofx:1.7.19") api("no.tornado:tornadofx:1.7.20")
//api("no.tornado:tornadofx-controlsfx:0.1.1") //api("no.tornado:tornadofx-controlsfx:0.1.1")
api("de.jensd:fontawesomefx-fontawesome:4.7.0-11"){ api("de.jensd:fontawesomefx-fontawesome:4.7.0-11") {
exclude(group = "org.openjfx") exclude(group = "org.openjfx")
} }
api("de.jensd:fontawesomefx-commons:11.0"){ api("de.jensd:fontawesomefx-commons:11.0") {
exclude(group = "org.openjfx") exclude(group = "org.openjfx")
} }
} }
} }
jsMain{ jsMain {
dependencies { dependencies {
api("hep.dataforge:dataforge-output-html:$dataforgeVersion") api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
//api(npm("bootstrap","4.4.1"))
implementation(npm("jsoneditor")) //React, React DOM + Wrappers (chapter 3)
implementation(npm("file-saver")) api("org.jetbrains:kotlin-react:16.13.0-pre.94-kotlin-1.3.70")
api("org.jetbrains:kotlin-react-dom:16.13.0-pre.94-kotlin-1.3.70")
api(npm("react", "16.13.0"))
api(npm("react-dom", "16.13.0"))
//Kotlin Styled (chapter 3)
api("org.jetbrains:kotlin-styled:1.0.0-pre.94-kotlin-1.3.70")
api(npm("styled-components"))
api(npm("inline-style-prefixer"))
api(npm("source-map-resolve","0.6.0"))
api(npm("file-saver","2.0.2"))
} }
} }
} }
} }
configure<JavaFXOptions> {
modules("javafx.controls")
}

View File

@ -1,4 +1,4 @@
package hep.dataforge.vis.common package hep.dataforge.vis
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name import hep.dataforge.names.Name
@ -20,6 +20,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
*/ */
abstract override val children: Map<NameToken, VisualObject> abstract override val children: Map<NameToken, VisualObject>
abstract override var styleSheet: StyleSheet?
protected set
/**
* Update or create stylesheet
*/
fun styleSheet(block: StyleSheet.() -> Unit) {
val res = styleSheet ?: StyleSheet(this).also { styleSheet = it }
res.block()
}
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) { override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
super.propertyChanged(name, before, after) super.propertyChanged(name, before, after)
forEach { forEach {
@ -37,7 +48,12 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
* Add listener for children change * Add listener for children change
*/ */
override fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit) { override fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit) {
structureChangeListeners.add(StructureChangeListeners(owner, action)) structureChangeListeners.add(
StructureChangeListeners(
owner,
action
)
)
} }
/** /**
@ -68,12 +84,41 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
* 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
*/ */
protected open fun addStatic(child: VisualObject) = protected open fun addStatic(child: VisualObject) =
setChild(NameToken("@static(${child.hashCode()})"), child) set(NameToken("@static(${child.hashCode()})").asName(), child)
protected abstract fun createGroup(): AbstractVisualGroup
/**
* Set this node as parent for given node
*/
protected fun attach(child: VisualObject) {
if (child.parent == null) {
child.parent = this
} else if (child.parent !== this) {
error("Can't reassign existing parent for $child")
}
}
/** /**
* Recursively create a child group * Recursively create a child group
*/ */
protected abstract fun createGroup(name: Name): MutableVisualGroup private fun createGroups(name: Name): AbstractVisualGroup {
return when {
name.isEmpty() -> error("Should be unreachable")
name.length == 1 -> {
val token = name.first()!!
when (val current = children[token]) {
null -> createGroup().also { child ->
attach(child)
setChild(token, child)
}
is AbstractVisualGroup -> current
else -> error("Can't create group with name $name because it exists and not a group")
}
}
else -> createGroups(name.first()!!.asName()).createGroups(name.cutFirst())
}
}
/** /**
* Add named or unnamed child to the group. If key is null the child is considered unnamed. Both key and value are not * Add named or unnamed child to the group. If key is null the child is considered unnamed. Both key and value are not
@ -91,16 +136,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
if (child == null) { if (child == null) {
removeChild(token) removeChild(token)
} else { } else {
attach(child)
setChild(token, child) setChild(token, child)
} }
} }
else -> { else -> {
//TODO add safety check //TODO add safety check
val parent = (get(name.cutLast()) as? MutableVisualGroup) ?: createGroup(name.cutLast()) val parent = (get(name.cutLast()) as? MutableVisualGroup) ?: createGroups(name.cutLast())
parent[name.last()!!.asName()] = child parent[name.last()!!.asName()] = child
} }
} }
structureChangeListeners.forEach { it.callback(name, child) } childrenChanged(name, child)
} }
} }

View File

@ -1,9 +1,10 @@
package hep.dataforge.vis.common package hep.dataforge.vis
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.vis.common.VisualObject.Companion.STYLE_KEY import hep.dataforge.values.Value
import hep.dataforge.vis.VisualObject.Companion.STYLE_KEY
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
internal data class PropertyListener( internal data class PropertyListener(
@ -14,7 +15,7 @@ internal data class PropertyListener(
abstract class AbstractVisualObject : VisualObject { abstract class AbstractVisualObject : VisualObject {
@Transient @Transient
override var parent: VisualObject? = null override var parent: VisualGroup? = null
protected abstract var properties: Config? protected abstract var properties: Config?
@ -22,7 +23,7 @@ abstract class AbstractVisualObject : VisualObject {
get() = properties?.get(STYLE_KEY).stringList get() = properties?.get(STYLE_KEY).stringList
set(value) { set(value) {
//val allStyles = (field + value).distinct() //val allStyles = (field + value).distinct()
setProperty(STYLE_KEY, value) setProperty(STYLE_KEY, Value.of(value))
updateStyles(value) updateStyles(value)
} }
@ -66,7 +67,7 @@ abstract class AbstractVisualObject : VisualObject {
private var styleCache: Meta? = null private var styleCache: Meta? = null
/** /**
* Collect all styles for this object in a laminate * Collect all styles for this object in a single cached meta
*/ */
protected val mergedStyles: Meta protected val mergedStyles: Meta
get() = styleCache ?: findAllStyles().merge().also { get() = styleCache ?: findAllStyles().merge().also {

View File

@ -1,6 +1,9 @@
package hep.dataforge.vis.common package hep.dataforge.vis
import hep.dataforge.meta.* import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.get
import hep.dataforge.meta.number
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.values.int import hep.dataforge.values.int
import kotlin.math.max import kotlin.math.max
@ -234,7 +237,7 @@ 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 = buildMeta { 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

@ -0,0 +1,31 @@
package hep.dataforge.vis
import hep.dataforge.meta.Config
import hep.dataforge.names.NameToken
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("group")
class SimpleVisualGroup : AbstractVisualGroup() {
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")
private val _children = HashMap<NameToken, VisualObject>()
override val children: Map<NameToken, VisualObject> get() = _children
override fun removeChild(token: NameToken) {
_children.remove(token)?.apply { parent = null }
}
override fun setChild(token: NameToken, child: VisualObject) {
_children[token] = child
}
override fun createGroup(): SimpleVisualGroup = SimpleVisualGroup()
}

View File

@ -1,18 +1,19 @@
@file:UseSerializers(MetaSerializer::class) @file:UseSerializers(MetaSerializer::class)
package hep.dataforge.vis.common package hep.dataforge.vis
import hep.dataforge.io.serialization.MetaSerializer
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
/** /**
* A container for styles * A container for styles
*/ */
@Serializable @Serializable
class StyleSheet() { class StyleSheet private constructor(private val styleMap: MutableMap<String, Meta> = LinkedHashMap()) {
@Transient @Transient
internal var owner: VisualObject? = null internal var owner: VisualObject? = null
@ -20,12 +21,10 @@ class StyleSheet() {
this.owner = owner this.owner = owner
} }
private val styleMap = HashMap<String, Meta>()
val items: Map<String, Meta> get() = styleMap val items: Map<String, Meta> get() = styleMap
operator fun get(key: String): Meta? { operator fun get(key: String): Meta? {
return styleMap[key] ?: (owner?.parent as? VisualGroup)?.styleSheet?.get(key) return styleMap[key] ?: owner?.parent?.styleSheet?.get(key)
} }
/** /**
@ -46,20 +45,23 @@ class StyleSheet() {
} }
operator fun set(key: String, builder: MetaBuilder.() -> Unit) { operator fun set(key: String, builder: MetaBuilder.() -> Unit) {
val newStyle = get(key)?.let { buildMeta(it, builder) } ?: buildMeta(builder) val newStyle = get(key)?.edit(builder) ?: Meta(builder)
set(key, newStyle.seal()) set(key, newStyle.seal())
} }
companion object: KSerializer<StyleSheet>{ @Serializer(StyleSheet::class)
override val descriptor: SerialDescriptor companion object : KSerializer<StyleSheet> {
get() = TODO("Not yet implemented") private val mapSerializer = MapSerializer(String.serializer(), MetaSerializer)
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
override fun deserialize(decoder: Decoder): StyleSheet { override fun deserialize(decoder: Decoder): StyleSheet {
TODO("Not yet implemented") val map = mapSerializer.deserialize(decoder)
return StyleSheet(map as? MutableMap<String, Meta> ?: LinkedHashMap(map))
} }
override fun serialize(encoder: Encoder, obj: StyleSheet) { override fun serialize(encoder: Encoder, value: StyleSheet) {
TODO("Not yet implemented") mapSerializer.serialize(encoder, value.items)
} }
} }

View File

@ -1,4 +1,4 @@
package hep.dataforge.vis.common package hep.dataforge.vis
import hep.dataforge.context.* import hep.dataforge.context.*
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
@ -31,7 +31,8 @@ class Visual(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag = PluginTag(name = "visual", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag(name = "visual", group = PluginTag.DATAFORGE_GROUP)
override val type: KClass<out Visual> = Visual::class override val type: KClass<out Visual> = Visual::class
override fun invoke(meta: Meta, context: Context): Visual = Visual(meta) override fun invoke(meta: Meta, context: Context): Visual =
Visual(meta)
const val VISUAL_FACTORY_TYPE = "visual.factory" const val VISUAL_FACTORY_TYPE = "visual.factory"
} }

View File

@ -1,4 +1,4 @@
package hep.dataforge.vis.common package hep.dataforge.vis
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.provider.Provider import hep.dataforge.provider.Provider
@ -6,7 +6,8 @@ import hep.dataforge.provider.Provider
/** /**
* Represents a group of [VisualObject] instances * Represents a group of [VisualObject] instances
*/ */
interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject { interface VisualGroup : Provider, Iterable<VisualObject>,
VisualObject {
/** /**
* A map of top level named children * A map of top level named children
*/ */

View File

@ -1,11 +1,15 @@
package hep.dataforge.vis.common package hep.dataforge.vis
import hep.dataforge.meta.* import hep.dataforge.meta.Configurable
import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject.Companion.TYPE import hep.dataforge.vis.VisualObject.Companion.TYPE
import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
//private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) //private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers)
@ -21,24 +25,19 @@ interface VisualObject : Configurable {
* The parent object of this one. If null, this one is a root. * The parent object of this one. If null, this one is a root.
*/ */
@Transient @Transient
var parent: VisualObject? var parent: VisualGroup?
/** /**
* All properties including styles and prototypes if present, but without inheritance * All properties including styles and prototypes if present, but without inheritance
*/ */
fun allProperties(): Laminate fun allProperties(): Laminate
/**
* Set property for this object
*/
fun setProperty(name: Name, value: Any?) {
config[name] = value
}
/** /**
* Get property including or excluding parent properties * Get property including or excluding parent properties
*/ */
fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>? fun getProperty(name: Name, inherit: Boolean): MetaItem<*>?
override fun getProperty(name: Name): MetaItem<*>? = getProperty(name, true)
/** /**
* Trigger property invalidation event. If [name] is empty, notify that the whole object is changed * Trigger property invalidation event. If [name] is empty, notify that the whole object is changed
@ -66,6 +65,10 @@ interface VisualObject : Configurable {
const val TYPE = "visual" const val TYPE = "visual"
val STYLE_KEY = "@style".asName() val STYLE_KEY = "@style".asName()
private val VISUAL_OBJECT_SERIALIZER = PolymorphicSerializer(VisualObject::class)
fun serializer() = VISUAL_OBJECT_SERIALIZER
//const val META_KEY = "@meta" //const val META_KEY = "@meta"
//const val TAGS_KEY = "@tags" //const val TAGS_KEY = "@tags"
@ -77,11 +80,6 @@ interface VisualObject : Configurable {
*/ */
fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit) fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit)
/**
* Set [VisualObject] property using key as a String
*/
fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value)
/** /**
* 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

@ -1,4 +1,4 @@
package hep.dataforge.vis.common package hep.dataforge.vis
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
@ -105,8 +105,8 @@ fun VisualObject.int(default: Int, name: Name? = null, inherited: Boolean = fals
inline fun <reified E : Enum<E>> VisualObject.enum(default: E, name: Name? = null, inherited: Boolean = false) = inline fun <reified E : Enum<E>> VisualObject.enum(default: E, name: Name? = null, inherited: Boolean = false) =
VisualObjectDelegateWrapper(this, name, default, inherited) { VisualObjectDelegateWrapper(this, name, default, inherited) { item ->
item -> item.string?.let { enumValueOf<E>(it) } item.string?.let { enumValueOf<E>(it) }
} }
//merge properties //merge properties

View File

@ -1,22 +0,0 @@
package hep.dataforge.vis.common
import hep.dataforge.descriptors.ValueDescriptor
import hep.dataforge.meta.*
/**
* Extension property to access the "widget" key of [ValueDescriptor]
*/
var ValueDescriptor.widget: Meta
get() = this.config["widget"].node?: EmptyMeta
set(value) {
this.config["widget"] = value
}
/**
* Extension property to access the "widget.type" key of [ValueDescriptor]
*/
var ValueDescriptor.widgetType: String?
get() = this["widget.type"].string
set(value) {
this.config["widget.type"] = value
}

View File

@ -0,0 +1,19 @@
package hep.dataforge.vis
import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty
/**
* Return nearest selectable parent [Name]
*/
tailrec fun Name.selectable(): Name? = when {
isEmpty() -> {
null
}
last()?.body?.startsWith("@") != true -> {
this
}
else -> {
cutLast().selectable()
}
}

View File

@ -0,0 +1,23 @@
package hep.dataforge.vis
import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.values.asValue
/**
* Extension property to access the "widget" key of [ValueDescriptor]
*/
var ValueDescriptor.widget: Meta
get() = getProperty("widget").node ?: Meta.EMPTY
set(value) {
setProperty("widget", value)
}
/**
* Extension property to access the "widget.type" key of [ValueDescriptor]
*/
var ValueDescriptor.widgetType: String?
get() = getProperty("widget.type").string
set(value) {
setProperty("widget.type", value?.asValue())
}

View File

@ -0,0 +1,121 @@
package hep.dataforge.js
import kotlinx.html.*
import kotlinx.html.js.div
import org.w3c.dom.HTMLElement
import react.RBuilder
import react.dom.*
inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {
div("card w-100") {
div("card-body") {
h3(classes = "card-title") { +title }
block()
}
}
}
inline fun RBuilder.card(title: String, crossinline block: RBuilder.() -> Unit) {
div("card w-100") {
div("card-body") {
h3(classes = "card-title") { +title }
block()
}
}
}
fun TagConsumer<HTMLElement>.accordion(id: String, elements: List<Pair<String, DIV.() -> Unit>>) {
div("container-fluid") {
div("accordion") {
this.id = id
elements.forEachIndexed { index, (title, builder) ->
val headerID = "${id}-${index}-heading"
val collapseID = "${id}-${index}-collapse"
div("card") {
div("card-header") {
this.id = headerID
h5("mb-0") {
button(classes = "btn btn-link collapsed", type = ButtonType.button) {
attributes["data-toggle"] = "collapse"
attributes["data-target"] = "#$collapseID"
attributes["aria-expanded"] = "false"
attributes["aria-controls"] = collapseID
+title
}
}
}
div("collapse") {
this.id = collapseID
attributes["aria-labelledby"] = headerID
attributes["data-parent"] = "#$id"
div("card-body", block = builder)
}
}
}
}
}
}
typealias AccordionBuilder = MutableList<Pair<String, DIV.() -> Unit>>
fun AccordionBuilder.entry(title: String, builder: DIV.() -> Unit) {
add(title to builder)
}
fun TagConsumer<HTMLElement>.accordion(id: String, builder: AccordionBuilder.() -> Unit) {
val list = ArrayList<Pair<String, DIV.() -> Unit>>().apply(builder)
accordion(id, list)
}
fun RBuilder.accordion(id: String, elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>) {
div("container-fluid") {
div("accordion") {
attrs {
this.id = id
}
elements.forEachIndexed { index, (title, builder) ->
val headerID = "${id}-${index}-heading"
val collapseID = "${id}-${index}-collapse"
div("card") {
div("card-header") {
attrs {
this.id = headerID
}
h5("mb-0") {
button(classes = "btn btn-link collapsed", type = ButtonType.button) {
attrs {
attributes["data-toggle"] = "collapse"
attributes["data-target"] = "#$collapseID"
attributes["aria-expanded"] = "false"
attributes["aria-controls"] = collapseID
}
+title
}
}
}
div("collapse") {
attrs {
this.id = collapseID
attributes["aria-labelledby"] = headerID
attributes["data-parent"] = "#$id"
}
div("card-body", block = builder)
}
}
}
}
}
}
typealias RAccordionBuilder = MutableList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>
fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder<DIV>.() -> Unit) {
add(title to builder)
}
fun RBuilder.accordion(id: String, builder: RAccordionBuilder.() -> Unit) {
val list = ArrayList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>().apply(builder)
accordion(id, list)
}

View File

@ -0,0 +1,18 @@
package hep.dataforge.js
import react.RBuilder
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
fun <T> RBuilder.initState(init: () -> T): ReadWriteProperty<Any?, T> =
object : ReadWriteProperty<Any?, T> {
val pair = react.useState(init)
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return pair.first
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
pair.second(value)
}
}

View File

@ -0,0 +1,280 @@
package hep.dataforge.vis.editor
import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.*
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.plus
import hep.dataforge.values.*
import hep.dataforge.vis.widgetType
import kotlinx.html.ButtonType
import kotlinx.html.InputType
import kotlinx.html.classes
import kotlinx.html.js.onChangeFunction
import kotlinx.html.js.onClickFunction
import org.w3c.dom.Element
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.events.Event
import react.RBuilder
import react.RComponent
import react.RProps
import react.dom.*
import react.setState
interface ConfigEditorProps : RProps {
/**
* Root config object - always non null
*/
var root: Config
/**
* Full path to the displayed node in [root]. Could be empty
*/
var name: Name
/**
* Root default
*/
var default: Meta?
/**
* Root descriptor
*/
var descriptor: NodeDescriptor?
}
class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
override fun TreeState.init() {
expanded = true
}
override fun componentDidMount() {
props.root.onChange(this) { name, _, _ ->
if (name == props.name) {
forceUpdate()
}
}
}
override fun componentWillUnmount() {
props.root.removeListener(this)
}
private val onClick: (Event) -> Unit = {
setState {
expanded = !expanded
}
}
private val onValueChange: (Event) -> Unit = {
val value = when (val t = it.target) {
// (it.target as HTMLInputElement).value
is HTMLInputElement -> if (t.type == "checkbox") {
if (t.checked) True else False
} else {
t.value.asValue()
}
is HTMLSelectElement -> t.value.asValue()
else -> error("Unknown event target: $t")
}
try {
props.root.setValue(props.name, value)
} catch (ex: Exception) {
console.error("Can't set config property ${props.name} to $value")
}
}
private val removeValue: (Event) -> Unit = {
props.root.remove(props.name)
}
//TODO replace by separate components
private fun RBuilder.valueChooser(value: Value, descriptor: ValueDescriptor?) {
val type = descriptor?.type?.firstOrNull()
when {
type == ValueType.BOOLEAN -> {
input(type = InputType.checkBox, classes = "float-right") {
attrs {
defaultChecked = value.boolean
onChangeFunction = onValueChange
}
}
}
type == ValueType.NUMBER -> input(type = InputType.number, classes = "float-right") {
attrs {
descriptor.attributes["step"].string?.let {
step = it
}
descriptor.attributes["min"].string?.let {
min = it
}
descriptor.attributes["max"].string?.let {
max = it
}
defaultValue = value.string
onChangeFunction = onValueChange
}
}
descriptor?.allowedValues?.isNotEmpty() ?: false -> select("float-right") {
descriptor!!.allowedValues.forEach {
option {
+it.string
}
}
attrs {
multiple = false
onChangeFunction = onValueChange
}
}
descriptor?.widgetType == "color" -> input(type = InputType.color, classes = "float-right") {
attrs {
defaultValue = value.string
onChangeFunction = onValueChange
}
}
else -> input(type = InputType.text, classes = "float-right") {
attrs {
defaultValue = value.string
onChangeFunction = onValueChange
}
}
}
}
override fun RBuilder.render() {
val item = props.root[props.name]
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
val defaultItem = props.default?.get(props.name)
val actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem()
val token = props.name.last()?.toString() ?: "Properties"
when (actualItem) {
is MetaItem.NodeItem -> {
div {
span("tree-caret") {
attrs {
if (state.expanded) {
classes += "tree-caret-down"
}
onClickFunction = onClick
}
}
span("tree-label") {
+token
attrs {
if (item == null) {
classes += "tree-label-inactive"
}
}
}
}
if (state.expanded) {
ul("tree") {
val keys = buildSet<NameToken> {
item?.node?.items?.keys?.let { addAll(it) }
defaultItem?.node?.items?.keys?.let { addAll(it) }
(descriptorItem as? NodeDescriptor)?.items?.keys?.forEach {
add(NameToken(it))
}
}
keys.forEach { token ->
li("tree-item") {
child(ConfigEditorComponent::class) {
attrs {
this.root = props.root
this.name = props.name + token
this.default = props.default
this.descriptor = props.descriptor
}
}
}
}
}
}
}
is MetaItem.ValueItem -> {
div {
div("row") {
div("col") {
p("tree-label") {
+token
attrs {
if (item == null) {
classes += "tree-label-inactive"
}
}
}
}
div("col") {
valueChooser(actualItem.value, descriptorItem as? ValueDescriptor)
}
div("col-auto") {
div("dropleft p-0") {
button(classes = "btn btn-outline-primary") {
attrs {
type = ButtonType.button
attributes["data-toggle"] = "dropdown"
attributes["aria-haspopup"] = "true"
attributes["aria-expanded"] = "false"
attributes["data-boundary"] = "viewport"
}
+"\u22ee"
}
div(classes = "dropdown-menu") {
button(classes = "btn btn-outline dropdown-item") {
+"Info"
}
if (item != null) {
button(classes = "btn btn-outline dropdown-item") {
+"""Clear"""
}
attrs {
onClickFunction = removeValue
}
}
}
}
}
}
}
}
}
}
}
fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) {
render(this) {
child(ConfigEditorComponent::class) {
attrs {
root = config
name = Name.EMPTY
this.descriptor = descriptor
this.default = default
}
}
}
}
fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) {
div {
child(ConfigEditorComponent::class) {
attrs {
root = config
name = Name.EMPTY
this.descriptor = descriptor
this.default = default
}
}
}
}
fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null) {
configEditor(obj.config, descriptor ?: obj.descriptor, default)
}

View File

@ -0,0 +1,84 @@
package hep.dataforge.vis.editor
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.NameToken
import kotlinx.html.classes
import kotlinx.html.js.onClickFunction
import org.w3c.dom.events.Event
import react.RBuilder
import react.RComponent
import react.RProps
import react.dom.*
import react.setState
interface MetaViewerProps : RProps {
var name: NameToken
var meta: Meta
var descriptor: NodeDescriptor?
}
class MetaViewerComponent : RComponent<MetaViewerProps, TreeState>() {
override fun TreeState.init() {
expanded = false
}
private val onClick: (Event) -> Unit = {
setState {
expanded = !expanded
}
}
override fun RBuilder.render() {
div("d-inline-block text-truncate") {
if (props.meta.items.isNotEmpty()) {
span("tree-caret") {
attrs {
if (state.expanded) {
classes += "tree-caret-down"
}
onClickFunction = onClick
}
}
}
label("tree-label") {
+props.name.toString()
}
ul("tree") {
props.meta.items.forEach { (token, item) ->
//val descriptor = props.
li {
when (item) {
is MetaItem.NodeItem -> {
child(MetaViewerComponent::class) {
attrs {
name = token
meta = item.node
descriptor = props.descriptor?.nodes?.get(token.body)
}
}
}
is MetaItem.ValueItem -> {
div("row") {
div("col") {
label("tree-label") {
+token.toString()
}
}
div("col") {
label {
+item.value.toString()
}
}
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,127 @@
package hep.dataforge.vis.editor
import hep.dataforge.js.card
import hep.dataforge.js.initState
import hep.dataforge.names.Name
import hep.dataforge.names.plus
import hep.dataforge.names.startsWith
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.isEmpty
import kotlinx.html.classes
import kotlinx.html.js.onClickFunction
import org.w3c.dom.Element
import org.w3c.dom.events.Event
import react.*
import react.dom.*
interface ObjectTreeProps : RProps {
var name: Name
var selected: Name?
var obj: VisualObject
var clickCallback: (Name) -> Unit
}
interface TreeState : RState {
var expanded: Boolean
}
private fun RBuilder.objectTree(props: ObjectTreeProps): Unit {
var expanded: Boolean by initState{ props.selected?.startsWith(props.name) ?: false }
val onClick: (Event) -> Unit = {
expanded = !expanded
}
fun RBuilder.treeLabel(text: String) {
button(classes = "btn btn-link tree-label p-0") {
+text
attrs {
if (props.name == props.selected) {
classes += "tree-label-selected"
}
onClickFunction = { props.clickCallback(props.name) }
}
}
}
val token = props.name.last()?.toString() ?: "World"
val obj = props.obj
//display as node if any child is visible
if (obj is VisualGroup) {
div("d-block text-truncate") {
if (obj.children.any { !it.key.body.startsWith("@") }) {
span("tree-caret") {
attrs {
if (expanded) {
classes += "tree-caret-down"
}
onClickFunction = onClick
}
}
}
treeLabel(token)
}
if (expanded) {
ul("tree") {
obj.children.entries
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
.sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true }
.forEach { (childToken, child) ->
li("tree-item") {
child(ObjectTree) {
attrs {
this.name = props.name + childToken
this.obj = child
this.selected = props.selected
this.clickCallback = props.clickCallback
}
}
}
}
}
}
} else {
div("d-block text-truncate") {
span("tree-leaf") {}
treeLabel(token)
}
}
}
val ObjectTree: FunctionalComponent<ObjectTreeProps> = functionalComponent { props ->
objectTree(props)
}
fun Element.renderObjectTree(
visualObject: VisualObject,
clickCallback: (Name) -> Unit = {}
) = render(this) {
card("Object tree") {
child(ObjectTree) {
attrs {
this.name = Name.EMPTY
this.obj = visualObject
this.selected = null
this.clickCallback = clickCallback
}
}
}
}
fun RBuilder.objectTree(
visualObject: VisualObject,
selected: Name? = null,
clickCallback: (Name) -> Unit = {}
) {
child(ObjectTree) {
attrs {
this.name = Name.EMPTY
this.obj = visualObject
this.selected = selected
this.clickCallback = clickCallback
}
}
}

View File

@ -0,0 +1,49 @@
package hep.dataforge.vis.editor
import hep.dataforge.js.card
import hep.dataforge.meta.Meta
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty
import hep.dataforge.vis.VisualObject
import org.w3c.dom.Element
import react.RBuilder
import react.dom.li
import react.dom.nav
import react.dom.ol
import react.dom.render
import kotlin.collections.set
fun RBuilder.visualPropertyEditor(
path: Name,
item: VisualObject,
descriptor: NodeDescriptor? = item.descriptor,
default: Meta? = null
) {
card("Properties") {
if (!path.isEmpty()) {
nav {
attrs {
attributes["aria-label"] = "breadcrumb"
}
ol("breadcrumb") {
path.tokens.forEach { token ->
li("breadcrumb-item") {
+token.toString()
}
}
}
}
}
configEditor(item, descriptor, default)
}
}
fun Element.visualPropertyEditor(
path: Name,
item: VisualObject,
descriptor: NodeDescriptor? = item.descriptor,
default: Meta? = null
) = render(this) {
this.visualPropertyEditor(path, item, descriptor, default)
}

View File

@ -0,0 +1,11 @@
package hep.dataforge.vis.editor
//val TextValueChooser = functionalComponent<ConfigEditorProps> {
//
// input(type = InputType.number, classes = "float-right") {
// attrs {
// defaultValue = value.string
// onChangeFunction = onValueChange
// }
// }
//}

View File

@ -1,61 +0,0 @@
package hep.dataforge.vis.js.editor
import kotlinx.html.*
import kotlinx.html.js.div
import org.w3c.dom.HTMLElement
inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {
div("card w-100") {
div("card-body") {
h3(classes = "card-title") { +title }
block()
}
}
}
fun TagConsumer<HTMLElement>.accordion(id: String, elements: Map<String, DIV.() -> Unit>) {
div("container-fluid") {
div("accordion") {
this.id = id
elements.entries.forEachIndexed { index, (title, builder) ->
val headerID = "${id}-${index}-heading"
val collapseID = "${id}-${index}-collapse"
div("card") {
div("card-header") {
this.id = headerID
h5("mb-0") {
button(classes = "btn btn-link collapsed", type = ButtonType.button) {
attributes["data-toggle"] = "collapse"
attributes["data-target"] = "#$collapseID"
attributes["aria-expanded"] = "false"
attributes["aria-controls"] = collapseID
+title
}
}
}
div("collapse") {
this.id = collapseID
attributes["aria-labelledby"] = headerID
attributes["data-parent"] = "#$id"
div("card-body", block = builder)
}
}
}
}
}
}
class AccordionBuilder {
private val map = HashMap<String, DIV.() -> Unit>()
fun entry(title: String, block: DIV.() -> Unit) {
map[title] = block
}
fun build(consumer: TagConsumer<HTMLElement>, id: String) {
consumer.accordion(id, map)
}
}
fun TagConsumer<HTMLElement>.accordion(id: String, block: AccordionBuilder.() -> Unit) {
AccordionBuilder().apply(block).build(this, id)
}

View File

@ -1,77 +0,0 @@
package hep.dataforge.vis.js.editor
import hep.dataforge.names.Name
import hep.dataforge.names.plus
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.isEmpty
import kotlinx.html.TagConsumer
import kotlinx.html.dom.append
import kotlinx.html.js.*
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLSpanElement
import kotlin.dom.clear
fun Element.displayObjectTree(
obj: VisualObject,
clickCallback: (Name) -> Unit = {}
) {
clear()
append {
card("Object tree") {
subTree(Name.EMPTY, obj, clickCallback)
}
}
}
private fun TagConsumer<HTMLElement>.subTree(
name: Name,
obj: VisualObject,
clickCallback: (Name) -> Unit
) {
val token = name.last()?.toString()?:"World"
//display as node if any child is visible
if (obj is VisualGroup && obj.children.keys.any { !it.body.startsWith("@") }) {
lateinit var toggle: HTMLSpanElement
div("d-inline-block text-truncate") {
toggle = span("objTree-caret")
label("objTree-label") {
+token
onClickFunction = { clickCallback(name) }
}
}
val subtree = ul("objTree-subtree")
toggle.onclick = {
toggle.classList.toggle("objTree-caret-down")
subtree.apply {
//If expanded, add children dynamically
if (toggle.classList.contains("objTree-caret-down")) {
obj.children.entries
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
.sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true }
.forEach { (childToken, child) ->
append {
li().apply {
subTree(name + childToken, child, clickCallback)
}
}
}
} else {
// if not, clear them to conserve memory on very long lists
this.clear()
}
}
}
} else {
div("d-inline-block text-truncate") {
span("objTree-leaf")
label("objTree-label") {
+token
onClickFunction = { clickCallback(name) }
}
}
}
}

View File

@ -1,185 +0,0 @@
@file:Suppress(
"INTERFACE_WITH_SUPERCLASS",
"OVERRIDING_FINAL_MEMBER",
"RETURN_TYPE_MISMATCH_ON_OVERRIDE",
"CONFLICTING_OVERLOADS",
"EXTERNAL_DELEGATION"
)
package hep.dataforge.vis.js.editor
import org.w3c.dom.HTMLElement
external interface Node {
var field: String
var value: String? get() = definedExternally; set(value) = definedExternally
var path: dynamic
}
external interface NodeName {
var path: Array<String>
var type: dynamic /* 'object' | 'array' */
var size: Number
}
external interface ValidationError {
var path: dynamic
var message: String
}
external interface Template {
var text: String
var title: String
var className: String? get() = definedExternally; set(value) = definedExternally
var field: String
var value: Any
}
external interface `T$6` {
var startFrom: Number
var options: Array<String>
}
external interface AutoCompleteOptions {
var confirmKeys: Array<Number>? get() = definedExternally; set(value) = definedExternally
var caseSensitive: Boolean? get() = definedExternally; set(value) = definedExternally
// var getOptions: AutoCompleteOptionsGetter? get() = definedExternally; set(value) = definedExternally
}
external interface SelectionPosition {
var row: Number
var column: Number
}
external interface SerializableNode {
var value: Any
var path: dynamic
}
external interface Color {
var rgba: Array<Number>
var hsla: Array<Number>
var rgbString: String
var rgbaString: String
var hslString: String
var hslaString: String
var hex: String
}
//external interface `T$0` {
// var field: Boolean
// var value: Boolean
//}
//
//external interface `T$1` {
// @nativeGetter
// operator fun get(key: String): String?
//
// @nativeSetter
// operator fun set(key: String, value: String)
//}
//external interface Languages {
// @nativeGetter
// operator fun get(lang: String): `T$1`?
//
// @nativeSetter
// operator fun set(lang: String, value: `T$1`)
//}
external interface JSONEditorOptions {
// var ace: AceAjax.Ace? get() = definedExternally; set(value) = definedExternally
// var ajv: Ajv? get() = definedExternally; set(value) = definedExternally
var onChange: (() -> Unit)? get() = definedExternally; set(value) = definedExternally
var onChangeJSON: ((json: Any) -> Unit)? get() = definedExternally; set(value) = definedExternally
var onChangeText: ((jsonString: String) -> Unit)? get() = definedExternally; set(value) = definedExternally
var onEditable: ((node: Node) -> dynamic)? get() = definedExternally; set(value) = definedExternally
var onError: ((error: Error) -> Unit)? get() = definedExternally; set(value) = definedExternally
var onModeChange: ((newMode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */, oldMode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */) -> Unit)? get() = definedExternally; set(value) = definedExternally
var onNodeName: ((nodeName: NodeName) -> String?)? get() = definedExternally; set(value) = definedExternally
var onValidate: ((json: Any) -> dynamic)? get() = definedExternally; set(value) = definedExternally
var escapeUnicode: Boolean? get() = definedExternally; set(value) = definedExternally
var sortObjectKeys: Boolean? get() = definedExternally; set(value) = definedExternally
var history: Boolean? get() = definedExternally; set(value) = definedExternally
var mode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */
var modes: Array<dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */>? get() = definedExternally; set(value) = definedExternally
var name: String? get() = definedExternally; set(value) = definedExternally
var schema: Any? get() = definedExternally; set(value) = definedExternally
var schemaRefs: Any? get() = definedExternally; set(value) = definedExternally
var search: Boolean? get() = definedExternally; set(value) = definedExternally
var indentation: Number? get() = definedExternally; set(value) = definedExternally
var theme: String? get() = definedExternally; set(value) = definedExternally
var templates: Array<Template>? get() = definedExternally; set(value) = definedExternally
var autocomplete: AutoCompleteOptions? get() = definedExternally; set(value) = definedExternally
var mainMenuBar: Boolean? get() = definedExternally; set(value) = definedExternally
var navigationBar: Boolean? get() = definedExternally; set(value) = definedExternally
var statusBar: Boolean? get() = definedExternally; set(value) = definedExternally
var onTextSelectionChange: ((start: SelectionPosition, end: SelectionPosition, text: String) -> Unit)? get() = definedExternally; set(value) = definedExternally
var onSelectionChange: ((start: SerializableNode, end: SerializableNode) -> Unit)? get() = definedExternally; set(value) = definedExternally
var onEvent: ((node: Node, event: String) -> Unit)? get() = definedExternally; set(value) = definedExternally
var colorPicker: Boolean? get() = definedExternally; set(value) = definedExternally
var onColorPicker: ((parent: HTMLElement, color: String, onChange: (color: Color) -> Unit) -> Unit)? get() = definedExternally; set(value) = definedExternally
var timestampTag: Boolean? get() = definedExternally; set(value) = definedExternally
var language: String? get() = definedExternally; set(value) = definedExternally
//var languages: Languages? get() = definedExternally; set(value) = definedExternally
var modalAnchor: HTMLElement? get() = definedExternally; set(value) = definedExternally
var enableSort: Boolean? get() = definedExternally; set(value) = definedExternally
var enableTransform: Boolean? get() = definedExternally; set(value) = definedExternally
var maxVisibleChilds: Number? get() = definedExternally; set(value) = definedExternally
}
external interface JsonPath {
var path: dynamic
}
external interface EditorSelection {
var start: SerializableNode
var end: SerializableNode
}
external interface TextSelection {
var start: SelectionPosition
var end: SelectionPosition
var text: String
}
@JsModule("jsoneditor")
@JsNonModule
external open class JSONEditor(
container: HTMLElement,
options: JSONEditorOptions? = definedExternally /* null */,
json: dynamic = definedExternally /* null */
) {
open fun collapseAll()
open fun destroy()
open fun expandAll()
open fun focus()
open fun get(): Any
open fun getMode(): dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */
open fun getName(): String?
open fun getNodesByRange(start: JsonPath, end: JsonPath): Array<SerializableNode>
open fun getSelection(): EditorSelection
open fun getText(): String
open fun getTextSelection(): TextSelection
open fun refresh()
open fun set(json: Any)
open fun setMode(mode: String /* 'tree' */)
open fun setMode(mode: String /* 'view' */)
open fun setMode(mode: String /* 'form' */)
open fun setMode(mode: String /* 'code' */)
open fun setMode(mode: String /* 'text' */)
open fun setName(name: String? = definedExternally /* null */)
open fun setSchema(schema: Any?, schemaRefs: Any? = definedExternally /* null */)
open fun setSelection(start: JsonPath, end: JsonPath)
open fun setText(jsonString: String)
open fun setTextSelection(start: SelectionPosition, end: SelectionPosition)
open fun update(json: Any)
open fun updateText(jsonString: String)
companion object {
var VALID_OPTIONS: Array<String>
// var ace: AceAjax.Ace
// var Ajv: Ajv
var VanillaPicker: Any
}
}

View File

@ -1,75 +0,0 @@
package hep.dataforge.vis.js.editor
import hep.dataforge.io.toJson
import hep.dataforge.js.jsObject
import hep.dataforge.meta.DynamicMeta
import hep.dataforge.meta.Meta
import hep.dataforge.meta.update
import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.findStyle
import kotlinx.html.dom.append
import kotlinx.html.js.*
import org.w3c.dom.Element
import kotlin.collections.forEach
import kotlin.collections.isNotEmpty
import kotlin.collections.set
import kotlin.dom.clear
//FIXME something rotten in JS-Meta converter
fun Meta.toDynamic() = JSON.parse<dynamic>(toJson().toString())
//TODO add node descriptor instead of configuring property selector
fun Element.displayPropertyEditor(
name: Name,
item: VisualObject,
propertySelector: (VisualObject) -> Meta = { it.config }
) {
clear()
append {
card("Properties") {
if (!name.isEmpty()) {
nav {
attributes["aria-label"] = "breadcrumb"
ol("breadcrumb") {
name.tokens.forEach { token ->
li("breadcrumb-item") {
+token.toString()
}
}
}
}
}
val dMeta: dynamic = propertySelector(item).toDynamic()
val options: JSONEditorOptions = jsObject {
mode = "form"
onChangeJSON = { item.config.update(DynamicMeta(it.asDynamic())) }
}
JSONEditor(div(), options, dMeta)
}
val styles = item.styles
if (styles.isNotEmpty()) {
card("Styles") {
item.styles.forEach { style ->
val styleMeta = item.findStyle(style)
h4("container") { +style }
if (styleMeta != null) {
div("container").apply {
val options: JSONEditorOptions = jsObject {
mode = "view"
}
JSONEditor(
this,
options,
styleMeta.toDynamic()
)
}
}
}
}
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,48 +1,48 @@
.loader {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Remove default bullets */ /* Remove default bullets */
ul, .objTree-subtree { ul, .tree {
list-style-type: none; list-style-type: none;
} }
/* Style the caret/arrow */ /* Style the caret/arrow */
.objTree-caret { .tree-caret {
cursor: pointer; cursor: pointer;
user-select: none; /* Prevent text selection */ user-select: none; /* Prevent text selection */
} }
.objTree-label {
cursor: pointer;
}
/* Create the caret/arrow with a unicode, and style it */ /* Create the caret/arrow with a unicode, and style it */
.objTree-caret::before { .tree-caret::before {
content: "\25B6"; content: "\25B6";
color: black; color: black;
display: inline-block; display: inline-block;
margin-right: 6px; margin-right: 6px;
} }
.objTree-leaf::before { .tree-leaf{
user-select: none;
display: inline-block;
}
.tree-leaf::before {
content: "\25C6"; content: "\25C6";
color: black; color: black;
display: inline-block; display: inline-block;
margin-right: 6px; margin-right: 6px;
} }
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */ /* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.objTree-caret-down::before { .tree-caret-down::before {
transform: rotate(90deg); transform: rotate(90deg);
} }
.tree-label-inactive {
color: gray;
}
.tree-label-selected{
background-color: lightblue;
}
.no-padding{
padding: 0;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,6 @@
package hep.dataforge.vis.fx package hep.dataforge.vis
import hep.dataforge.context.* import hep.dataforge.context.*
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.boolean import hep.dataforge.meta.boolean
import javafx.application.Application import javafx.application.Application
@ -20,7 +19,7 @@ import kotlin.reflect.KClass
/** /**
* Plugin holding JavaFX application instance and its root stage * Plugin holding JavaFX application instance and its root stage
*/ */
class FXPlugin(meta: Meta = EmptyMeta) : AbstractPlugin(meta) { class FXPlugin(meta: Meta = Meta.EMPTY) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
private val stages: ObservableSet<Stage> = FXCollections.observableSet() private val stages: ObservableSet<Stage> = FXCollections.observableSet()
@ -96,7 +95,8 @@ class FXPlugin(meta: Meta = EmptyMeta) : AbstractPlugin(meta) {
companion object : PluginFactory<FXPlugin> { companion object : PluginFactory<FXPlugin> {
override val type: KClass<out FXPlugin> = FXPlugin::class override val type: KClass<out FXPlugin> = FXPlugin::class
override val tag: PluginTag = PluginTag("vis.fx", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag("vis.fx", group = PluginTag.DATAFORGE_GROUP)
override fun invoke(meta: Meta, context: Context): FXPlugin = FXPlugin(meta) override fun invoke(meta: Meta, context: Context): FXPlugin =
FXPlugin(meta)
} }
} }

View File

@ -1,4 +1,4 @@
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.Name import hep.dataforge.names.Name

View File

@ -3,7 +3,7 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get

View File

@ -3,15 +3,15 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.vis.fx.dfIconView import hep.dataforge.vis.dfIconView
import javafx.scene.Node import javafx.scene.Node
import javafx.scene.control.* import javafx.scene.control.*
import javafx.scene.control.cell.TextFieldTreeTableCell import javafx.scene.control.cell.TextFieldTreeTableCell
@ -128,7 +128,11 @@ class ConfigEditor(
when (item) { when (item) {
is FXMetaValue<Config> -> { is FXMetaValue<Config> -> {
text = null text = null
val chooser = ValueChooser.build(Global, item.valueProperty, item.descriptor) { val chooser = ValueChooser.build(
Global,
item.valueProperty,
item.descriptor
) {
item.set(it) item.set(it)
} }
graphic = chooser.node graphic = chooser.node

View File

@ -1,9 +1,9 @@
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.descriptors.ItemDescriptor
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.descriptors.ValueDescriptor
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.ItemDescriptor
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.asName import hep.dataforge.names.asName
@ -174,10 +174,10 @@ private fun <M : MutableMeta<M>> M.createEmptyNode(token: NameToken, append: Boo
val name = token.asName() val name = token.asName()
val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1 val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1
val newName = name.withIndex(index.toString()) val newName = name.withIndex(index.toString())
set(newName, EmptyMeta) set(newName, Meta.EMPTY)
get(newName).node!! get(newName).node!!
} else { } else {
this.setNode(token.asName(), EmptyMeta) this.setNode(token.asName(), Meta.EMPTY)
//FIXME possible concurrency bug //FIXME possible concurrency bug
get(token).node!! get(token).node!!
} }
@ -211,9 +211,9 @@ fun <M : MutableMeta<M>> FXMetaNode<M>.addValue(key: String) {
fun <M : MutableMeta<M>> FXMetaNode<M>.addNode(key: String) { fun <M : MutableMeta<M>> FXMetaNode<M>.addNode(key: String) {
val parent = getOrCreateNode() val parent = getOrCreateNode()
if (descriptor?.multiple == true) { if (descriptor?.multiple == true) {
parent.append(key, EmptyMeta) parent.append(key, Meta.EMPTY)
} else { } else {
parent[key] = EmptyMeta parent[key] = Meta.EMPTY
} }
} }

View File

@ -14,18 +14,23 @@
* limitations under the License. * limitations under the License.
*/ */
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vis.fx.dfIconView import hep.dataforge.vis.dfIconView
import javafx.beans.property.SimpleStringProperty import javafx.beans.property.SimpleStringProperty
import javafx.scene.control.TreeItem import javafx.scene.control.TreeItem
import javafx.scene.control.TreeSortMode import javafx.scene.control.TreeSortMode
import javafx.scene.control.TreeTableView import javafx.scene.control.TreeTableView
import tornadofx.* import tornadofx.*
class MetaViewer(val rootNode: FXMetaNode<*>, title: String = "Meta viewer") : Fragment(title, dfIconView) { class MetaViewer(val rootNode: FXMetaNode<*>, title: String = "Meta viewer") : Fragment(title,
constructor(meta: Meta, title: String = "Meta viewer"): this(FXMeta.root(meta),title = title) dfIconView
) {
constructor(meta: Meta, title: String = "Meta viewer"): this(
FXMeta.root(
meta
),title = title)
override val root = borderpane { override val root = borderpane {
center { center {

View File

@ -3,7 +3,7 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.Name import hep.dataforge.names.Name

View File

@ -3,7 +3,7 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.values.Value import hep.dataforge.values.Value

View File

@ -3,20 +3,19 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.Named import hep.dataforge.context.Named
import hep.dataforge.descriptors.ValueDescriptor
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.provider.provideByType import hep.dataforge.provider.provideByType
import hep.dataforge.values.Null import hep.dataforge.values.Null
import hep.dataforge.values.Value import hep.dataforge.values.Value
import hep.dataforge.vis.common.widget import hep.dataforge.vis.widget
import hep.dataforge.vis.common.widgetType import hep.dataforge.vis.widgetType
import javafx.beans.property.ObjectProperty import javafx.beans.property.ObjectProperty
import javafx.beans.value.ObservableValue import javafx.beans.value.ObservableValue
import javafx.scene.Node import javafx.scene.Node
@ -66,7 +65,7 @@ interface ValueChooser {
@Type("hep.dataforge.vis.fx.valueChooserFactory") @Type("hep.dataforge.vis.fx.valueChooserFactory")
interface Factory : Named { interface Factory : Named {
operator fun invoke(meta: Meta = EmptyMeta): ValueChooser operator fun invoke(meta: Meta = Meta.EMPTY): ValueChooser
} }
companion object { companion object {
@ -108,8 +107,7 @@ interface ValueChooser {
descriptor: ValueDescriptor? = null, descriptor: ValueDescriptor? = null,
setter: (Value) -> Unit setter: (Value) -> Unit
): ValueChooser { ): ValueChooser {
val chooser = val chooser = build(context, descriptor)
build(context, descriptor)
chooser.setDisplayValue(value.value ?: Null) chooser.setDisplayValue(value.value ?: Null)
value.onChange { value.onChange {
chooser.setDisplayValue(it ?: Null) chooser.setDisplayValue(it ?: Null)

View File

@ -3,9 +3,9 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.descriptors.ValueDescriptor import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.values.Null import hep.dataforge.values.Null
import hep.dataforge.values.Value import hep.dataforge.values.Value
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty

View File

@ -1,11 +1,11 @@
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.common.findStyle import hep.dataforge.vis.findStyle
import javafx.beans.binding.Binding import javafx.beans.binding.Binding
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.scene.Node import javafx.scene.Node

View File

@ -1,7 +1,7 @@
package hep.dataforge.vis.fx.editor package hep.dataforge.vis.editor
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.scene.control.SelectionMode import javafx.scene.control.SelectionMode
import javafx.scene.control.TreeItem import javafx.scene.control.TreeItem

View File

@ -1,12 +1,12 @@
package hep.dataforge.vis.fx.demo package hep.dataforge.vis.demo
import hep.dataforge.descriptors.NodeDescriptor import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta import hep.dataforge.meta.asConfig
import hep.dataforge.meta.toConfig import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.vis.fx.editor.ConfigEditor import hep.dataforge.vis.editor.ConfigEditor
import hep.dataforge.vis.fx.editor.FXMeta import hep.dataforge.vis.editor.FXMeta
import hep.dataforge.vis.fx.editor.MetaViewer import hep.dataforge.vis.editor.MetaViewer
import javafx.geometry.Orientation import javafx.geometry.Orientation
import tornadofx.* import tornadofx.*
@ -15,7 +15,7 @@ class MetaEditorDemoApp : App(MetaEditorDemo::class)
class MetaEditorDemo : View("Meta editor demo") { class MetaEditorDemo : View("Meta editor demo") {
val meta = buildMeta { val meta = Meta {
"aNode" put { "aNode" put {
"innerNode" put { "innerNode" put {
"innerValue" put true "innerValue" put true
@ -23,33 +23,35 @@ class MetaEditorDemo : View("Meta editor demo") {
"b" put 223 "b" put 223
"c" put "StringValue" "c" put "StringValue"
} }
}.toConfig() }.asConfig()
val descriptor = NodeDescriptor { val descriptor = NodeDescriptor {
node("aNode") { defineNode("aNode") {
info = "A root demo node" info = "A root demo node"
value("b") { defineValue("b") {
info = "b number value" info = "b number value"
type(ValueType.NUMBER) type(ValueType.NUMBER)
} }
node("otherNode") { defineNode("otherNode") {
value("otherValue") { defineValue("otherValue") {
type(ValueType.BOOLEAN) type(ValueType.BOOLEAN)
default(false) default(false)
info = "default value" info = "default value"
} }
} }
} }
value("multiple"){ defineValue("multiple") {
info = "A sns value" info = "A sns value"
multiple = true multiple = true
} }
} }
private val rootNode = FXMeta.root(meta,descriptor) private val rootNode = FXMeta.root(meta, descriptor)
override val root = override val root =
splitpane(Orientation.HORIZONTAL, MetaViewer(rootNode).root, ConfigEditor(rootNode).root) splitpane(Orientation.HORIZONTAL, MetaViewer(rootNode).root, ConfigEditor(
rootNode
).root)
} }
fun main() { fun main() {

View File

@ -7,7 +7,7 @@ kotlin {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
api(project(":dataforge-vis-spatial")) api(project(":dataforge-vis-spatial"))
api("scientifik:gdml:0.1.6") api("scientifik:gdml:0.1.7")
} }
} }
} }

View File

@ -2,12 +2,12 @@ package hep.dataforge.vis.spatial.gdml
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.buildMeta
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vis.common.useStyle
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
import hep.dataforge.vis.useStyle
import scientifik.gdml.* import scientifik.gdml.*
import kotlin.random.Random import kotlin.random.Random
@ -44,7 +44,7 @@ class GDMLTransformer(val root: GDML) {
fun VisualObject3D.useStyle(name: String, builder: MetaBuilder.() -> Unit) { fun VisualObject3D.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
styleCache.getOrPut(name.toName()) { styleCache.getOrPut(name.toName()) {
buildMeta(builder) Meta(builder)
} }
useStyle(name) useStyle(name)
} }
@ -69,7 +69,13 @@ class GDMLTransformer(val root: GDML) {
var onFinish: GDMLTransformer.() -> Unit = {} var onFinish: GDMLTransformer.() -> Unit = {}
internal fun finalize(final: VisualGroup3D): VisualGroup3D { internal fun finalize(final: VisualGroup3D): VisualGroup3D {
final.prototypes = proto //final.prototypes = proto
final.prototypes {
proto.children.forEach { (token, item) ->
item.parent = null
set(token.asName(), item)
}
}
styleCache.forEach { styleCache.forEach {
final.styleSheet { final.styleSheet {
define(it.key.toString(), it.value) define(it.key.toString(), it.value)

View File

@ -4,8 +4,8 @@ package hep.dataforge.vis.spatial.gdml
import hep.dataforge.names.Name 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.vis.common.get import hep.dataforge.vis.get
import hep.dataforge.vis.common.set import hep.dataforge.vis.set
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.World.ONE import hep.dataforge.vis.spatial.World.ONE
import hep.dataforge.vis.spatial.World.ZERO import hep.dataforge.vis.spatial.World.ZERO

View File

@ -1,43 +1,39 @@
package hep.dataforge.vis.spatial.gdml package hep.dataforge.vis.spatial.gdml
import hep.dataforge.vis.spatial.stringify
import nl.adaptivity.xmlutil.StAXReader import nl.adaptivity.xmlutil.StAXReader
import org.junit.Test import org.junit.jupiter.api.Test
import scientifik.gdml.GDML import scientifik.gdml.GDML
import java.io.File
import java.net.URL
import kotlin.test.Ignore
class TestConvertor { class TestConvertor {
@Test @Test
@Ignore
fun testBMNGeometry() { fun testBMNGeometry() {
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") val stream = javaClass.getResourceAsStream("/gdml/BM@N.gdml")
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N.gdml")
val stream = if (file.exists()) {
file.inputStream()
} else {
url.openStream()
}
val xmlReader = StAXReader(stream, "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
xml.toVisual()
}
@Test
@Ignore
fun testCubes() {
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml ")
val stream = if (file.exists()) {
file.inputStream()
} else {
return
}
val xmlReader = StAXReader(stream, "UTF-8") val xmlReader = StAXReader(stream, "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader) val xml = GDML.format.parse(GDML.serializer(), xmlReader)
val visual = xml.toVisual() val visual = xml.toVisual()
println(visual) println(visual.stringify())
}
@Test
fun testCubes() {
val stream = javaClass.getResourceAsStream("/gdml/cubes.gdml")
val xmlReader = StAXReader(stream, "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
val visual = xml.toVisual()
// println(visual)
}
@Test
fun testSimple() {
val stream = javaClass.getResourceAsStream("/gdml/simple1.gdml")
val xmlReader = StAXReader(stream, "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
val visual = xml.toVisual()
println(visual.stringify())
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,254 @@
<gdml>
<define>
<position name="center" x="0.0" y="0.0" z="0.0" unit="cm"/>
<position name="box_position" x="25.0" y="50.0" z="75.0" unit="cm"/>
<rotation name="Rot0" x="0.0" y="0.0" z="0.0" unit="deg"/>
<rotation name="Rot1" x="0.0" y="0.0" z="60.0" unit="deg"/>
<rotation name="Rot2" x="0.0" y="0.0" z="120.0" unit="deg"/>
<rotation name="Rot3" x="0.0" y="0.0" z="180.0" unit="deg"/>
<rotation name="Rot4" x="0.0" y="0.0" z="240.0" unit="deg"/>
<rotation name="Rot5" x="0.0" y="0.0" z="300.0" unit="deg"/>
<rotation name="Rot000" x="0.0" y="0.0" z="0.0" unit="deg"/>
<position name="Pos000" x="-50.0" y="-50.0" z="-50.0" unit="cm"/>
<rotation name="Rot001" x="0.0" y="0.0" z="120.0" unit="deg"/>
<position name="Pos001" x="-50.0" y="-50.0" z="0.0" unit="cm"/>
<rotation name="Rot002" x="0.0" y="0.0" z="240.0" unit="deg"/>
<position name="Pos002" x="-50.0" y="-50.0" z="50.0" unit="cm"/>
<rotation name="Rot010" x="0.0" y="120.0" z="0.0" unit="deg"/>
<position name="Pos010" x="-50.0" y="0.0" z="-50.0" unit="cm"/>
<rotation name="Rot011" x="0.0" y="120.0" z="120.0" unit="deg"/>
<position name="Pos011" x="-50.0" y="0.0" z="0.0" unit="cm"/>
<rotation name="Rot012" x="0.0" y="120.0" z="240.0" unit="deg"/>
<position name="Pos012" x="-50.0" y="0.0" z="50.0" unit="cm"/>
<rotation name="Rot020" x="0.0" y="240.0" z="0.0" unit="deg"/>
<position name="Pos020" x="-50.0" y="50.0" z="-50.0" unit="cm"/>
<rotation name="Rot021" x="0.0" y="240.0" z="120.0" unit="deg"/>
<position name="Pos021" x="-50.0" y="50.0" z="0.0" unit="cm"/>
<rotation name="Rot022" x="0.0" y="240.0" z="240.0" unit="deg"/>
<position name="Pos022" x="-50.0" y="50.0" z="50.0" unit="cm"/>
<rotation name="Rot100" x="120.0" y="0.0" z="0.0" unit="deg"/>
<position name="Pos100" x="0.0" y="-50.0" z="-50.0" unit="cm"/>
<rotation name="Rot101" x="120.0" y="0.0" z="120.0" unit="deg"/>
<position name="Pos101" x="0.0" y="-50.0" z="0.0" unit="cm"/>
<rotation name="Rot102" x="120.0" y="0.0" z="240.0" unit="deg"/>
<position name="Pos102" x="0.0" y="-50.0" z="50.0" unit="cm"/>
<rotation name="Rot110" x="120.0" y="120.0" z="0.0" unit="deg"/>
<position name="Pos110" x="0.0" y="0.0" z="-50.0" unit="cm"/>
<rotation name="Rot111" x="120.0" y="120.0" z="120.0" unit="deg"/>
<position name="Pos111" x="0.0" y="0.0" z="0.0" unit="cm"/>
<rotation name="Rot112" x="120.0" y="120.0" z="240.0" unit="deg"/>
<position name="Pos112" x="0.0" y="0.0" z="50.0" unit="cm"/>
<rotation name="Rot120" x="120.0" y="240.0" z="0.0" unit="deg"/>
<position name="Pos120" x="0.0" y="50.0" z="-50.0" unit="cm"/>
<rotation name="Rot121" x="120.0" y="240.0" z="120.0" unit="deg"/>
<position name="Pos121" x="0.0" y="50.0" z="0.0" unit="cm"/>
<rotation name="Rot122" x="120.0" y="240.0" z="240.0" unit="deg"/>
<position name="Pos122" x="0.0" y="50.0" z="50.0" unit="cm"/>
<rotation name="Rot200" x="240.0" y="0.0" z="0.0" unit="deg"/>
<position name="Pos200" x="50.0" y="-50.0" z="-50.0" unit="cm"/>
<rotation name="Rot201" x="240.0" y="0.0" z="120.0" unit="deg"/>
<position name="Pos201" x="50.0" y="-50.0" z="0.0" unit="cm"/>
<rotation name="Rot202" x="240.0" y="0.0" z="240.0" unit="deg"/>
<position name="Pos202" x="50.0" y="-50.0" z="50.0" unit="cm"/>
<rotation name="Rot210" x="240.0" y="120.0" z="0.0" unit="deg"/>
<position name="Pos210" x="50.0" y="0.0" z="-50.0" unit="cm"/>
<rotation name="Rot211" x="240.0" y="120.0" z="120.0" unit="deg"/>
<position name="Pos211" x="50.0" y="0.0" z="0.0" unit="cm"/>
<rotation name="Rot212" x="240.0" y="120.0" z="240.0" unit="deg"/>
<position name="Pos212" x="50.0" y="0.0" z="50.0" unit="cm"/>
<rotation name="Rot220" x="240.0" y="240.0" z="0.0" unit="deg"/>
<position name="Pos220" x="50.0" y="50.0" z="-50.0" unit="cm"/>
<rotation name="Rot221" x="240.0" y="240.0" z="120.0" unit="deg"/>
<position name="Pos221" x="50.0" y="50.0" z="0.0" unit="cm"/>
<rotation name="Rot222" x="240.0" y="240.0" z="240.0" unit="deg"/>
<position name="Pos222" x="50.0" y="50.0" z="50.0" unit="cm"/>
</define>
<materials/>
<solids>
<tube aunit="degree" name="segment" rmax="20.0" z="5.0" rmin="17.0" startphi="0.0" deltaphi="60.0"/>
<box name="cave" x="200.0" y="200.0" z="200.0"/>
<box name="box" x="30.0" y="30.0" z="30.0"/>
</solids>
<structure>
<volume name="segment_vol">
<materialref ref="G4_WATER"/>
<solidref ref="segment"/>
</volume>
<volume name="composite">
<physvol name="segment_0">
<volumeref ref="segment_vol"/>
<positionref ref="center"/>
<rotationref ref="Rot0"/>
</physvol>
<physvol name="segment_1">
<volumeref ref="segment_vol"/>
<positionref ref="center"/>
<rotationref ref="Rot1"/>
</physvol>
<physvol name="segment_2">
<volumeref ref="segment_vol"/>
<positionref ref="center"/>
<rotationref ref="Rot2"/>
</physvol>
<physvol name="segment_3">
<volumeref ref="segment_vol"/>
<positionref ref="center"/>
<rotationref ref="Rot3"/>
</physvol>
<physvol name="segment_4">
<volumeref ref="segment_vol"/>
<positionref ref="center"/>
<rotationref ref="Rot4"/>
</physvol>
<physvol name="segment_5">
<volumeref ref="segment_vol"/>
<positionref ref="center"/>
<rotationref ref="Rot5"/>
</physvol>
<materialref ref="G4_AIR"/>
<solidref ref="box"/>
</volume>
<volume name="world">
<physvol name="composite_000">
<volumeref ref="composite"/>
<positionref ref="Pos000"/>
<rotationref ref="Rot000"/>
</physvol>
<physvol name="composite_001">
<volumeref ref="composite"/>
<positionref ref="Pos001"/>
<rotationref ref="Rot001"/>
</physvol>
<physvol name="composite_002">
<volumeref ref="composite"/>
<positionref ref="Pos002"/>
<rotationref ref="Rot002"/>
</physvol>
<physvol name="composite_003">
<volumeref ref="composite"/>
<positionref ref="Pos010"/>
<rotationref ref="Rot010"/>
</physvol>
<physvol name="composite_004">
<volumeref ref="composite"/>
<positionref ref="Pos011"/>
<rotationref ref="Rot011"/>
</physvol>
<physvol name="composite_005">
<volumeref ref="composite"/>
<positionref ref="Pos012"/>
<rotationref ref="Rot012"/>
</physvol>
<physvol name="composite_006">
<volumeref ref="composite"/>
<positionref ref="Pos020"/>
<rotationref ref="Rot020"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos021"/>
<rotationref ref="Rot021"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos022"/>
<rotationref ref="Rot022"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos100"/>
<rotationref ref="Rot100"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos101"/>
<rotationref ref="Rot101"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos102"/>
<rotationref ref="Rot102"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos110"/>
<rotationref ref="Rot110"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos111"/>
<rotationref ref="Rot111"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos112"/>
<rotationref ref="Rot112"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos120"/>
<rotationref ref="Rot120"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos121"/>
<rotationref ref="Rot121"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos122"/>
<rotationref ref="Rot122"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos200"/>
<rotationref ref="Rot200"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos201"/>
<rotationref ref="Rot201"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos202"/>
<rotationref ref="Rot202"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos210"/>
<rotationref ref="Rot210"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos211"/>
<rotationref ref="Rot211"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos212"/>
<rotationref ref="Rot212"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos220"/>
<rotationref ref="Rot220"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos221"/>
<rotationref ref="Rot221"/>
</physvol>
<physvol>
<volumeref ref="composite"/>
<positionref ref="Pos222"/>
<rotationref ref="Rot222"/>
</physvol>
<materialref ref="G4_AIR"/>
<solidref ref="cave"/>
</volume>
</structure>
<setup name="Default" version="1.0">
<world ref="world"/>
</setup>
</gdml>

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<gdml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd">
<define/>
<materials>
<material name="Vacuum" Z="1">
<D unit="g/cm3" value="0"/>
<atom unit="g/mole" value="0"/>
</material>
<material name="Al" Z="13">
<D unit="g/cm3" value="2.7000000000000002"/>
<atom unit="g/mole" value="26.98"/>
</material>
</materials>
<solids>
<box name="R" x="50" y="50" z="10" lunit="cm"/>
</solids>
<structure>
<volume name="R">
<materialref ref="Vacuum"/>
<solidref ref="R"/>
</volume>
</structure>
<setup name="default" version="1.0">
<world ref="R"/>
</setup>
</gdml>

View File

@ -1,17 +1,16 @@
import org.openjfx.gradle.JavaFXOptions import scientifik.serialization
import scientifik.useSerialization
plugins { plugins {
id("scientifik.mpp") id("scientifik.mpp")
id("org.openjfx.javafxplugin")
} }
useSerialization() serialization()
kotlin { kotlin {
jvm { js {
withJava() useCommonJs()
} }
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
@ -32,14 +31,9 @@ kotlin {
jsMain { jsMain {
dependencies { dependencies {
// api(project(":wrappers")) // api(project(":wrappers"))
implementation(npm("three", "0.106.2")) implementation(npm("three", "0.114.0"))
implementation(npm("@hi-level/three-csg", "1.0.6")) implementation(npm("three-csg-ts", "1.0.1"))
} }
} }
} }
} }
configure<JavaFXOptions> {
modules("javafx.controls")
}

View File

@ -2,15 +2,11 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.float import hep.dataforge.meta.float
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.*
import hep.dataforge.vis.common.VisualFactory
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.set
import hep.dataforge.vis.spatial.Box.Companion.TYPE_NAME import hep.dataforge.vis.spatial.Box.Companion.TYPE_NAME
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -29,7 +25,6 @@ class Box(
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
//TODO add helper for color configuration //TODO add helper for color configuration
@ -69,7 +64,7 @@ class Box(
} }
} }
inline fun VisualGroup3D.box( inline fun MutableVisualGroup.box(
xSize: Number, xSize: Number,
ySize: Number, ySize: Number,
zSize: Number, zSize: Number,

View File

@ -2,11 +2,10 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.names.NameToken
import hep.dataforge.vis.common.set import hep.dataforge.vis.*
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -23,7 +22,7 @@ class Composite(
val compositeType: CompositeType, val compositeType: CompositeType,
val first: VisualObject3D, val first: VisualObject3D,
val second: VisualObject3D val second: VisualObject3D
) : AbstractVisualObject(), VisualObject3D { ) : AbstractVisualObject(), VisualObject3D, VisualGroup {
init { init {
first.parent = this first.parent = this
@ -34,11 +33,16 @@ class Composite(
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override val children: Map<NameToken, VisualObject>
get() = mapOf(NameToken("first") to first, NameToken("second") to second)
override val styleSheet: StyleSheet?
get() = null
} }
inline fun VisualGroup3D.composite( inline fun MutableVisualGroup.composite(
type: CompositeType, type: CompositeType,
name: String = "", name: String = "",
builder: VisualGroup3D.() -> Unit builder: VisualGroup3D.() -> Unit
@ -50,24 +54,24 @@ inline fun VisualGroup3D.composite(
it.config.update(group.config) it.config.update(group.config)
//it.material = group.material //it.material = group.material
if(group.position!=null) { if (group.position != null) {
it.position = group.position it.position = group.position
} }
if(group.rotation!=null) { if (group.rotation != null) {
it.rotation = group.rotation it.rotation = group.rotation
} }
if(group.scale!=null) { if (group.scale != null) {
it.scale = group.scale it.scale = group.scale
} }
set(name, it) set(name, it)
} }
} }
fun VisualGroup3D.union(name: String = "", builder: VisualGroup3D.() -> Unit) = inline fun MutableVisualGroup.union(name: String = "", builder: VisualGroup3D.() -> Unit) =
composite(CompositeType.UNION, name, builder = builder) composite(CompositeType.UNION, name, builder = builder)
fun VisualGroup3D.subtract(name: String = "", builder: VisualGroup3D.() -> Unit) = inline fun MutableVisualGroup.subtract(name: String = "", builder: VisualGroup3D.() -> Unit) =
composite(CompositeType.SUBTRACT, name, builder = builder) composite(CompositeType.SUBTRACT, name, builder = builder)
fun VisualGroup3D.intersect(name: String = "", builder: VisualGroup3D.() -> Unit) = inline fun MutableVisualGroup.intersect(name: String = "", builder: VisualGroup3D.() -> Unit) =
composite(CompositeType.INTERSECT, name, builder = builder) composite(CompositeType.INTERSECT, name, builder = builder)

View File

@ -2,10 +2,10 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.common.set import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -25,7 +25,6 @@ class ConeSegment(
var angle: Float = PI2 var angle: Float = PI2
) : AbstractVisualObject(), VisualObject3D, Shape { ) : AbstractVisualObject(), VisualObject3D, Shape {
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override var position: Point3D? = null override var position: Point3D? = null
@ -76,7 +75,7 @@ class ConeSegment(
} }
inline fun VisualGroup3D.cylinder( inline fun MutableVisualGroup.cylinder(
r: Number, r: Number,
height: Number, height: Number,
name: String = "", name: String = "",
@ -88,7 +87,7 @@ inline fun VisualGroup3D.cylinder(
).apply(block).also { set(name, it) } ).apply(block).also { set(name, it) }
inline fun VisualGroup3D.cone( inline fun MutableVisualGroup.cone(
bottomRadius: Number, bottomRadius: Number,
height: Number, height: Number,
upperRadius: Number = 0.0, upperRadius: Number = 0.0,

View File

@ -2,10 +2,10 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.common.set import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -14,7 +14,6 @@ import kotlinx.serialization.UseSerializers
@SerialName("3d.convex") @SerialName("3d.convex")
class Convex(val points: List<Point3D>) : AbstractVisualObject(), VisualObject3D { class Convex(val points: List<Point3D>) : AbstractVisualObject(), VisualObject3D {
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override var position: Point3D? = null override var position: Point3D? = null
@ -26,7 +25,7 @@ class Convex(val points: List<Point3D>) : AbstractVisualObject(), VisualObject3D
} }
} }
inline fun VisualGroup3D.convex(name: String = "", action: ConvexBuilder.() -> Unit = {}) = inline fun MutableVisualGroup.convex(name: String = "", action: ConvexBuilder.() -> Unit = {}) =
ConvexBuilder().apply(action).build().also { set(name, it) } ConvexBuilder().apply(action).build().also { set(name, it) }
class ConvexBuilder { class ConvexBuilder {

View File

@ -1,10 +1,10 @@
@file:UseSerializers(Point2DSerializer::class, Point3DSerializer::class) @file:UseSerializers(Point2DSerializer::class, Point3DSerializer::class)
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.common.set import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -45,7 +45,6 @@ class Extruded(
var layers: MutableList<Layer> = ArrayList() var layers: MutableList<Layer> = ArrayList()
) : AbstractVisualObject(), VisualObject3D, Shape { ) : AbstractVisualObject(), VisualObject3D, Shape {
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override var position: Point3D? = null override var position: Point3D? = null
@ -113,5 +112,5 @@ class Extruded(
} }
} }
fun VisualGroup3D.extrude(name: String = "", action: Extruded.() -> Unit = {}) = fun MutableVisualGroup.extrude(name: String = "", action: Extruded.() -> Unit = {}) =
Extruded().apply(action).also { set(name, it) } Extruded().apply(action).also { set(name, it) }

View File

@ -1,6 +1,5 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
/** /**
@ -14,7 +13,7 @@ interface GeometryBuilder<T : Any> {
* @param normal optional external normal to the face * @param normal optional external normal to the face
* @param meta optional additional platform-specific parameters like color or texture index * @param meta optional additional platform-specific parameters like color or texture index
*/ */
fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = EmptyMeta) fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = Meta.EMPTY)
fun build(): T fun build(): T
} }
@ -25,7 +24,7 @@ fun GeometryBuilder<*>.face4(
vertex3: Point3D, vertex3: Point3D,
vertex4: Point3D, vertex4: Point3D,
normal: Point3D? = null, normal: Point3D? = null,
meta: Meta = EmptyMeta meta: Meta = Meta.EMPTY
) { ) {
face(vertex1, vertex2, vertex3, normal, meta) face(vertex1, vertex2, vertex3, normal, meta)
face(vertex1, vertex3, vertex4, normal, meta) face(vertex1, vertex3, vertex4, normal, meta)

View File

@ -2,19 +2,17 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.common.set import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@Serializable @Serializable
@SerialName("3d.label") @SerialName("3d.label")
class Label3D(var text: String, var fontSize: Double, var fontFamily: String) : AbstractVisualObject(), class Label3D(var text: String, var fontSize: Double, var fontFamily: String) : AbstractVisualObject(), VisualObject3D {
VisualObject3D {
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override var position: Point3D? = null override var position: Point3D? = null
@ -23,7 +21,7 @@ class Label3D(var text: String, var fontSize: Double, var fontFamily: String) :
} }
fun VisualGroup3D.label( fun MutableVisualGroup.label(
text: String, text: String,
fontSize: Number = 20, fontSize: Number = 20,
fontFamily: String = "Arial", fontFamily: String = "Arial",

View File

@ -1,16 +1,18 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.vis.common.Colors import hep.dataforge.values.asValue
import hep.dataforge.vis.Colors
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
import hep.dataforge.vis.widgetType
class Material3D(override val config: Config) : Specific { class Material3D : Scheme() {
/** /**
* Primary web-color for the material * Primary web-color for the material
@ -32,8 +34,7 @@ class Material3D(override val config: Config) : Specific {
*/ */
var wireframe by boolean(false, WIREFRAME_KEY) var wireframe by boolean(false, WIREFRAME_KEY)
companion object : Specification<Material3D> { companion object : SchemeSpec<Material3D>(::Material3D) {
override fun wrap(config: Config): Material3D = Material3D(config)
val MATERIAL_KEY = "material".asName() val MATERIAL_KEY = "material".asName()
internal val COLOR_KEY = "color".asName() internal val COLOR_KEY = "color".asName()
@ -44,21 +45,26 @@ class Material3D(override val config: Config) : Specific {
internal val WIREFRAME_KEY = "wireframe".asName() internal val WIREFRAME_KEY = "wireframe".asName()
val MATERIAL_WIREFRAME_KEY = MATERIAL_KEY + WIREFRAME_KEY val MATERIAL_WIREFRAME_KEY = MATERIAL_KEY + WIREFRAME_KEY
val descriptor = NodeDescriptor { val descriptor by lazy {
value(VisualObject3D.VISIBLE_KEY) { //must be lazy to avoid initialization bug
type(ValueType.BOOLEAN) NodeDescriptor {
default(true) defineValue(COLOR_KEY) {
}
node(MATERIAL_KEY) {
value(COLOR_KEY) {
type(ValueType.STRING, ValueType.NUMBER) type(ValueType.STRING, ValueType.NUMBER)
default("#ffffff") default("#ffffff")
widgetType = "color"
} }
value(OPACITY_KEY) { defineValue(OPACITY_KEY) {
type(ValueType.NUMBER) type(ValueType.NUMBER)
default(1.0) default(1.0)
configure {
"attributes" to {
this["min"] = 0.0
this["max"] = 1.0
this["step"] = 0.1
}
}
} }
value(WIREFRAME_KEY) { defineValue(WIREFRAME_KEY) {
type(ValueType.BOOLEAN) type(ValueType.BOOLEAN)
default(false) default(false)
} }
@ -71,14 +77,14 @@ class Material3D(override val config: Config) : Specific {
* Set color as web-color * Set color as web-color
*/ */
fun VisualObject3D.color(webColor: String) { fun VisualObject3D.color(webColor: String) {
setProperty(MATERIAL_COLOR_KEY, webColor) setProperty(MATERIAL_COLOR_KEY, webColor.asValue())
} }
/** /**
* Set color as integer * Set color as integer
*/ */
fun VisualObject3D.color(rgb: Int) { fun VisualObject3D.color(rgb: Int) {
setProperty(MATERIAL_COLOR_KEY, rgb) setProperty(MATERIAL_COLOR_KEY, rgb.asValue())
} }
fun VisualObject3D.color(r: UByte, g: UByte, b: UByte) = setProperty( fun VisualObject3D.color(r: UByte, g: UByte, b: UByte) = setProperty(
@ -92,7 +98,7 @@ fun VisualObject3D.color(r: UByte, g: UByte, b: UByte) = setProperty(
var VisualObject3D.color: String? var VisualObject3D.color: String?
get() = getProperty(MATERIAL_COLOR_KEY)?.let { Colors.fromMeta(it) } get() = getProperty(MATERIAL_COLOR_KEY)?.let { Colors.fromMeta(it) }
set(value) { set(value) {
setProperty(MATERIAL_COLOR_KEY, value) setProperty(MATERIAL_COLOR_KEY, value?.asValue())
} }
val VisualObject3D.material: Material3D? val VisualObject3D.material: Material3D?
@ -110,5 +116,5 @@ fun VisualObject3D.material(builder: Material3D.() -> Unit) {
var VisualObject3D.opacity: Double? var VisualObject3D.opacity: Double?
get() = getProperty(MATERIAL_OPACITY_KEY).double get() = getProperty(MATERIAL_OPACITY_KEY).double
set(value) { set(value) {
setProperty(MATERIAL_OPACITY_KEY, value) setProperty(MATERIAL_OPACITY_KEY, value?.asValue())
} }

View File

@ -2,13 +2,13 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.number import hep.dataforge.meta.number
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.common.set import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -16,7 +16,6 @@ import kotlinx.serialization.UseSerializers
@Serializable @Serializable
@SerialName("3d.line") @SerialName("3d.line")
class PolyLine(var points: List<Point3D>) : AbstractVisualObject(), VisualObject3D { class PolyLine(var points: List<Point3D>) : AbstractVisualObject(), VisualObject3D {
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override var position: Point3D? = null override var position: Point3D? = null
@ -32,5 +31,5 @@ class PolyLine(var points: List<Point3D>) : AbstractVisualObject(), VisualObject
} }
fun VisualGroup3D.polyline(vararg points: Point3D, name: String = "", action: PolyLine.() -> Unit = {}) = fun MutableVisualGroup.polyline(vararg points: Point3D, name: String = "", action: PolyLine.() -> Unit = {}) =
PolyLine(points.toList()).apply(action).also { set(name, it) } PolyLine(points.toList()).apply(action).also { set(name, it) }

View File

@ -1,18 +1,13 @@
@file:UseSerializers(Point3DSerializer::class, NameSerializer::class, ConfigSerializer::class) @file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Laminate import hep.dataforge.meta.Laminate
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.names.Name import hep.dataforge.names.*
import hep.dataforge.names.NameToken import hep.dataforge.vis.*
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.vis.common.*
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
@ -26,7 +21,9 @@ import kotlin.collections.set
*/ */
@Serializable @Serializable
@SerialName("3d.proxy") @SerialName("3d.proxy")
class Proxy private constructor(val templateName: Name) : AbstractVisualObject(), VisualGroup, VisualObject3D { class Proxy private constructor(
val templateName: Name
) : AbstractVisualObject(), VisualGroup, VisualObject3D {
constructor(parent: VisualGroup3D, templateName: Name) : this(templateName) { constructor(parent: VisualGroup3D, templateName: Name) : this(templateName) {
this.parent = parent this.parent = parent
@ -36,7 +33,6 @@ class Proxy private constructor(val templateName: Name) : AbstractVisualObject()
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
/** /**
@ -44,10 +40,12 @@ class Proxy private constructor(val templateName: Name) : AbstractVisualObject()
*/ */
val prototype: VisualObject3D val prototype: VisualObject3D
get() = (parent as? VisualGroup3D)?.getPrototype(templateName) get() = (parent as? VisualGroup3D)?.getPrototype(templateName)
?: error("Template with name $templateName not found in $parent") ?: error("Prototype with name $templateName not found in $parent")
override val styleSheet: StyleSheet override val styleSheet: StyleSheet
get() = (parent as? VisualGroup)?.styleSheet ?: StyleSheet(this) get() = parent?.styleSheet ?: StyleSheet(
this
)
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
@ -89,7 +87,8 @@ class Proxy private constructor(val templateName: Name) : AbstractVisualObject()
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) }) //override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup { inner class ProxyChild(val name: Name) : AbstractVisualObject(),
VisualGroup {
val prototype: VisualObject get() = prototypeFor(name) val prototype: VisualObject get() = prototypeFor(name)
@ -155,20 +154,18 @@ val VisualObject.prototype: VisualObject
/** /**
* Create ref for existing prototype * Create ref for existing prototype
*/ */
inline fun VisualGroup3D.ref( fun VisualGroup3D.ref(
templateName: Name, templateName: Name,
name: String = "", name: String = ""
block: Proxy.() -> Unit = {} ): Proxy = Proxy(this, templateName).also { set(name, it) }
) = Proxy(this, templateName).apply(block).also { set(name, it) }
/** /**
* Add new proxy wrapping given object and automatically adding it to the prototypes * Add new proxy wrapping given object and automatically adding it to the prototypes
*/ */
fun VisualGroup3D.proxy( fun VisualGroup3D.proxy(
templateName: Name, name: String,
obj: VisualObject3D, obj: VisualObject3D,
name: String = "", templateName: Name = name.toName()
block: Proxy.() -> Unit = {}
): Proxy { ): Proxy {
val existing = getPrototype(templateName) val existing = getPrototype(templateName)
if (existing == null) { if (existing == null) {
@ -178,5 +175,14 @@ fun VisualGroup3D.proxy(
} else if (existing != obj) { } else if (existing != obj) {
error("Can't add different prototype on top of existing one") error("Can't add different prototype on top of existing one")
} }
return ref(templateName, name, block) return ref(templateName, name)
}
fun VisualGroup3D.proxyGroup(
name: String,
templateName: Name = name.toName(),
block: MutableVisualGroup.() -> Unit
): Proxy {
val group = VisualGroup3D().apply(block)
return proxy(name, group, templateName)
} }

View File

@ -2,10 +2,10 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.common.set import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -23,7 +23,6 @@ class Sphere(
var theta: Float = PI.toFloat() var theta: Float = PI.toFloat()
) : AbstractVisualObject(), VisualObject3D, Shape { ) : AbstractVisualObject(), VisualObject3D, Shape {
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override var position: Point3D? = null override var position: Point3D? = null
@ -61,7 +60,7 @@ class Sphere(
} }
} }
inline fun VisualGroup3D.sphere( inline fun MutableVisualGroup.sphere(
radius: Number, radius: Number,
phi: Number = 2 * PI, phi: Number = 2 * PI,
theta: Number = PI, theta: Number = PI,

View File

@ -1,10 +1,10 @@
@file:UseSerializers(Point3DSerializer::class) @file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.common.set import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -29,7 +29,6 @@ class Tube(
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
init { init {
@ -129,7 +128,7 @@ class Tube(
} }
inline fun VisualGroup3D.tube( inline fun MutableVisualGroup.tube(
r: Number, r: Number,
height: Number, height: Number,
innerRadius: Number = 0f, innerRadius: Number = 0f,

View File

@ -4,14 +4,13 @@ import hep.dataforge.context.AbstractPlugin
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag import hep.dataforge.context.PluginTag
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.MetaSerializer
import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vis.common.Visual import hep.dataforge.vis.SimpleVisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.Visual
import hep.dataforge.vis.VisualObject
import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
@ -39,29 +38,29 @@ class Visual3D(meta: Meta) : AbstractPlugin(meta) {
val serialModule = SerializersModule { val serialModule = SerializersModule {
contextual(Point3DSerializer) contextual(Point3DSerializer)
contextual(Point2DSerializer) contextual(Point2DSerializer)
contextual(NameSerializer)
contextual(NameTokenSerializer)
contextual(MetaSerializer)
contextual(ConfigSerializer)
polymorphic(VisualObject::class, VisualObject3D::class) { polymorphic(VisualObject::class, VisualObject3D::class) {
VisualGroup3D::class with VisualGroup3D.serializer() subclass(SimpleVisualGroup.serializer())
Proxy::class with Proxy.serializer() subclass(VisualGroup3D.serializer())
Composite::class with Composite.serializer() subclass(Proxy.serializer())
Tube::class with Tube.serializer() subclass(Composite.serializer())
Box::class with Box.serializer() subclass(Tube.serializer())
Convex::class with Convex.serializer() subclass(Box.serializer())
Extruded::class with Extruded.serializer() subclass(Convex.serializer())
addSubclass(PolyLine.serializer()) subclass(Extruded.serializer())
addSubclass(Label3D.serializer()) subclass(PolyLine.serializer())
subclass(Label3D.serializer())
subclass(Sphere.serializer())
} }
} }
val json = Json( @OptIn(UnstableDefault::class)
internal val json = Json(
JsonConfiguration( JsonConfiguration(
prettyPrint = true, prettyPrint = true,
useArrayPolymorphism = false, useArrayPolymorphism = false,
encodeDefaults = false encodeDefaults = false,
ignoreUnknownKeys = true
), ),
context = serialModule context = serialModule
) )

View File

@ -1,48 +1,49 @@
@file:UseSerializers( @file:UseSerializers(
Point3DSerializer::class, Point3DSerializer::class
ConfigSerializer::class,
NameTokenSerializer::class,
NameSerializer::class,
MetaSerializer::class
) )
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.MetaSerializer
import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.isEmpty import hep.dataforge.vis.*
import hep.dataforge.vis.common.AbstractVisualGroup
import hep.dataforge.vis.common.StyleSheet
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlin.collections.set import kotlin.collections.set
interface PrototypeHolder {
val parent: VisualGroup?
val prototypes: MutableVisualGroup?
}
/** /**
* Represents 3-dimensional Visual Group * Represents 3-dimensional Visual Group
*/ */
@Serializable @Serializable
@SerialName("group.3d") @SerialName("group.3d")
class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, PrototypeHolder {
override var styleSheet: StyleSheet? = null override var styleSheet: StyleSheet? = null
private set
/** /**
* A container for templates visible inside this group * A container for templates visible inside this group
*/ */
var prototypes: VisualGroup3D? = null @Serializable(PrototypesSerializer::class)
set(value) { override var prototypes: MutableVisualGroup? = null
value?.parent = this private set
field = value
} /**
* Create or edit prototype node as a group
*/
fun prototypes(builder: MutableVisualGroup.() -> Unit): Unit {
(prototypes ?: Prototypes().also {
attach(it)
prototypes = it
}).run(builder)
}
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed //FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
override var properties: Config? = null override var properties: Config? = null
@ -55,38 +56,18 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
private val _children = HashMap<NameToken, VisualObject>() private val _children = HashMap<NameToken, VisualObject>()
override val children: Map<NameToken, VisualObject> get() = _children override val children: Map<NameToken, VisualObject> get() = _children
// init {
// //Do after deserialization
// attachChildren()
// }
override fun attachChildren() { override fun attachChildren() {
prototypes?.parent = this prototypes?.parent = this
prototypes?.attachChildren() prototypes?.attachChildren()
super.attachChildren() super.attachChildren()
} }
/**
* Update or create stylesheet
*/
fun styleSheet(block: StyleSheet.() -> Unit) {
val res = styleSheet ?: StyleSheet(this).also { styleSheet = it }
res.block()
}
override fun removeChild(token: NameToken) { override fun removeChild(token: NameToken) {
_children.remove(token)?.run { parent = null } _children.remove(token)?.apply { parent = null }
childrenChanged(token.asName(), null)
} }
override fun setChild(token: NameToken, child: VisualObject) { override fun setChild(token: NameToken, child: VisualObject) {
if (child.parent == null) {
child.parent = this
} else if (child.parent !== this) {
error("Can't reassign existing parent for $child")
}
_children[token] = child _children[token] = child
childrenChanged(token.asName(), child)
} }
// /** // /**
@ -94,25 +75,13 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
// */ // */
// override fun addStatic(child: VisualObject) = setChild(NameToken("@static(${child.hashCode()})"), child) // override fun addStatic(child: VisualObject) = setChild(NameToken("@static(${child.hashCode()})"), child)
override fun createGroup(name: Name): VisualGroup3D { override fun createGroup(): VisualGroup3D = VisualGroup3D()
return when {
name.isEmpty() -> error("Should be unreachable")
name.length == 1 -> {
val token = name.first()!!
when (val current = children[token]) {
null -> VisualGroup3D().also { setChild(token, it) }
is VisualGroup3D -> current
else -> error("Can't create group with name $name because it exists and not a group")
}
}
else -> createGroup(name.first()!!.asName()).createGroup(name.cutFirst())
}
}
companion object { companion object {
// val PROTOTYPES_KEY = NameToken("@prototypes") // val PROTOTYPES_KEY = NameToken("@prototypes")
fun fromJson(json: String): VisualGroup3D = fun parseJson(json: String): VisualGroup3D =
Visual3D.json.parse(serializer(), json).also { it.attachChildren() } Visual3D.json.parse(serializer(), json).also { it.attachChildren() }
} }
} }
@ -120,21 +89,50 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
/** /**
* Ger a prototype redirecting the request to the parent if prototype is not found * Ger a prototype redirecting the request to the parent if prototype is not found
*/ */
tailrec fun VisualGroup3D.getPrototype(name: Name): VisualObject3D? = tailrec fun PrototypeHolder.getPrototype(name: Name): VisualObject3D? =
prototypes?.get(name) as? VisualObject3D ?: (parent as? VisualGroup3D)?.getPrototype(name) prototypes?.get(name) as? VisualObject3D ?: (parent as? VisualGroup3D)?.getPrototype(name)
/** /**
* Create or edit prototype node as a group * Define a group with given [name], attach it to this parent and return it.
*/ */
inline fun VisualGroup3D.prototypes(builder: VisualGroup3D.() -> Unit): Unit { fun MutableVisualGroup.group(name: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
(prototypes ?: VisualGroup3D().also { prototypes = it }).run(builder)
}
/**
* Define a group with given [key], attach it to this parent and return it.
*/
fun VisualGroup3D.group(key: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
VisualGroup3D().apply(action).also { VisualGroup3D().apply(action).also {
set(key, it) set(name, it)
} }
internal class Prototypes(
override var children: MutableMap<NameToken, VisualObject> = LinkedHashMap()
) : AbstractVisualGroup(), MutableVisualGroup, PrototypeHolder {
override var styleSheet: StyleSheet?
get() = null
set(_) {
error("Can't define stylesheet for prototypes block")
}
override fun removeChild(token: NameToken) {
children.remove(token)
childrenChanged(token.asName(), null)
}
override fun setChild(token: NameToken, child: VisualObject) {
children[token] = child
}
override fun createGroup() = SimpleVisualGroup()
override var properties: Config?
get() = null
set(_) {
error("Can't define properties for prototypes block")
}
override val prototypes: MutableVisualGroup get() = this
override fun attachChildren() {
children.values.forEach {
it.parent = parent
(it as? VisualGroup)?.attachChildren()
}
}
}

View File

@ -1,13 +1,15 @@
@file:UseSerializers(Point3DSerializer::class, NameSerializer::class, NameTokenSerializer::class) @file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.output.Renderer import hep.dataforge.output.Renderer
import hep.dataforge.vis.common.VisualObject import hep.dataforge.values.ValueType
import hep.dataforge.values.asValue
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.IGNORE_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.IGNORE_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY
@ -25,7 +27,8 @@ interface VisualObject3D : VisualObject {
companion object { companion object {
val VISIBLE_KEY = "visible".asName() val VISIBLE_KEY = "visible".asName()
// val SELECTED_KEY = "selected".asName()
// val SELECTED_KEY = "selected".asName()
val DETAIL_KEY = "detail".asName() val DETAIL_KEY = "detail".asName()
val LAYER_KEY = "layer".asName() val LAYER_KEY = "layer".asName()
val IGNORE_KEY = "ignore".asName() val IGNORE_KEY = "ignore".asName()
@ -55,6 +58,22 @@ interface VisualObject3D : VisualObject {
val xScale = scale + x val xScale = scale + x
val yScale = scale + y val yScale = scale + y
val zScale = scale + z val zScale = scale + z
val descriptor by lazy {
NodeDescriptor {
defineValue(VISIBLE_KEY) {
type(ValueType.BOOLEAN)
default(true)
}
defineItem(Material3D.MATERIAL_KEY.toString(), Material3D.descriptor)
// Material3D.MATERIAL_COLOR_KEY put "#ffffff"
// Material3D.MATERIAL_OPACITY_KEY put 1.0
// Material3D.MATERIAL_WIREFRAME_KEY put false
}
}
} }
} }
@ -64,10 +83,10 @@ interface VisualObject3D : VisualObject {
var VisualObject3D.layer: Int var VisualObject3D.layer: Int
get() = getProperty(LAYER_KEY).int ?: 0 get() = getProperty(LAYER_KEY).int ?: 0
set(value) { set(value) {
setProperty(LAYER_KEY, value) setProperty(LAYER_KEY, value.asValue())
} }
fun Renderer<VisualObject3D>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) = fun Renderer<VisualObject3D>.render(meta: Meta = Meta.EMPTY, action: VisualGroup3D.() -> Unit) =
render(VisualGroup3D().apply(action), meta) render(VisualGroup3D().apply(action), meta)
// Common properties // Common properties
@ -86,7 +105,7 @@ enum class RotationOrder {
*/ */
var VisualObject3D.rotationOrder: RotationOrder var VisualObject3D.rotationOrder: RotationOrder
get() = getProperty(VisualObject3D.rotationOrder).enum<RotationOrder>() ?: RotationOrder.XYZ get() = getProperty(VisualObject3D.rotationOrder).enum<RotationOrder>() ?: RotationOrder.XYZ
set(value) = setProperty(VisualObject3D.rotationOrder, value.name) set(value) = setProperty(VisualObject3D.rotationOrder, value.name.asValue())
/** /**
@ -94,19 +113,19 @@ var VisualObject3D.rotationOrder: RotationOrder
*/ */
var VisualObject3D.detail: Int? var VisualObject3D.detail: Int?
get() = getProperty(DETAIL_KEY, false).int get() = getProperty(DETAIL_KEY, false).int
set(value) = setProperty(DETAIL_KEY, value) set(value) = setProperty(DETAIL_KEY, value?.asValue())
var VisualObject.visible: Boolean? var VisualObject.visible: Boolean?
get() = getProperty(VISIBLE_KEY).boolean get() = getProperty(VISIBLE_KEY).boolean
set(value) = setProperty(VISIBLE_KEY, value) set(value) = setProperty(VISIBLE_KEY, value?.asValue())
/** /**
* If this property is true, the object will be ignored on render. * If this property is true, the object will be ignored on render.
* Property is not inherited. * Property is not inherited.
*/ */
var VisualObject.ignore: Boolean? var VisualObject.ignore: Boolean?
get() = getProperty(IGNORE_KEY,false).boolean get() = getProperty(IGNORE_KEY, false).boolean
set(value) = setProperty(IGNORE_KEY, value) set(value) = setProperty(IGNORE_KEY, value?.asValue())
//var VisualObject.selected: Boolean? //var VisualObject.selected: Boolean?
// get() = getProperty(SELECTED_KEY).boolean // get() = getProperty(SELECTED_KEY).boolean

View File

@ -1,10 +0,0 @@
package hep.dataforge.vis.spatial
import kotlin.math.PI
object World {
val ZERO = Point3D(0.0, 0.0, 0.0)
val ONE = Point3D(1.0, 1.0, 1.0)
}
const val PI2: Float = 2 * PI.toFloat()

View File

@ -1,9 +1,16 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.number import hep.dataforge.meta.number
import kotlin.math.PI
object World {
val ZERO = Point3D(0.0, 0.0, 0.0)
val ONE = Point3D(1.0, 1.0, 1.0)
}
const val PI2: Float = 2 * PI.toFloat()
expect class Point2D(x: Number, y: Number) { expect class Point2D(x: Number, y: Number) {
var x: Double var x: Double
@ -13,7 +20,7 @@ expect class Point2D(x: Number, y: Number) {
operator fun Point2D.component1() = x operator fun Point2D.component1() = x
operator fun Point2D.component2() = y operator fun Point2D.component2() = y
fun Point2D.toMeta() = buildMeta { fun Point2D.toMeta() = Meta {
VisualObject3D.x put x VisualObject3D.x put x
VisualObject3D.y put y VisualObject3D.y put y
} }
@ -34,7 +41,7 @@ operator fun Point3D.component3() = z
fun Meta.point3D() = Point3D(this["x"].number ?: 0, this["y"].number ?: 0, this["y"].number ?: 0) fun Meta.point3D() = Point3D(this["x"].number ?: 0, this["y"].number ?: 0, this["y"].number ?: 0)
fun Point3D.toMeta() = buildMeta { fun Point3D.toMeta() = Meta {
VisualObject3D.x put x VisualObject3D.x put x
VisualObject3D.y put y VisualObject3D.y put y
VisualObject3D.z put z VisualObject3D.z put z

View File

@ -1,17 +1,19 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.descriptor import hep.dataforge.meta.double
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.toName import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.internal.DoubleSerializer import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.internal.StringDescriptor import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.internal.nullable import kotlinx.serialization.builtins.serializer
inline fun <R> Decoder.decodeStructure( inline fun <R> Decoder.decodeStructure(
desc: SerialDescriptor, desc: SerialDescriptor,
vararg typeParams: KSerializer<*> = emptyArray(), vararg typeParams: KSerializer<*> = emptyArray(),
crossinline block: CompositeDecoder.() -> R crossinline block: CompositeDecoder.() -> R
): R { ): R {
val decoder = beginStructure(desc, *typeParams) val decoder = beginStructure(desc, *typeParams)
val res = decoder.block() val res = decoder.block()
@ -31,7 +33,7 @@ inline fun Encoder.encodeStructure(
@Serializer(Point3D::class) @Serializer(Point3D::class)
object Point3DSerializer : KSerializer<Point3D> { object Point3DSerializer : KSerializer<Point3D> {
override val descriptor: SerialDescriptor = descriptor("hep.dataforge.vis.spatial.Point3D") { override val descriptor: SerialDescriptor = SerialDescriptor("hep.dataforge.vis.spatial.Point3D") {
double("x", true) double("x", true)
double("y", true) double("y", true)
double("z", true) double("z", true)
@ -45,28 +47,28 @@ object Point3DSerializer : KSerializer<Point3D> {
loop@ while (true) { loop@ while (true) {
when (val i = decodeElementIndex(descriptor)) { when (val i = decodeElementIndex(descriptor)) {
CompositeDecoder.READ_DONE -> break@loop CompositeDecoder.READ_DONE -> break@loop
0 -> x = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0 0 -> x = decodeNullableSerializableElement(descriptor, 0, Double.serializer().nullable) ?: 0.0
1 -> y = decodeNullableSerializableElement(descriptor, 1, DoubleSerializer.nullable) ?: 0.0 1 -> y = decodeNullableSerializableElement(descriptor, 1, Double.serializer().nullable) ?: 0.0
2 -> z = decodeNullableSerializableElement(descriptor, 2, DoubleSerializer.nullable) ?: 0.0 2 -> z = decodeNullableSerializableElement(descriptor, 2, Double.serializer().nullable) ?: 0.0
else -> throw SerializationException("Unknown index $i") else -> throw SerializationException("Unknown index $i")
} }
} }
} }
return Point3D(x?:0.0, y?:0.0, z?:0.0) return Point3D(x ?: 0.0, y ?: 0.0, z ?: 0.0)
} }
override fun serialize(encoder: Encoder, obj: Point3D) { override fun serialize(encoder: Encoder, value: Point3D) {
encoder.encodeStructure(descriptor) { encoder.encodeStructure(descriptor) {
if (obj.x != 0.0) encodeDoubleElement(descriptor, 0, obj.x) if (value.x != 0.0) encodeDoubleElement(descriptor, 0, value.x)
if (obj.y != 0.0) encodeDoubleElement(descriptor, 1, obj.y) if (value.y != 0.0) encodeDoubleElement(descriptor, 1, value.y)
if (obj.z != 0.0) encodeDoubleElement(descriptor, 2, obj.z) if (value.z != 0.0) encodeDoubleElement(descriptor, 2, value.z)
} }
} }
} }
@Serializer(Point2D::class) @Serializer(Point2D::class)
object Point2DSerializer : KSerializer<Point2D> { object Point2DSerializer : KSerializer<Point2D> {
override val descriptor: SerialDescriptor = descriptor("hep.dataforge.vis.spatial.Point2D") { override val descriptor: SerialDescriptor = SerialDescriptor("hep.dataforge.vis.spatial.Point2D") {
double("x", true) double("x", true)
double("y", true) double("y", true)
} }
@ -78,32 +80,48 @@ object Point2DSerializer : KSerializer<Point2D> {
loop@ while (true) { loop@ while (true) {
when (val i = decodeElementIndex(descriptor)) { when (val i = decodeElementIndex(descriptor)) {
CompositeDecoder.READ_DONE -> break@loop CompositeDecoder.READ_DONE -> break@loop
0 -> x = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0 0 -> x = decodeNullableSerializableElement(descriptor, 0, Double.serializer().nullable) ?: 0.0
1 -> y = decodeNullableSerializableElement(descriptor, 1, DoubleSerializer.nullable) ?: 0.0 1 -> y = decodeNullableSerializableElement(descriptor, 1, Double.serializer().nullable) ?: 0.0
else -> throw SerializationException("Unknown index $i") else -> throw SerializationException("Unknown index $i")
} }
} }
} }
return Point2D(x?:0.0, y?:0.0) return Point2D(x ?: 0.0, y ?: 0.0)
} }
override fun serialize(encoder: Encoder, obj: Point2D) { override fun serialize(encoder: Encoder, value: Point2D) {
encoder.encodeStructure(descriptor) { encoder.encodeStructure(descriptor) {
if (obj.x != 0.0) encodeDoubleElement(descriptor, 0, obj.x) if (value.x != 0.0) encodeDoubleElement(descriptor, 0, value.x)
if (obj.y != 0.0) encodeDoubleElement(descriptor, 1, obj.y) if (value.y != 0.0) encodeDoubleElement(descriptor, 1, value.y)
} }
} }
} }
@Serializer(NameToken::class) @Serializer(MutableVisualGroup::class)
object NameTokenSerializer : KSerializer<NameToken> { internal object PrototypesSerializer : KSerializer<MutableVisualGroup> {
override val descriptor: SerialDescriptor = StringDescriptor.withName("NameToken")
override fun deserialize(decoder: Decoder): NameToken { private val mapSerializer: KSerializer<Map<NameToken, VisualObject>> =
return decoder.decodeString().toName().first()!! MapSerializer(
NameToken.serializer(),
VisualObject.serializer()
)
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
override fun deserialize(decoder: Decoder): MutableVisualGroup {
val map = mapSerializer.deserialize(decoder)
return Prototypes(map as? MutableMap<NameToken, VisualObject> ?: LinkedHashMap(map))
} }
override fun serialize(encoder: Encoder, obj: NameToken) { override fun serialize(encoder: Encoder, value: MutableVisualGroup) {
encoder.encodeString(obj.toString()) mapSerializer.serialize(encoder, value.children)
}
}
fun VisualObject.stringify(): String = Visual3D.json.stringify(VisualObject.serializer(), this)
fun VisualObject.Companion.parseJson(str: String) = Visual3D.json.parse(VisualObject.serializer(), str).also {
if(it is VisualGroup){
it.attachChildren()
} }
} }

View File

@ -2,16 +2,13 @@ package hep.dataforge.vis.spatial.specifications
import hep.dataforge.meta.* import hep.dataforge.meta.*
class AxesSpec(override val config: Config) : Specific { class Axes : Scheme() {
var visible by boolean(!config.isEmpty()) var visible by boolean(!config.isEmpty())
var size by double(AXIS_SIZE) var size by double(AXIS_SIZE)
var width by double(AXIS_WIDTH) var width by double(AXIS_WIDTH)
companion object : Specification<AxesSpec> { companion object : SchemeSpec<Axes>(::Axes) {
override fun wrap(config: Config): AxesSpec = AxesSpec(config)
const val AXIS_SIZE = 1000.0 const val AXIS_SIZE = 1000.0
const val AXIS_WIDTH = 3.0 const val AXIS_WIDTH = 3.0
} }
} }

View File

@ -1,10 +1,14 @@
package hep.dataforge.vis.spatial.specifications package hep.dataforge.vis.spatial.specifications
import hep.dataforge.meta.* import hep.dataforge.meta.Scheme
import hep.dataforge.meta.SchemeSpec
import hep.dataforge.meta.double
import hep.dataforge.meta.int
import kotlin.math.PI import kotlin.math.PI
class CameraSpec(override val config: Config) : Specific { class Camera : Scheme() {
var fov by int(FIELD_OF_VIEW) var fov by int(FIELD_OF_VIEW)
//var aspect by double(1.0) //var aspect by double(1.0)
var nearClip by double(NEAR_CLIP) var nearClip by double(NEAR_CLIP)
var farClip by double(FAR_CLIP) var farClip by double(FAR_CLIP)
@ -14,11 +18,10 @@ class CameraSpec(override val config: Config) : Specific {
var latitude by double(INITIAL_LATITUDE) var latitude by double(INITIAL_LATITUDE)
val zenith: Double get() = PI / 2 - latitude val zenith: Double get() = PI / 2 - latitude
companion object : Specification<CameraSpec> { companion object : SchemeSpec<Camera>(::Camera) {
override fun wrap(config: Config): CameraSpec = CameraSpec(config)
const val INITIAL_DISTANCE = 300.0 const val INITIAL_DISTANCE = 300.0
const val INITIAL_AZIMUTH = 0.0 const val INITIAL_AZIMUTH = 0.0
const val INITIAL_LATITUDE = PI/6 const val INITIAL_LATITUDE = PI / 6
const val NEAR_CLIP = 0.1 const val NEAR_CLIP = 0.1
const val FAR_CLIP = 10000.0 const val FAR_CLIP = 10000.0
const val FIELD_OF_VIEW = 75 const val FIELD_OF_VIEW = 75

View File

@ -0,0 +1,15 @@
package hep.dataforge.vis.spatial.specifications
import hep.dataforge.meta.Scheme
import hep.dataforge.meta.SchemeSpec
import hep.dataforge.meta.int
import hep.dataforge.meta.spec
class Canvas : Scheme() {
var axes by spec(Axes, Axes.empty())
var camera by spec(Camera, Camera.empty())
var controls by spec(Controls, Controls.empty())
var minSize by int(300)
companion object : SchemeSpec<Canvas>(::Canvas)
}

View File

@ -1,15 +0,0 @@
package hep.dataforge.vis.spatial.specifications
import hep.dataforge.meta.*
class CanvasSpec(override val config: Config) : Specific {
var axes by spec(AxesSpec)
var camera by spec(CameraSpec)
var controls by spec(ControlsSpec)
var minSize by int(300)
companion object: Specification<CanvasSpec>{
override fun wrap(config: Config): CanvasSpec = CanvasSpec(config)
}
}

View File

@ -0,0 +1,9 @@
package hep.dataforge.vis.spatial.specifications
import hep.dataforge.meta.Scheme
import hep.dataforge.meta.SchemeSpec
class Controls : Scheme() {
companion object : SchemeSpec<Controls>(::Controls)
}

View File

@ -1,11 +0,0 @@
package hep.dataforge.vis.spatial.specifications
import hep.dataforge.meta.Config
import hep.dataforge.meta.Specific
import hep.dataforge.meta.Specification
class ControlsSpec(override val config: Config) : Specific {
companion object : Specification<ControlsSpec> {
override fun wrap(config: Config): ControlsSpec = ControlsSpec(config)
}
}

View File

@ -2,9 +2,9 @@ package hep.dataforge.vis.spatial.transform
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.vis.common.MutableVisualGroup import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
internal fun mergeChild(parent: VisualGroup, child: VisualObject): VisualObject { internal fun mergeChild(parent: VisualGroup, child: VisualObject): VisualObject {

View File

@ -2,11 +2,10 @@ package hep.dataforge.vis.spatial.transform
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.vis.common.MutableVisualGroup import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.spatial.Proxy import hep.dataforge.vis.spatial.Proxy
import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.prototypes
object UnRef : VisualTreeTransform<VisualGroup3D>() { object UnRef : VisualTreeTransform<VisualGroup3D>() {
private fun VisualGroup.countRefs(): Map<Name, Int> { private fun VisualGroup.countRefs(): Map<Name, Int> {

View File

@ -1,6 +1,6 @@
package hep.dataforge.vis.spatial.transform package hep.dataforge.vis.spatial.transform
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
/** /**
* A root class for [VisualObject] tree optimization * A root class for [VisualObject] tree optimization

View File

@ -1,8 +1,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.toMeta
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.getIndexed import hep.dataforge.meta.getIndexed
import hep.dataforge.meta.node
import hep.dataforge.meta.toMetaItem
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -26,9 +27,9 @@ class ConvexTest {
val convex = group.first() as Convex val convex = group.first() as Convex
val json = Visual3D.json.toJson(Convex.serializer(), convex) val json = Visual3D.json.toJson(Convex.serializer(), convex)
val meta = json.toMeta() val meta = json.toMetaItem().node!!
val points = meta.getIndexed("points").values.map { (it as MetaItem.NodeItem<*>).node.point3D()} val points = meta.getIndexed("points").values.map { (it as MetaItem.NodeItem<*>).node.point3D() }
assertEquals(8, points.count()) assertEquals(8, points.count())
assertEquals(8, convex.points.size) assertEquals(8, convex.points.size)

View File

@ -1,7 +1,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.Colors
import hep.dataforge.vis.common.get import hep.dataforge.vis.get
import kotlin.math.PI import kotlin.math.PI
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -3,7 +3,7 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.int import hep.dataforge.meta.int
import hep.dataforge.meta.set import hep.dataforge.meta.set
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.vis.common.useStyle import hep.dataforge.vis.useStyle
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -1,12 +1,12 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.spatial.Visual3D.Companion.json import hep.dataforge.names.toName
import kotlinx.serialization.ImplicitReflectionSerializer import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.get
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class SerializationTest { class SerializationTest {
@ImplicitReflectionSerializer
@Test @Test
fun testCubeSerialization() { fun testCubeSerialization() {
val cube = Box(100f, 100f, 100f).apply { val cube = Box(100f, 100f, 100f).apply {
@ -14,9 +14,30 @@ class SerializationTest {
x = 100 x = 100
z = -100 z = -100
} }
val string = json.stringify(Box.serializer(), cube) val string = cube.stringify()
println(string) println(string)
val newCube = json.parse(Box.serializer(), string) val newCube = VisualObject.parseJson(string)
assertEquals(cube.config, newCube.config) assertEquals(cube.config, newCube.config)
} }
@Test
fun testProxySerialization() {
val cube = Box(100f, 100f, 100f).apply {
color(222)
x = 100
z = -100
}
val group = VisualGroup3D().apply {
proxy("cube", cube)
proxyGroup("pg", "pg.content".toName()){
sphere(50){
x = -100
}
}
}
val string = group.stringify()
println(string)
val reconstructed = VisualGroup3D.parseJson(string)
assertEquals(group["cube"]?.config, reconstructed["cube"]?.config)
}
} }

View File

@ -2,18 +2,19 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.getProperty
import hep.dataforge.meta.string import hep.dataforge.meta.string
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.output.Renderer import hep.dataforge.output.Renderer
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.Colors
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.specifications.CameraSpec import hep.dataforge.vis.spatial.specifications.Camera
import hep.dataforge.vis.spatial.specifications.CanvasSpec import hep.dataforge.vis.spatial.specifications.Canvas
import hep.dataforge.vis.spatial.specifications.ControlsSpec import hep.dataforge.vis.spatial.specifications.Controls
import hep.dataforge.vis.spatial.three.ThreeMaterials.HIGHLIGHT_MATERIAL import hep.dataforge.vis.spatial.three.ThreeMaterials.HIGHLIGHT_MATERIAL
import hep.dataforge.vis.spatial.three.ThreeMaterials.SELECTED_MATERIAL
import info.laht.threekt.WebGLRenderer import info.laht.threekt.WebGLRenderer
import info.laht.threekt.cameras.PerspectiveCamera import info.laht.threekt.cameras.PerspectiveCamera
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
@ -23,6 +24,7 @@ import info.laht.threekt.external.controls.OrbitControls
import info.laht.threekt.external.controls.TrackballControls import info.laht.threekt.external.controls.TrackballControls
import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.geometries.EdgesGeometry
import info.laht.threekt.helpers.AxesHelper import info.laht.threekt.helpers.AxesHelper
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.math.Vector2 import info.laht.threekt.math.Vector2
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
@ -39,7 +41,7 @@ import kotlin.math.sin
/** /**
* *
*/ */
class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: CanvasSpec) : Renderer<VisualObject3D> { class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canvas) : Renderer<VisualObject3D> {
override val context: Context get() = three.context override val context: Context get() = three.context
@ -51,17 +53,19 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
private val raycaster = Raycaster() private val raycaster = Raycaster()
private val mousePosition: Vector2 = Vector2() private val mousePosition: Vector2 = Vector2()
var clickListener: ((Name) -> Unit)? = null var onClick: ((Name?) -> Unit)? = null
val axes = AxesHelper(spec.axes.size.toInt()).apply { val axes = AxesHelper(canvas.axes.size.toInt()).apply {
visible = spec.axes.visible visible = canvas.axes.visible
} }
val scene: Scene = Scene().apply { val scene: Scene = Scene().apply {
add(axes) add(axes)
} }
val camera = buildCamera(spec.camera) val camera = buildCamera(canvas.camera)
private var picked: Object3D? = null
init { init {
element.clear() element.clear()
@ -75,29 +79,26 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
} }
}, false) }, false)
element.addEventListener("mousedown", { event -> element.addEventListener("mousedown", {
val mesh = pick() val picked = pick()
if (mesh != null) { onClick?.invoke(picked?.fullName())
val name = mesh.fullName()
clickListener?.invoke(name)
}
}, false) }, false)
camera.aspect = 1.0 camera.aspect = 1.0
val renderer = WebGLRenderer { antialias = true }.apply { val renderer = WebGLRenderer { antialias = true }.apply {
setClearColor(Colors.skyblue, 1) setClearColor(Colors.skyblue, 1)
} }
addControls(renderer.domElement, spec.controls) addControls(renderer.domElement, canvas.controls)
fun animate() { fun animate() {
val mesh = pick() val picked = pick()
if (mesh != null && highlighted != mesh) { if (picked != null && this.picked != picked) {
highlighted?.toggleHighlight(false) this.picked?.toggleHighlight(false,HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL)
mesh.toggleHighlight(true) picked.toggleHighlight(true, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL)
this.picked = picked
} }
window.requestAnimationFrame { window.requestAnimationFrame {
@ -108,7 +109,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
element.appendChild(renderer.domElement) element.appendChild(renderer.domElement)
renderer.setSize(max(spec.minSize, element.offsetWidth), max(spec.minSize, element.offsetWidth)) renderer.setSize(max(canvas.minSize, element.offsetWidth), max(canvas.minSize, element.offsetWidth))
element.onresize = { element.onresize = {
renderer.setSize(element.offsetWidth, element.offsetWidth) renderer.setSize(element.offsetWidth, element.offsetWidth)
@ -118,18 +119,6 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
animate() animate()
} }
private fun pick(): Mesh? {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mousePosition, camera)
// calculate objects intersecting the picking ray
return root?.let { root ->
val intersects = raycaster.intersectObject(root, true)
val intersect = intersects.firstOrNull()
intersect?.`object` as? Mesh
}
}
/** /**
* Resolve full name of the object relative to the global root * Resolve full name of the object relative to the global root
*/ */
@ -142,7 +131,26 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
} }
} }
private fun buildCamera(spec: CameraSpec) = PerspectiveCamera( private fun Object3D.isStatic(): Boolean {
return false
}
private fun Object3D?.upTrace(): Object3D? = if (this?.name?.startsWith("@") == true) parent else this
private fun pick(): Object3D? {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mousePosition, camera)
// calculate objects intersecting the picking ray
return root?.let { root ->
val intersects = raycaster.intersectObject(root, true)
val obj = intersects.map { it.`object` }.firstOrNull { !it.isStatic() }
obj.upTrace()
}
}
private fun buildCamera(spec: Camera) = PerspectiveCamera(
spec.fov, spec.fov,
1.0, 1.0,
spec.nearClip, spec.nearClip,
@ -153,18 +161,23 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth)) translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth))
} }
private fun addControls(element: Node, controlsSpec: ControlsSpec) { private fun addControls(element: Node, controls: Controls) {
when (controlsSpec["type"].string) { when (controls.getProperty("type").string) {
"trackball" -> TrackballControls(camera, element) "trackball" -> TrackballControls(camera, element)
else -> OrbitControls(camera, element) else -> OrbitControls(camera, element)
} }
} }
override fun render(obj: VisualObject3D, meta: Meta) { fun clear(){
//clear old root
scene.children.find { it.name == "@root" }?.let { scene.children.find { it.name == "@root" }?.let {
scene.remove(it) scene.remove(it)
} }
}
override fun render(obj: VisualObject3D, meta: Meta) {
//clear old root
clear()
val object3D = three.buildObject3D(obj) val object3D = three.buildObject3D(obj)
object3D.name = "@root" object3D.name = "@root"
@ -173,43 +186,65 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
root = object3D root = object3D
} }
private var highlighted: Mesh? = null private var highlighted: Object3D? = null
/** /**
* Toggle highlight for the given [Mesh] object * Toggle highlight for the given [Mesh] object
*/ */
private fun Mesh.toggleHighlight(highlight: Boolean) { private fun Object3D.toggleHighlight(
if (highlight) { highlight: Boolean,
val edges = LineSegments( edgesName: String,
EdgesGeometry(geometry as BufferGeometry), material: LineBasicMaterial = SELECTED_MATERIAL
HIGHLIGHT_MATERIAL ) {
).apply { if (userData[DO_NOT_HIGHLIGHT_TAG] == true) {
name = "@highlight" return
}
if (this is Mesh) {
if (highlight) {
val edges = LineSegments(
EdgesGeometry(geometry as BufferGeometry),
material
).apply {
name = edgesName
}
add(edges)
} else {
val highlightEdges = children.find { it.name == edgesName }
highlightEdges?.let { remove(it) }
} }
add(edges)
highlighted = this
} else { } else {
val highlightEdges = children.find { it.name == "@highlight" } children.filter { it.name != edgesName }.forEach {
highlightEdges?.let { remove(it) } it.toggleHighlight(highlight, edgesName, material)
}
} }
} }
/** /**
* Toggle highlight for element with given name * Toggle highlight for element with given name
*/ */
fun highlight(name: Name?) { fun select(name: Name?) {
if (name == null) { if (name == null) {
highlighted?.toggleHighlight(false) highlighted?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
highlighted = null highlighted = null
return return
} }
val mesh = root?.findChild(name) as? Mesh val obj = root?.findChild(name)
if (mesh != null && highlighted != mesh) { if (obj != null && highlighted != obj) {
highlighted?.toggleHighlight(false) highlighted?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
mesh.toggleHighlight(true) obj.toggleHighlight(true, SELECT_NAME, SELECTED_MATERIAL)
highlighted = obj
} }
} }
companion object {
const val DO_NOT_HIGHLIGHT_TAG = "doNotHighlight"
private const val HIGHLIGHT_NAME = "@highlight"
private const val SELECT_NAME = "@select"
}
} }
fun ThreePlugin.output(element: HTMLElement, spec: CanvasSpec = CanvasSpec.empty()): ThreeCanvas = fun ThreePlugin.output(element: HTMLElement, spec: Canvas = Canvas.empty()): ThreeCanvas =
ThreeCanvas(element, this, spec) ThreeCanvas(element, this, spec)
fun ThreePlugin.render(element: HTMLElement, obj: VisualObject3D, spec: Canvas = Canvas.empty()): Unit =
output(element, spec).render(obj)

View File

@ -0,0 +1,76 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.context.Context
import hep.dataforge.names.Name
import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.specifications.Canvas
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import react.RBuilder
import react.RComponent
import react.RProps
import react.RState
import react.dom.div
import react.dom.findDOMNode
interface ThreeCanvasProps : RProps {
var context: Context
var obj: VisualObject3D
var options: Canvas?
var selected: Name?
var clickCallback: (Name?) -> Unit
var canvasCallback: ((ThreeCanvas?) -> Unit)?
}
interface ThreeCanvasState : RState {
var element: Element?
// var canvas: ThreeCanvas?
}
class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() {
var canvas: ThreeCanvas? = null
override fun componentDidMount() {
if(canvas == null) {
val element = state.element as? HTMLElement ?: error("Canvas element not found")
val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin)
canvas = three.output(element, props.options ?: Canvas.empty()).apply {
onClick = props.clickCallback
}
props.canvasCallback?.invoke(canvas)
}
canvas?.render(props.obj)
}
// override fun componentWillUnmount() {
// state.element?.clear()
// props.canvasCallback?.invoke(null)
// }
override fun componentDidUpdate(prevProps: ThreeCanvasProps, prevState: ThreeCanvasState, snapshot: Any) {
if (prevProps.obj != props.obj) {
componentDidMount()
}
if (prevProps.selected != props.selected) {
canvas?.select(props.selected)
}
}
override fun RBuilder.render() {
div {
ref {
state.element = findDOMNode(it)
}
}
}
}
fun RBuilder.threeCanvas(object3D: VisualObject3D, options: Canvas.() -> Unit = {}) {
child(ThreeCanvasComponent::class) {
attrs {
this.obj = object3D
this.options = Canvas.invoke(options)
}
}
}

View File

@ -2,6 +2,7 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.spatial.Label3D import hep.dataforge.vis.spatial.Label3D
import hep.dataforge.vis.spatial.color import hep.dataforge.vis.spatial.color
import hep.dataforge.vis.spatial.three.ThreeCanvas.Companion.DO_NOT_HIGHLIGHT_TAG
import info.laht.threekt.DoubleSide import info.laht.threekt.DoubleSide
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.geometries.PlaneBufferGeometry import info.laht.threekt.geometries.PlaneBufferGeometry
@ -18,7 +19,7 @@ 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<Label3D> { object ThreeCanvasLabelFactory : ThreeFactory<Label3D> {
override val type: KClass<in Label3D> get() = Label3D::class override val type: KClass<in Label3D> get() = Label3D::class
override fun invoke(obj: Label3D): Object3D { override fun invoke(obj: Label3D): Object3D {
@ -31,7 +32,7 @@ object ThreeCanvasLabelFactory: ThreeFactory<Label3D> {
//canvas.width = metrics.width.toInt() //canvas.width = metrics.width.toInt()
context.fillText(obj.text, (canvas.width - metrics.width)/2, 0.5*canvas.height) context.fillText(obj.text, (canvas.width - metrics.width) / 2, 0.5 * canvas.height)
// canvas contents will be used for a texture // canvas contents will be used for a texture
@ -51,6 +52,7 @@ object ThreeCanvasLabelFactory: ThreeFactory<Label3D> {
mesh.updatePosition(obj) mesh.updatePosition(obj)
mesh.userData[DO_NOT_HIGHLIGHT_TAG] = true
return mesh return mesh
} }
} }

View File

@ -3,7 +3,7 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE

View File

@ -2,8 +2,8 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.Colors
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.Material3D import hep.dataforge.vis.spatial.Material3D
import info.laht.threekt.materials.LineBasicMaterial import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.Material import info.laht.threekt.materials.Material
@ -22,11 +22,17 @@ object ThreeMaterials {
color.set(DEFAULT_LINE_COLOR) color.set(DEFAULT_LINE_COLOR)
} }
val HIGHLIGHT_MATERIAL = LineBasicMaterial().apply { val SELECTED_MATERIAL = LineBasicMaterial().apply {
color.set(Colors.ivory) color.set(Colors.ivory)
linewidth = 8.0 linewidth = 8.0
} }
val HIGHLIGHT_MATERIAL = LineBasicMaterial().apply {
color.set(Colors.blue)
linewidth = 8.0
}
fun getLineMaterial(meta: Meta?): LineBasicMaterial { fun getLineMaterial(meta: Meta?): LineBasicMaterial {
if (meta == null) return DEFAULT_LINE if (meta == null) return DEFAULT_LINE
return LineBasicMaterial().apply { return LineBasicMaterial().apply {

View File

@ -3,7 +3,7 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.context.* import hep.dataforge.context.*
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import kotlin.collections.set import kotlin.collections.set

View File

@ -2,9 +2,8 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.spatial.Point3D import hep.dataforge.vis.spatial.Point3D
import hep.dataforge.vis.spatial.Point3DSerializer import hep.dataforge.vis.spatial.Point3DSerializer
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
@ -26,7 +25,6 @@ class CustomThreeVisualObject(val threeFactory: ThreeFactory<VisualObject3D>) :
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
@Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override fun toObject3D(): Object3D = threeFactory(this) override fun toObject3D(): Object3D = threeFactory(this)

View File

@ -1,4 +1,4 @@
@file:JsModule("@hi-level/three-csg") @file:JsModule("three-csg-ts")
@file:JsNonModule @file:JsNonModule
@file:Suppress( @file:Suppress(
"INTERFACE_WITH_SUPERCLASS", "INTERFACE_WITH_SUPERCLASS",

View File

@ -1,19 +1,25 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.js.accordion
import hep.dataforge.js.entry
import hep.dataforge.js.requireJS import hep.dataforge.js.requireJS
import hep.dataforge.vis.js.editor.accordion
import hep.dataforge.vis.spatial.Visual3D import hep.dataforge.vis.spatial.Visual3D
import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualGroup3D
import kotlinx.html.InputType import kotlinx.html.*
import kotlinx.html.TagConsumer
import kotlinx.html.button
import kotlinx.html.dom.append import kotlinx.html.dom.append
import kotlinx.html.js.* import kotlinx.html.js.onChangeFunction
import kotlinx.html.js.onClickFunction
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
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.dom.button
import react.dom.div
import react.dom.input
import react.dom.label
import kotlin.dom.clear import kotlin.dom.clear
private fun saveData(event: Event, fileName: String, mimeType: String = "text/plain", dataBuilder: () -> String) { private fun saveData(event: Event, fileName: String, mimeType: String = "text/plain", dataBuilder: () -> String) {
@ -25,6 +31,70 @@ private fun saveData(event: Event, fileName: String, mimeType: String = "text/pl
fileSaver.saveAs(blob, fileName) fileSaver.saveAs(blob, fileName)
} }
fun RBuilder.canvasControls(canvas: ThreeCanvas) = accordion("controls") {
entry("Settings") {
div("row") {
div("col-2") {
label("checkbox-inline") {
input(type = InputType.checkBox){
attrs {
defaultChecked = canvas.axes.visible
onChangeFunction = {
canvas.axes.visible = (it.target as HTMLInputElement).checked
}
}
}
+"Axes"
}
}
div("col-1") {
button {
+"Export"
attrs {
onClickFunction = {
val json = (canvas.content as? VisualGroup3D)?.let { group ->
Visual3D.json.stringify(
VisualGroup3D.serializer(),
group
)
}
if (json != null) {
saveData(it, "object.json", "text/json") {
json
}
}
}
}
}
}
}
}
entry("Layers") {
div("row") {
(0..11).forEach { layer ->
div("col-1") {
label { +layer.toString() }
input(type = InputType.checkBox){
attrs {
if (layer == 0) {
defaultChecked = true
}
onChangeFunction = {
if ((it.target as HTMLInputElement).checked) {
canvas.camera.layers.enable(layer)
} else {
canvas.camera.layers.disable(layer)
}
}
}
}
}
}
}
}
}
fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer<HTMLElement>.() -> Unit = {}) { fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer<HTMLElement>.() -> Unit = {}) {
clear() clear()
append { append {
@ -33,7 +103,7 @@ fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer<HTMLEl
div("row") { div("row") {
div("col-2") { div("col-2") {
label("checkbox-inline") { label("checkbox-inline") {
input(type = InputType.checkBox).apply { input(type = InputType.checkBox) {
checked = canvas.axes.visible checked = canvas.axes.visible
onChangeFunction = { onChangeFunction = {
canvas.axes.visible = checked canvas.axes.visible = checked
@ -67,7 +137,7 @@ fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer<HTMLEl
(0..11).forEach { layer -> (0..11).forEach { layer ->
div("col-1") { div("col-1") {
label { +layer.toString() } label { +layer.toString() }
input(type = InputType.checkBox).apply { input(type = InputType.checkBox) {
if (layer == 0) { if (layer == 0) {
checked = true checked = true
} }
@ -84,61 +154,6 @@ fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer<HTMLEl
} }
} }
} }
/* card("Settings") {
div("row") {
div("col-2") {
label("checkbox-inline") {
input(type = InputType.checkBox).apply {
checked = canvas.axes.visible
onChangeFunction = {
canvas.axes.visible = checked
}
}
+"Axes"
}
}
div("col-1") {
button {
+"Export"
onClickFunction = {
val json = (canvas.content as? VisualGroup3D)?.let { group ->
Visual3D.json.stringify(
VisualGroup3D.serializer(),
group
)
}
if (json != null) {
saveData(it, "object.json", "text/json"){
json
}
}
}
}
}
}
}
card("Layers") {
div("row") {
(0..11).forEach { layer ->
div("col-1") {
label { +layer.toString() }
input(type = InputType.checkBox).apply {
if (layer == 0) {
checked = true
}
onChangeFunction = {
if (checked) {
canvas.camera.layers.enable(layer)
} else {
canvas.camera.layers.disable(layer)
}
}
}
}
}
}
}*/
block() block()
} }
} }

View File

@ -153,7 +153,7 @@ open external class Object3D {
* An object that can be used to store custom data about the Object3D. * An object that can be used to store custom data about the Object3D.
* It should not hold references to functions as these will not be cloned. * It should not hold references to functions as these will not be cloned.
*/ */
var userData: Map<String, Any> var userData: dynamic
/** /**
* An optional callback that is executed immediately before the Object3D is rendered. * An optional callback that is executed immediately before the Object3D is rendered.

View File

@ -5,7 +5,7 @@ import hep.dataforge.context.ContextAware
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.output.Renderer import hep.dataforge.output.Renderer
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.specifications.CanvasSpec import hep.dataforge.vis.spatial.specifications.Canvas
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.property.ObjectProperty import javafx.beans.property.ObjectProperty
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
@ -14,7 +14,7 @@ import javafx.scene.paint.Color
import org.fxyz3d.scene.Axes import org.fxyz3d.scene.Axes
import tornadofx.* import tornadofx.*
class FXCanvas3D(val plugin: FX3DPlugin, val spec: CanvasSpec = CanvasSpec.empty()) : class FXCanvas3D(val plugin: FX3DPlugin, val spec: Canvas = Canvas.empty()) :
Fragment(), Renderer<VisualObject3D>, ContextAware { Fragment(), Renderer<VisualObject3D>, ContextAware {
override val context: Context get() = plugin.context override val context: Context get() = plugin.context

View File

@ -5,7 +5,7 @@ import hep.dataforge.meta.double
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.int import hep.dataforge.meta.int
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.Colors
import hep.dataforge.vis.spatial.Material3D import hep.dataforge.vis.spatial.Material3D
import javafx.scene.paint.Color import javafx.scene.paint.Color
import javafx.scene.paint.Material import javafx.scene.paint.Material

View File

@ -3,7 +3,7 @@ package hep.dataforge.vis.spatial.fx
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty import hep.dataforge.names.isEmpty
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.Proxy import hep.dataforge.vis.spatial.Proxy
import javafx.scene.Group import javafx.scene.Group
import javafx.scene.Node import javafx.scene.Node

View File

@ -1,6 +1,5 @@
package hep.dataforge.vis.spatial.fx package hep.dataforge.vis.spatial.fx
import hep.dataforge.vis.spatial.specifications.CameraSpec
import javafx.beans.InvalidationListener import javafx.beans.InvalidationListener
import javafx.beans.property.SimpleDoubleProperty import javafx.beans.property.SimpleDoubleProperty
import javafx.event.EventHandler import javafx.event.EventHandler
@ -15,6 +14,7 @@ import javafx.scene.transform.Rotate
import javafx.scene.transform.Translate import javafx.scene.transform.Translate
import tornadofx.* import tornadofx.*
import kotlin.math.* import kotlin.math.*
import hep.dataforge.vis.spatial.specifications.Camera as CameraSpec
class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: CameraSpec) { class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: CameraSpec) {

View File

@ -4,7 +4,7 @@ import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.VisualObject
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.binding.ObjectBinding import javafx.beans.binding.ObjectBinding
import tornadofx.* import tornadofx.*

View File

@ -1,23 +1,24 @@
package hep.dataforge.vis.spatial.gdml package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.JSON_PRETTY
import hep.dataforge.vis.spatial.*
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import kotlinx.serialization.modules.SerialModule import kotlinx.serialization.modules.SerialModule
import kotlinx.serialization.modules.SerialModuleCollector import kotlinx.serialization.modules.SerialModuleCollector
import kotlin.reflect.KClass import kotlin.reflect.KClass
internal val SerialDescriptor.jsonType private fun SerialDescriptor.getJsonType() = when (this.kind) {
get() = when (this.kind) { StructureKind.LIST -> "array"
StructureKind.LIST -> "array" PrimitiveKind.BYTE, PrimitiveKind.SHORT, PrimitiveKind.INT, PrimitiveKind.LONG,
PrimitiveKind.BYTE, PrimitiveKind.SHORT, PrimitiveKind.INT, PrimitiveKind.LONG, PrimitiveKind.FLOAT, PrimitiveKind.DOUBLE -> "number"
PrimitiveKind.FLOAT, PrimitiveKind.DOUBLE -> "number" PrimitiveKind.STRING, PrimitiveKind.CHAR, UnionKind.ENUM_KIND -> "string"
PrimitiveKind.STRING, PrimitiveKind.CHAR, UnionKind.ENUM_KIND -> "string" PrimitiveKind.BOOLEAN -> "boolean"
PrimitiveKind.BOOLEAN -> "boolean" else -> "object"
else -> "object" }
}
private fun SerialDescriptor.isVisualObject() = serialName.startsWith("3d")||serialName.startsWith("group")
private const val definitionNode = "\$defs"
private fun SerialModule.enumerate(type: KClass<*>): Sequence<SerialDescriptor> { private fun SerialModule.enumerate(type: KClass<*>): Sequence<SerialDescriptor> {
val list = ArrayList<SerialDescriptor>() val list = ArrayList<SerialDescriptor>()
@ -60,13 +61,16 @@ private fun SerialModule.enumerate(type: KClass<*>): Sequence<SerialDescriptor>
*/ */
private fun jsonSchema(descriptor: SerialDescriptor, context: SerialModule): JsonObject { private fun jsonSchema(descriptor: SerialDescriptor, context: SerialModule): JsonObject {
if (descriptor.name in arrayOf( if (descriptor.serialName in arrayOf(
"hep.dataforge.vis.spatial.Point3D", "hep.dataforge.vis.spatial.Point3D",
"hep.dataforge.vis.spatial.Point3D?",
"hep.dataforge.vis.spatial.Point2D", "hep.dataforge.vis.spatial.Point2D",
Meta::class.qualifiedName "hep.dataforge.vis.spatial.Point2D?",
"hep.dataforge.meta.Meta",
"hep.dataforge.meta.Meta?"
) )
) return json { ) return json {
"\$ref" to "#/definitions/${descriptor.name}" "\$ref" to "#/$definitionNode/${descriptor.serialName.replace("?", "")}"
} }
@ -79,37 +83,35 @@ private fun jsonSchema(descriptor: SerialDescriptor, context: SerialModule): Jso
if (!isEnum && !isPolymorphic) descriptor.elementDescriptors().forEachIndexed { index, child -> if (!isEnum && !isPolymorphic) descriptor.elementDescriptors().forEachIndexed { index, child ->
val elementName = descriptor.getElementName(index) val elementName = descriptor.getElementName(index)
properties[elementName] = when (elementName) { val elementSchema = when (elementName) {
"templates" -> json { "properties" -> json {
"\$ref" to "#/definitions/hep.dataforge.vis.spatial.VisualGroup3D" "\$ref" to "#/$definitionNode/hep.dataforge.meta.Meta"
} }
"properties" -> json { "first", "second" -> json {
"\$ref" to "#/definitions/${Meta::class.qualifiedName}" "\$ref" to "#/$definitionNode/children"
}
"first", "second" -> json{
"\$ref" to "#/definitions/children"
} }
"styleSheet" -> json { "styleSheet" -> json {
"type" to "object" "type" to "object"
"additionalProperties" to json { "additionalProperties" to json {
"\$ref" to "#/definitions/${Meta::class.qualifiedName}" "\$ref" to "#/$definitionNode/hep.dataforge.meta.Meta"
} }
} }
in arrayOf("children") -> json { in arrayOf("children", "prototypes") -> json {
"type" to "object" "type" to "object"
"additionalProperties" to json { "additionalProperties" to json {
"\$ref" to "#/definitions/children" "\$ref" to "#/$definitionNode/children"
} }
} }
else -> jsonSchema(child, context) else -> jsonSchema(child, context)
} }
properties[elementName] = elementSchema
if (!descriptor.isElementOptional(index)) requiredProperties.add(elementName) if (!descriptor.isElementOptional(index)) requiredProperties.add(elementName)
} }
val jsonType = descriptor.jsonType val jsonType = descriptor.getJsonType()
val objectData: MutableMap<String, JsonElement> = mutableMapOf( val objectData: MutableMap<String, JsonElement> = mutableMapOf(
"description" to JsonLiteral(descriptor.name), "description" to JsonLiteral(descriptor.serialName),
"type" to JsonLiteral(jsonType) "type" to JsonLiteral(jsonType)
) )
if (isEnum) { if (isEnum) {
@ -118,6 +120,11 @@ private fun jsonSchema(descriptor: SerialDescriptor, context: SerialModule): Jso
} }
when (jsonType) { when (jsonType) {
"object" -> { "object" -> {
if(descriptor.isVisualObject()) {
properties["type"] = json {
"const" to descriptor.serialName
}
}
objectData["properties"] = JsonObject(properties) objectData["properties"] = JsonObject(properties)
val required = requiredProperties.map { JsonLiteral(it) } val required = requiredProperties.map { JsonLiteral(it) }
if (required.isNotEmpty()) { if (required.isNotEmpty()) {
@ -140,9 +147,9 @@ fun main() {
"children" to json { "children" to json {
"anyOf" to jsonArray { "anyOf" to jsonArray {
context.enumerate(VisualObject3D::class).forEach { context.enumerate(VisualObject3D::class).forEach {
if (it.name == "hep.dataforge.vis.spatial.VisualGroup3D") { if (it.serialName == "hep.dataforge.vis.spatial.VisualGroup3D") {
+json { +json {
"\$ref" to "#/definitions/${it.name}" "\$ref" to "#/$definitionNode/${it.serialName}"
} }
} else { } else {
+jsonSchema(it, context) +jsonSchema(it, context)
@ -150,18 +157,47 @@ fun main() {
} }
} }
} }
"hep.dataforge.vis.spatial.Point3D" to jsonSchema(Point3DSerializer.descriptor, context) "hep.dataforge.meta.Meta" to json {
"hep.dataforge.vis.spatial.Point2D" to jsonSchema(Point2DSerializer.descriptor, context) "type" to "object"
"hep.dataforge.vis.spatial.VisualGroup3D" to jsonSchema(VisualGroup3D.serializer().descriptor, context) }
"hep.dataforge.vis.spatial.Point3D" to json {
"type" to "object"
"properties" to json {
"x" to json {
"type" to "number"
}
"y" to json {
"type" to "number"
}
"z" to json {
"type" to "number"
}
}
}
"hep.dataforge.vis.spatial.Point2D" to json {
"type" to "object"
"properties" to json {
"x" to json {
"type" to "number"
}
"y" to json {
"type" to "number"
}
}
}
"hep.dataforge.vis.spatial.VisualGroup3D" to jsonSchema(
VisualGroup3D.serializer().descriptor,
context
)
} }
println( println(
Json.indented.stringify( JSON_PRETTY.stringify(
JsonObjectSerializer, JsonObjectSerializer,
json { json {
"definitions" to definitions "\$defs" to definitions
"\$ref" to "#/definitions/hep.dataforge.vis.spatial.VisualGroup3D" "\$ref" to "#/$definitionNode/hep.dataforge.vis.spatial.VisualGroup3D"
} }
) )
) )

View File

@ -1,11 +1,15 @@
import org.openjfx.gradle.JavaFXOptions import scientifik.DependencyConfiguration
import scientifik.FXModule
import scientifik.fx
plugins { plugins {
id("scientifik.mpp") id("scientifik.mpp")
id("org.openjfx.javafxplugin")
id("application") id("application")
} }
val fxVersion: String by rootProject.extra
fx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
kotlin { kotlin {
jvm { jvm {
@ -27,11 +31,6 @@ kotlin {
api(project(":dataforge-vis-spatial-gdml")) api(project(":dataforge-vis-spatial-gdml"))
} }
} }
jvmMain{
dependencies {
api("org.fxyz3d:fxyz3d:0.5.2")
}
}
} }
} }
@ -39,6 +38,8 @@ application {
mainClassName = "hep.dataforge.vis.spatial.gdml.demo.GDMLDemoAppKt" mainClassName = "hep.dataforge.vis.spatial.gdml.demo.GDMLDemoAppKt"
} }
configure<JavaFXOptions> { val convertGdmlToJson by tasks.creating(JavaExec::class) {
modules("javafx.controls") group = "application"
classpath = sourceSets["main"].runtimeClasspath
main = "hep.dataforge.vis.spatial.gdml.demo.SaveToJsonKt"
} }

Some files were not shown because too many files have changed in this diff Show More