Merge pull request #3 from mipt-npm/dev

Dev
This commit is contained in:
Peter Klimai 2020-04-19 22:12:50 +03:00 committed by GitHub
commit dfd8cc5d54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
136 changed files with 8053 additions and 2094 deletions

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 {
val kotlinVersion = "1.3.61"
val toolsVersion = "0.3.2"
kotlin("jvm") version kotlinVersion apply false
id("kotlin-dce-js") version kotlinVersion apply false
val toolsVersion = "0.4.2"
id("scientifik.mpp") version toolsVersion apply false
id("scientifik.jvm") version toolsVersion apply false
id("scientifik.js") version toolsVersion apply false
@ -21,21 +18,23 @@ allprojects {
maven("https://dl.bintray.com/pdvrieze/maven")
maven("http://maven.jzy3d.org/releases")
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/rjaros/kotlin")
}
group = "hep.dataforge"
version = "0.1.1-dev"
}
subprojects{
this.useSerialization()
version = "0.1.3-dev"
}
val githubProject by extra("dataforge-vis")
val bintrayRepo by extra("dataforge")
val fxVersion by extra("14")
subprojects {
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 {
id("scientifik.mpp")
id("org.openjfx.javafxplugin")
}
val dataforgeVersion: String by rootProject.extra
//val kvisionVersion: String by rootProject.extra("2.0.0-M1")
useSerialization()
kotlin {
jvm{
withJava()
}
sourceSets {
commonMain{
commonMain {
dependencies {
api("hep.dataforge:dataforge-output:$dataforgeVersion")
}
}
jvmMain{
jvmMain {
dependencies {
api("no.tornado:tornadofx:1.7.19")
api("no.tornado:tornadofx:1.7.20")
//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")
}
api("de.jensd:fontawesomefx-commons:11.0"){
api("de.jensd:fontawesomefx-commons:11.0") {
exclude(group = "org.openjfx")
}
}
}
jsMain{
jsMain {
dependencies {
api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
//api(npm("bootstrap","4.4.1"))
implementation(npm("jsoneditor"))
implementation(npm("file-saver"))
}
}
}
}
configure<JavaFXOptions> {
modules("javafx.controls")
//React, React DOM + Wrappers (chapter 3)
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"))
}
}
}
}

View File

@ -1,4 +1,4 @@
package hep.dataforge.vis.common
package hep.dataforge.vis
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name
@ -20,6 +20,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
*/
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<*>?) {
super.propertyChanged(name, before, after)
forEach {
@ -37,7 +48,12 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
* Add listener for children change
*/
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
*/
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
*/
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
@ -91,16 +136,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
if (child == null) {
removeChild(token)
} else {
attach(child)
setChild(token, child)
}
}
else -> {
//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
}
}
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.names.Name
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
internal data class PropertyListener(
@ -14,7 +15,7 @@ internal data class PropertyListener(
abstract class AbstractVisualObject : VisualObject {
@Transient
override var parent: VisualObject? = null
override var parent: VisualGroup? = null
protected abstract var properties: Config?
@ -22,7 +23,7 @@ abstract class AbstractVisualObject : VisualObject {
get() = properties?.get(STYLE_KEY).stringList
set(value) {
//val allStyles = (field + value).distinct()
setProperty(STYLE_KEY, value)
setProperty(STYLE_KEY, Value.of(value))
updateStyles(value)
}
@ -66,7 +67,7 @@ abstract class AbstractVisualObject : VisualObject {
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
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.int
import kotlin.math.max
@ -234,7 +237,7 @@ object Colors {
/**
* 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()
GREEN_KEY put g.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)
package hep.dataforge.vis.common
package hep.dataforge.vis
import hep.dataforge.io.serialization.MetaSerializer
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import kotlinx.serialization.*
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
/**
* A container for styles
*/
@Serializable
class StyleSheet() {
class StyleSheet private constructor(private val styleMap: MutableMap<String, Meta> = LinkedHashMap()) {
@Transient
internal var owner: VisualObject? = null
@ -20,12 +21,10 @@ class StyleSheet() {
this.owner = owner
}
private val styleMap = HashMap<String, Meta>()
val items: Map<String, Meta> get() = styleMap
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) {
val newStyle = get(key)?.let { buildMeta(it, builder) } ?: buildMeta(builder)
val newStyle = get(key)?.edit(builder) ?: Meta(builder)
set(key, newStyle.seal())
}
companion object: KSerializer<StyleSheet>{
override val descriptor: SerialDescriptor
get() = TODO("Not yet implemented")
@Serializer(StyleSheet::class)
companion object : KSerializer<StyleSheet> {
private val mapSerializer = MapSerializer(String.serializer(), MetaSerializer)
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
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) {
TODO("Not yet implemented")
override fun serialize(encoder: Encoder, value: StyleSheet) {
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.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 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"
}

View File

@ -1,4 +1,4 @@
package hep.dataforge.vis.common
package hep.dataforge.vis
import hep.dataforge.names.*
import hep.dataforge.provider.Provider
@ -6,7 +6,8 @@ import hep.dataforge.provider.Provider
/**
* 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
*/

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.asName
import hep.dataforge.names.toName
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
//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.
*/
@Transient
var parent: VisualObject?
var parent: VisualGroup?
/**
* All properties including styles and prototypes if present, but without inheritance
*/
fun allProperties(): Laminate
/**
* Set property for this object
*/
fun setProperty(name: Name, value: Any?) {
config[name] = value
}
/**
* 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
@ -66,6 +65,10 @@ interface VisualObject : Configurable {
const val TYPE = "visual"
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 TAGS_KEY = "@tags"
@ -77,11 +80,6 @@ interface VisualObject : Configurable {
*/
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.
*/

View File

@ -1,4 +1,4 @@
package hep.dataforge.vis.common
package hep.dataforge.vis
import hep.dataforge.meta.*
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) =
VisualObjectDelegateWrapper(this, name, default, inherited) {
item -> item.string?.let { enumValueOf<E>(it) }
VisualObjectDelegateWrapper(this, name, default, inherited) { item ->
item.string?.let { enumValueOf<E>(it) }
}
//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 */
ul, .objTree-subtree {
ul, .tree {
list-style-type: none;
}
/* Style the caret/arrow */
.objTree-caret {
.tree-caret {
cursor: pointer;
user-select: none; /* Prevent text selection */
}
.objTree-label {
cursor: pointer;
}
/* Create the caret/arrow with a unicode, and style it */
.objTree-caret::before {
.tree-caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
.objTree-leaf::before {
.tree-leaf{
user-select: none;
display: inline-block;
}
.tree-leaf::before {
content: "\25C6";
color: black;
display: inline-block;
margin-right: 6px;
}
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.objTree-caret-down::before {
.tree-caret-down::before {
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.meta.EmptyMeta
import hep.dataforge.meta.Meta
import hep.dataforge.meta.boolean
import javafx.application.Application
@ -20,7 +19,7 @@ import kotlin.reflect.KClass
/**
* 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
private val stages: ObservableSet<Stage> = FXCollections.observableSet()
@ -96,7 +95,8 @@ class FXPlugin(meta: Meta = EmptyMeta) : AbstractPlugin(meta) {
companion object : PluginFactory<FXPlugin> {
override val type: KClass<out FXPlugin> = FXPlugin::class
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.names.Name

View File

@ -3,7 +3,7 @@
* To change this template file, choose Tools | Templates
* 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.get

View File

@ -3,15 +3,15 @@
* To change this template file, choose Tools | Templates
* 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.FontAwesomeIconView
import hep.dataforge.context.Global
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.Config
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.NameToken
import hep.dataforge.vis.fx.dfIconView
import hep.dataforge.vis.dfIconView
import javafx.scene.Node
import javafx.scene.control.*
import javafx.scene.control.cell.TextFieldTreeTableCell
@ -128,7 +128,11 @@ class ConfigEditor(
when (item) {
is FXMetaValue<Config> -> {
text = null
val chooser = ValueChooser.build(Global, item.valueProperty, item.descriptor) {
val chooser = ValueChooser.build(
Global,
item.valueProperty,
item.descriptor
) {
item.set(it)
}
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.descriptors.ItemDescriptor
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
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 index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1
val newName = name.withIndex(index.toString())
set(newName, EmptyMeta)
set(newName, Meta.EMPTY)
get(newName).node!!
} else {
this.setNode(token.asName(), EmptyMeta)
this.setNode(token.asName(), Meta.EMPTY)
//FIXME possible concurrency bug
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) {
val parent = getOrCreateNode()
if (descriptor?.multiple == true) {
parent.append(key, EmptyMeta)
parent.append(key, Meta.EMPTY)
} else {
parent[key] = EmptyMeta
parent[key] = Meta.EMPTY
}
}

View File

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

View File

@ -3,7 +3,7 @@
* To change this template file, choose Tools | Templates
* 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.names.Name

View File

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

View File

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

View File

@ -3,9 +3,9 @@
* To change this template file, choose Tools | Templates
* 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.Value
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.Meta
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.update
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.findStyle
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.findStyle
import javafx.beans.binding.Binding
import javafx.beans.property.SimpleObjectProperty
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.common.VisualObject
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject
import javafx.beans.property.SimpleObjectProperty
import javafx.scene.control.SelectionMode
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.buildMeta
import hep.dataforge.meta.toConfig
import hep.dataforge.meta.Meta
import hep.dataforge.meta.asConfig
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.values.ValueType
import hep.dataforge.vis.fx.editor.ConfigEditor
import hep.dataforge.vis.fx.editor.FXMeta
import hep.dataforge.vis.fx.editor.MetaViewer
import hep.dataforge.vis.editor.ConfigEditor
import hep.dataforge.vis.editor.FXMeta
import hep.dataforge.vis.editor.MetaViewer
import javafx.geometry.Orientation
import tornadofx.*
@ -15,7 +15,7 @@ class MetaEditorDemoApp : App(MetaEditorDemo::class)
class MetaEditorDemo : View("Meta editor demo") {
val meta = buildMeta {
val meta = Meta {
"aNode" put {
"innerNode" put {
"innerValue" put true
@ -23,33 +23,35 @@ class MetaEditorDemo : View("Meta editor demo") {
"b" put 223
"c" put "StringValue"
}
}.toConfig()
}.asConfig()
val descriptor = NodeDescriptor {
node("aNode") {
defineNode("aNode") {
info = "A root demo node"
value("b") {
defineValue("b") {
info = "b number value"
type(ValueType.NUMBER)
}
node("otherNode") {
value("otherValue") {
defineNode("otherNode") {
defineValue("otherValue") {
type(ValueType.BOOLEAN)
default(false)
info = "default value"
}
}
}
value("multiple"){
defineValue("multiple") {
info = "A sns value"
multiple = true
}
}
private val rootNode = FXMeta.root(meta,descriptor)
private val rootNode = FXMeta.root(meta, descriptor)
override val root =
splitpane(Orientation.HORIZONTAL, MetaViewer(rootNode).root, ConfigEditor(rootNode).root)
splitpane(Orientation.HORIZONTAL, MetaViewer(rootNode).root, ConfigEditor(
rootNode
).root)
}
fun main() {

View File

@ -7,7 +7,7 @@ kotlin {
val commonMain by getting {
dependencies {
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.MetaBuilder
import hep.dataforge.meta.buildMeta
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName
import hep.dataforge.vis.common.useStyle
import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
import hep.dataforge.vis.useStyle
import scientifik.gdml.*
import kotlin.random.Random
@ -44,7 +44,7 @@ class GDMLTransformer(val root: GDML) {
fun VisualObject3D.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
styleCache.getOrPut(name.toName()) {
buildMeta(builder)
Meta(builder)
}
useStyle(name)
}
@ -69,7 +69,13 @@ class GDMLTransformer(val root: GDML) {
var onFinish: GDMLTransformer.() -> Unit = {}
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 {
final.styleSheet {
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.asName
import hep.dataforge.names.plus
import hep.dataforge.vis.common.get
import hep.dataforge.vis.common.set
import hep.dataforge.vis.get
import hep.dataforge.vis.set
import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.World.ONE
import hep.dataforge.vis.spatial.World.ZERO

View File

@ -1,43 +1,39 @@
package hep.dataforge.vis.spatial.gdml
import hep.dataforge.vis.spatial.stringify
import nl.adaptivity.xmlutil.StAXReader
import org.junit.Test
import org.junit.jupiter.api.Test
import scientifik.gdml.GDML
import java.io.File
import java.net.URL
import kotlin.test.Ignore
class TestConvertor {
@Test
@Ignore
fun testBMNGeometry() {
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
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 stream = javaClass.getResourceAsStream("/gdml/BM@N.gdml")
val xmlReader = StAXReader(stream, "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
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.useSerialization
import scientifik.serialization
plugins {
id("scientifik.mpp")
id("org.openjfx.javafxplugin")
}
useSerialization()
serialization()
kotlin {
jvm {
withJava()
js {
useCommonJs()
}
sourceSets {
commonMain {
dependencies {
@ -32,14 +31,9 @@ kotlin {
jsMain {
dependencies {
// api(project(":wrappers"))
implementation(npm("three", "0.106.2"))
implementation(npm("@hi-level/three-csg", "1.0.6"))
implementation(npm("three", "0.114.0"))
implementation(npm("three-csg-ts", "1.0.1"))
}
}
}
}
configure<JavaFXOptions> {
modules("javafx.controls")
}

View File

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

View File

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

View File

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

View File

@ -2,10 +2,10 @@
package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.set
import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
@ -14,7 +14,6 @@ import kotlinx.serialization.UseSerializers
@SerialName("3d.convex")
class Convex(val points: List<Point3D>) : AbstractVisualObject(), VisualObject3D {
@Serializable(ConfigSerializer::class)
override var properties: Config? = 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) }
class ConvexBuilder {

View File

@ -1,10 +1,10 @@
@file:UseSerializers(Point2DSerializer::class, Point3DSerializer::class)
package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.set
import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
@ -45,7 +45,6 @@ class Extruded(
var layers: MutableList<Layer> = ArrayList()
) : AbstractVisualObject(), VisualObject3D, Shape {
@Serializable(ConfigSerializer::class)
override var properties: Config? = 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) }

View File

@ -1,6 +1,5 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta
/**
@ -14,7 +13,7 @@ interface GeometryBuilder<T : Any> {
* @param normal optional external normal to the face
* @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
}
@ -25,7 +24,7 @@ fun GeometryBuilder<*>.face4(
vertex3: Point3D,
vertex4: Point3D,
normal: Point3D? = null,
meta: Meta = EmptyMeta
meta: Meta = Meta.EMPTY
) {
face(vertex1, vertex2, vertex3, normal, meta)
face(vertex1, vertex3, vertex4, normal, meta)

View File

@ -2,19 +2,17 @@
package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.set
import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
@Serializable
@SerialName("3d.label")
class Label3D(var text: String, var fontSize: Double, var fontFamily: String) : AbstractVisualObject(),
VisualObject3D {
@Serializable(ConfigSerializer::class)
class Label3D(var text: String, var fontSize: Double, var fontFamily: String) : AbstractVisualObject(), VisualObject3D {
override var properties: Config? = 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,
fontSize: Number = 20,
fontFamily: String = "Arial",

View File

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

View File

@ -2,13 +2,13 @@
package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config
import hep.dataforge.meta.number
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.set
import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
@ -16,7 +16,6 @@ import kotlinx.serialization.UseSerializers
@Serializable
@SerialName("3d.line")
class PolyLine(var points: List<Point3D>) : AbstractVisualObject(), VisualObject3D {
@Serializable(ConfigSerializer::class)
override var properties: Config? = 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) }

View File

@ -1,18 +1,13 @@
@file:UseSerializers(Point3DSerializer::class, NameSerializer::class, ConfigSerializer::class)
@file:UseSerializers(Point3DSerializer::class)
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.Laminate
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.get
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.vis.common.*
import hep.dataforge.names.*
import hep.dataforge.vis.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@ -26,7 +21,9 @@ import kotlin.collections.set
*/
@Serializable
@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) {
this.parent = parent
@ -36,7 +33,6 @@ class Proxy private constructor(val templateName: Name) : AbstractVisualObject()
override var rotation: Point3D? = null
override var scale: Point3D? = null
@Serializable(ConfigSerializer::class)
override var properties: Config? = null
/**
@ -44,10 +40,12 @@ class Proxy private constructor(val templateName: Name) : AbstractVisualObject()
*/
val prototype: VisualObject3D
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
get() = (parent as? VisualGroup)?.styleSheet ?: StyleSheet(this)
get() = parent?.styleSheet ?: StyleSheet(
this
)
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
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) })
inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup {
inner class ProxyChild(val name: Name) : AbstractVisualObject(),
VisualGroup {
val prototype: VisualObject get() = prototypeFor(name)
@ -155,20 +154,18 @@ val VisualObject.prototype: VisualObject
/**
* Create ref for existing prototype
*/
inline fun VisualGroup3D.ref(
fun VisualGroup3D.ref(
templateName: Name,
name: String = "",
block: Proxy.() -> Unit = {}
) = Proxy(this, templateName).apply(block).also { set(name, it) }
name: String = ""
): Proxy = Proxy(this, templateName).also { set(name, it) }
/**
* Add new proxy wrapping given object and automatically adding it to the prototypes
*/
fun VisualGroup3D.proxy(
templateName: Name,
name: String,
obj: VisualObject3D,
name: String = "",
block: Proxy.() -> Unit = {}
templateName: Name = name.toName()
): Proxy {
val existing = getPrototype(templateName)
if (existing == null) {
@ -178,5 +175,14 @@ fun VisualGroup3D.proxy(
} else if (existing != obj) {
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
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.Config
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.set
import hep.dataforge.vis.AbstractVisualObject
import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.set
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
@ -23,7 +23,6 @@ class Sphere(
var theta: Float = PI.toFloat()
) : AbstractVisualObject(), VisualObject3D, Shape {
@Serializable(ConfigSerializer::class)
override var properties: Config? = null
override var position: Point3D? = null
@ -61,7 +60,7 @@ class Sphere(
}
}
inline fun VisualGroup3D.sphere(
inline fun MutableVisualGroup.sphere(
radius: Number,
phi: Number = 2 * PI,
theta: Number = PI,

View File

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

View File

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

View File

@ -1,47 +1,48 @@
@file:UseSerializers(
Point3DSerializer::class,
ConfigSerializer::class,
NameTokenSerializer::class,
NameSerializer::class,
MetaSerializer::class
Point3DSerializer::class
)
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.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.isEmpty
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 hep.dataforge.vis.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import kotlin.collections.set
interface PrototypeHolder {
val parent: VisualGroup?
val prototypes: MutableVisualGroup?
}
/**
* Represents 3-dimensional Visual Group
*/
@Serializable
@SerialName("group.3d")
class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, PrototypeHolder {
override var styleSheet: StyleSheet? = null
private set
/**
* A container for templates visible inside this group
*/
var prototypes: VisualGroup3D? = null
set(value) {
value?.parent = this
field = value
@Serializable(PrototypesSerializer::class)
override var prototypes: MutableVisualGroup? = null
private set
/**
* 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
@ -55,38 +56,18 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
private val _children = HashMap<NameToken, VisualObject>()
override val children: Map<NameToken, VisualObject> get() = _children
// init {
// //Do after deserialization
// attachChildren()
// }
override fun attachChildren() {
prototypes?.parent = this
prototypes?.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) {
_children.remove(token)?.run { parent = null }
childrenChanged(token.asName(), null)
_children.remove(token)?.apply { parent = null }
}
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
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 createGroup(name: Name): 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())
}
}
override fun createGroup(): VisualGroup3D = VisualGroup3D()
companion object {
// val PROTOTYPES_KEY = NameToken("@prototypes")
fun fromJson(json: String): VisualGroup3D =
fun parseJson(json: String): VisualGroup3D =
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
*/
tailrec fun VisualGroup3D.getPrototype(name: Name): VisualObject3D? =
tailrec fun PrototypeHolder.getPrototype(name: Name): VisualObject3D? =
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 {
(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 =
fun MutableVisualGroup.group(name: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
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
import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.asName
import hep.dataforge.names.plus
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.IGNORE_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY
@ -25,7 +27,8 @@ interface VisualObject3D : VisualObject {
companion object {
val VISIBLE_KEY = "visible".asName()
// val SELECTED_KEY = "selected".asName()
// val SELECTED_KEY = "selected".asName()
val DETAIL_KEY = "detail".asName()
val LAYER_KEY = "layer".asName()
val IGNORE_KEY = "ignore".asName()
@ -55,6 +58,22 @@ interface VisualObject3D : VisualObject {
val xScale = scale + x
val yScale = scale + y
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
get() = getProperty(LAYER_KEY).int ?: 0
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)
// Common properties
@ -86,7 +105,7 @@ enum class RotationOrder {
*/
var VisualObject3D.rotationOrder: RotationOrder
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?
get() = getProperty(DETAIL_KEY, false).int
set(value) = setProperty(DETAIL_KEY, value)
set(value) = setProperty(DETAIL_KEY, value?.asValue())
var VisualObject.visible: 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.
* Property is not inherited.
*/
var VisualObject.ignore: Boolean?
get() = getProperty(IGNORE_KEY,false).boolean
set(value) = setProperty(IGNORE_KEY, value)
get() = getProperty(IGNORE_KEY, false).boolean
set(value) = setProperty(IGNORE_KEY, value?.asValue())
//var VisualObject.selected: 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
import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.get
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) {
var x: Double
@ -13,7 +20,7 @@ expect class Point2D(x: Number, y: Number) {
operator fun Point2D.component1() = x
operator fun Point2D.component2() = y
fun Point2D.toMeta() = buildMeta {
fun Point2D.toMeta() = Meta {
VisualObject3D.x put x
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 Point3D.toMeta() = buildMeta {
fun Point3D.toMeta() = Meta {
VisualObject3D.x put x
VisualObject3D.y put y
VisualObject3D.z put z

View File

@ -1,12 +1,14 @@
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.toName
import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject
import kotlinx.serialization.*
import kotlinx.serialization.internal.DoubleSerializer
import kotlinx.serialization.internal.StringDescriptor
import kotlinx.serialization.internal.nullable
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.builtins.serializer
inline fun <R> Decoder.decodeStructure(
desc: SerialDescriptor,
@ -31,7 +33,7 @@ inline fun Encoder.encodeStructure(
@Serializer(Point3D::class)
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("y", true)
double("z", true)
@ -45,28 +47,28 @@ object Point3DSerializer : KSerializer<Point3D> {
loop@ while (true) {
when (val i = decodeElementIndex(descriptor)) {
CompositeDecoder.READ_DONE -> break@loop
0 -> x = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0
1 -> y = decodeNullableSerializableElement(descriptor, 1, DoubleSerializer.nullable) ?: 0.0
2 -> z = decodeNullableSerializableElement(descriptor, 2, DoubleSerializer.nullable) ?: 0.0
0 -> x = decodeNullableSerializableElement(descriptor, 0, Double.serializer().nullable) ?: 0.0
1 -> y = decodeNullableSerializableElement(descriptor, 1, Double.serializer().nullable) ?: 0.0
2 -> z = decodeNullableSerializableElement(descriptor, 2, Double.serializer().nullable) ?: 0.0
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) {
if (obj.x != 0.0) encodeDoubleElement(descriptor, 0, obj.x)
if (obj.y != 0.0) encodeDoubleElement(descriptor, 1, obj.y)
if (obj.z != 0.0) encodeDoubleElement(descriptor, 2, obj.z)
if (value.x != 0.0) encodeDoubleElement(descriptor, 0, value.x)
if (value.y != 0.0) encodeDoubleElement(descriptor, 1, value.y)
if (value.z != 0.0) encodeDoubleElement(descriptor, 2, value.z)
}
}
}
@Serializer(Point2D::class)
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("y", true)
}
@ -78,32 +80,48 @@ object Point2DSerializer : KSerializer<Point2D> {
loop@ while (true) {
when (val i = decodeElementIndex(descriptor)) {
CompositeDecoder.READ_DONE -> break@loop
0 -> x = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0
1 -> y = decodeNullableSerializableElement(descriptor, 1, DoubleSerializer.nullable) ?: 0.0
0 -> x = decodeNullableSerializableElement(descriptor, 0, Double.serializer().nullable) ?: 0.0
1 -> y = decodeNullableSerializableElement(descriptor, 1, Double.serializer().nullable) ?: 0.0
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) {
if (obj.x != 0.0) encodeDoubleElement(descriptor, 0, obj.x)
if (obj.y != 0.0) encodeDoubleElement(descriptor, 1, obj.y)
if (value.x != 0.0) encodeDoubleElement(descriptor, 0, value.x)
if (value.y != 0.0) encodeDoubleElement(descriptor, 1, value.y)
}
}
}
@Serializer(NameToken::class)
object NameTokenSerializer : KSerializer<NameToken> {
override val descriptor: SerialDescriptor = StringDescriptor.withName("NameToken")
@Serializer(MutableVisualGroup::class)
internal object PrototypesSerializer : KSerializer<MutableVisualGroup> {
override fun deserialize(decoder: Decoder): NameToken {
return decoder.decodeString().toName().first()!!
private val mapSerializer: KSerializer<Map<NameToken, VisualObject>> =
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) {
encoder.encodeString(obj.toString())
override fun serialize(encoder: Encoder, value: MutableVisualGroup) {
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.*
class AxesSpec(override val config: Config) : Specific {
class Axes : Scheme() {
var visible by boolean(!config.isEmpty())
var size by double(AXIS_SIZE)
var width by double(AXIS_WIDTH)
companion object : Specification<AxesSpec> {
override fun wrap(config: Config): AxesSpec = AxesSpec(config)
companion object : SchemeSpec<Axes>(::Axes) {
const val AXIS_SIZE = 1000.0
const val AXIS_WIDTH = 3.0
}
}

View File

@ -1,10 +1,14 @@
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
class CameraSpec(override val config: Config) : Specific {
class Camera : Scheme() {
var fov by int(FIELD_OF_VIEW)
//var aspect by double(1.0)
var nearClip by double(NEAR_CLIP)
var farClip by double(FAR_CLIP)
@ -14,11 +18,10 @@ class CameraSpec(override val config: Config) : Specific {
var latitude by double(INITIAL_LATITUDE)
val zenith: Double get() = PI / 2 - latitude
companion object : Specification<CameraSpec> {
override fun wrap(config: Config): CameraSpec = CameraSpec(config)
companion object : SchemeSpec<Camera>(::Camera) {
const val INITIAL_DISTANCE = 300.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 FAR_CLIP = 10000.0
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.names.asName
import hep.dataforge.vis.common.MutableVisualGroup
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.*
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.asName
import hep.dataforge.vis.common.MutableVisualGroup
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.MutableVisualGroup
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.spatial.Proxy
import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.prototypes
object UnRef : VisualTreeTransform<VisualGroup3D>() {
private fun VisualGroup.countRefs(): Map<Name, Int> {

View File

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

View File

@ -1,8 +1,9 @@
package hep.dataforge.vis.spatial
import hep.dataforge.io.toMeta
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.getIndexed
import hep.dataforge.meta.node
import hep.dataforge.meta.toMetaItem
import kotlin.test.Test
import kotlin.test.assertEquals
@ -26,9 +27,9 @@ class ConvexTest {
val convex = group.first() as 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, convex.points.size)

View File

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

View File

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

View File

@ -1,12 +1,12 @@
package hep.dataforge.vis.spatial
import hep.dataforge.vis.spatial.Visual3D.Companion.json
import kotlinx.serialization.ImplicitReflectionSerializer
import hep.dataforge.names.toName
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.get
import kotlin.test.Test
import kotlin.test.assertEquals
class SerializationTest {
@ImplicitReflectionSerializer
@Test
fun testCubeSerialization() {
val cube = Box(100f, 100f, 100f).apply {
@ -14,9 +14,30 @@ class SerializationTest {
x = 100
z = -100
}
val string = json.stringify(Box.serializer(), cube)
val string = cube.stringify()
println(string)
val newCube = json.parse(Box.serializer(), string)
val newCube = VisualObject.parseJson(string)
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.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.getProperty
import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.names.plus
import hep.dataforge.names.toName
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.specifications.CameraSpec
import hep.dataforge.vis.spatial.specifications.CanvasSpec
import hep.dataforge.vis.spatial.specifications.ControlsSpec
import hep.dataforge.vis.spatial.specifications.Camera
import hep.dataforge.vis.spatial.specifications.Canvas
import hep.dataforge.vis.spatial.specifications.Controls
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.cameras.PerspectiveCamera
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.geometries.EdgesGeometry
import info.laht.threekt.helpers.AxesHelper
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.math.Vector2
import info.laht.threekt.objects.LineSegments
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
@ -51,17 +53,19 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
private val raycaster = Raycaster()
private val mousePosition: Vector2 = Vector2()
var clickListener: ((Name) -> Unit)? = null
var onClick: ((Name?) -> Unit)? = null
val axes = AxesHelper(spec.axes.size.toInt()).apply {
visible = spec.axes.visible
val axes = AxesHelper(canvas.axes.size.toInt()).apply {
visible = canvas.axes.visible
}
val scene: Scene = Scene().apply {
add(axes)
}
val camera = buildCamera(spec.camera)
val camera = buildCamera(canvas.camera)
private var picked: Object3D? = null
init {
element.clear()
@ -75,29 +79,26 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
}
}, false)
element.addEventListener("mousedown", { event ->
val mesh = pick()
if (mesh != null) {
val name = mesh.fullName()
clickListener?.invoke(name)
}
element.addEventListener("mousedown", {
val picked = pick()
onClick?.invoke(picked?.fullName())
}, false)
camera.aspect = 1.0
val renderer = WebGLRenderer { antialias = true }.apply {
setClearColor(Colors.skyblue, 1)
}
addControls(renderer.domElement, spec.controls)
addControls(renderer.domElement, canvas.controls)
fun animate() {
val mesh = pick()
val picked = pick()
if (mesh != null && highlighted != mesh) {
highlighted?.toggleHighlight(false)
mesh.toggleHighlight(true)
if (picked != null && this.picked != picked) {
this.picked?.toggleHighlight(false,HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL)
picked.toggleHighlight(true, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL)
this.picked = picked
}
window.requestAnimationFrame {
@ -108,7 +109,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
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 = {
renderer.setSize(element.offsetWidth, element.offsetWidth)
@ -118,18 +119,6 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
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
*/
@ -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,
1.0,
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))
}
private fun addControls(element: Node, controlsSpec: ControlsSpec) {
when (controlsSpec["type"].string) {
private fun addControls(element: Node, controls: Controls) {
when (controls.getProperty("type").string) {
"trackball" -> TrackballControls(camera, element)
else -> OrbitControls(camera, element)
}
}
override fun render(obj: VisualObject3D, meta: Meta) {
//clear old root
fun clear(){
scene.children.find { it.name == "@root" }?.let {
scene.remove(it)
}
}
override fun render(obj: VisualObject3D, meta: Meta) {
//clear old root
clear()
val object3D = three.buildObject3D(obj)
object3D.name = "@root"
@ -173,43 +186,65 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: Canvas
root = object3D
}
private var highlighted: Mesh? = null
private var highlighted: Object3D? = null
/**
* Toggle highlight for the given [Mesh] object
*/
private fun Mesh.toggleHighlight(highlight: Boolean) {
private fun Object3D.toggleHighlight(
highlight: Boolean,
edgesName: String,
material: LineBasicMaterial = SELECTED_MATERIAL
) {
if (userData[DO_NOT_HIGHLIGHT_TAG] == true) {
return
}
if (this is Mesh) {
if (highlight) {
val edges = LineSegments(
EdgesGeometry(geometry as BufferGeometry),
HIGHLIGHT_MATERIAL
material
).apply {
name = "@highlight"
name = edgesName
}
add(edges)
highlighted = this
} else {
val highlightEdges = children.find { it.name == "@highlight" }
val highlightEdges = children.find { it.name == edgesName }
highlightEdges?.let { remove(it) }
}
} else {
children.filter { it.name != edgesName }.forEach {
it.toggleHighlight(highlight, edgesName, material)
}
}
}
/**
* Toggle highlight for element with given name
*/
fun highlight(name: Name?) {
fun select(name: Name?) {
if (name == null) {
highlighted?.toggleHighlight(false)
highlighted?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
highlighted = null
return
}
val mesh = root?.findChild(name) as? Mesh
if (mesh != null && highlighted != mesh) {
highlighted?.toggleHighlight(false)
mesh.toggleHighlight(true)
val obj = root?.findChild(name)
if (obj != null && highlighted != obj) {
highlighted?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
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)
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.color
import hep.dataforge.vis.spatial.three.ThreeCanvas.Companion.DO_NOT_HIGHLIGHT_TAG
import info.laht.threekt.DoubleSide
import info.laht.threekt.core.Object3D
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
*/
object ThreeCanvasLabelFactory: ThreeFactory<Label3D> {
object ThreeCanvasLabelFactory : ThreeFactory<Label3D> {
override val type: KClass<in Label3D> get() = Label3D::class
override fun invoke(obj: Label3D): Object3D {
@ -31,7 +32,7 @@ object ThreeCanvasLabelFactory: ThreeFactory<Label3D> {
//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
@ -51,6 +52,7 @@ object ThreeCanvasLabelFactory: ThreeFactory<Label3D> {
mesh.updatePosition(obj)
mesh.userData[DO_NOT_HIGHLIGHT_TAG] = true
return mesh
}
}

View File

@ -3,7 +3,7 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.names.Name
import hep.dataforge.names.startsWith
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.Material3D.Companion.MATERIAL_KEY
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.values.ValueType
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.Colors
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.Material3D
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.Material
@ -22,11 +22,17 @@ object ThreeMaterials {
color.set(DEFAULT_LINE_COLOR)
}
val HIGHLIGHT_MATERIAL = LineBasicMaterial().apply {
val SELECTED_MATERIAL = LineBasicMaterial().apply {
color.set(Colors.ivory)
linewidth = 8.0
}
val HIGHLIGHT_MATERIAL = LineBasicMaterial().apply {
color.set(Colors.blue)
linewidth = 8.0
}
fun getLineMaterial(meta: Meta?): LineBasicMaterial {
if (meta == null) return DEFAULT_LINE
return LineBasicMaterial().apply {

View File

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

View File

@ -2,9 +2,8 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.io.serialization.ConfigSerializer
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.Point3DSerializer
import hep.dataforge.vis.spatial.VisualObject3D
@ -26,7 +25,6 @@ class CustomThreeVisualObject(val threeFactory: ThreeFactory<VisualObject3D>) :
override var rotation: Point3D? = null
override var scale: Point3D? = null
@Serializable(ConfigSerializer::class)
override var properties: Config? = null
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:Suppress(
"INTERFACE_WITH_SUPERCLASS",

View File

@ -1,19 +1,25 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.js.accordion
import hep.dataforge.js.entry
import hep.dataforge.js.requireJS
import hep.dataforge.vis.js.editor.accordion
import hep.dataforge.vis.spatial.Visual3D
import hep.dataforge.vis.spatial.VisualGroup3D
import kotlinx.html.InputType
import kotlinx.html.TagConsumer
import kotlinx.html.button
import kotlinx.html.*
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.HTMLElement
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event
import org.w3c.files.Blob
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
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)
}
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 = {}) {
clear()
append {
@ -33,7 +103,7 @@ fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer<HTMLEl
div("row") {
div("col-2") {
label("checkbox-inline") {
input(type = InputType.checkBox).apply {
input(type = InputType.checkBox) {
checked = canvas.axes.visible
onChangeFunction = {
canvas.axes.visible = checked
@ -67,7 +137,7 @@ fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer<HTMLEl
(0..11).forEach { layer ->
div("col-1") {
label { +layer.toString() }
input(type = InputType.checkBox).apply {
input(type = InputType.checkBox) {
if (layer == 0) {
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()
}
}

View File

@ -153,7 +153,7 @@ open external class 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.
*/
var userData: Map<String, Any>
var userData: dynamic
/**
* 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.output.Renderer
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.beans.property.ObjectProperty
import javafx.beans.property.SimpleObjectProperty
@ -14,7 +14,7 @@ import javafx.scene.paint.Color
import org.fxyz3d.scene.Axes
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 {
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.int
import hep.dataforge.values.ValueType
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.Colors
import hep.dataforge.vis.spatial.Material3D
import javafx.scene.paint.Color
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.isEmpty
import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.Proxy
import javafx.scene.Group
import javafx.scene.Node

View File

@ -1,6 +1,5 @@
package hep.dataforge.vis.spatial.fx
import hep.dataforge.vis.spatial.specifications.CameraSpec
import javafx.beans.InvalidationListener
import javafx.beans.property.SimpleDoubleProperty
import javafx.event.EventHandler
@ -15,6 +14,7 @@ import javafx.scene.transform.Rotate
import javafx.scene.transform.Translate
import tornadofx.*
import kotlin.math.*
import hep.dataforge.vis.spatial.specifications.Camera as 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.startsWith
import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.VisualObject
import javafx.application.Platform
import javafx.beans.binding.ObjectBinding
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.vis.spatial.*
import hep.dataforge.meta.JSON_PRETTY
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.SerialModule
import kotlinx.serialization.modules.SerialModuleCollector
import kotlin.reflect.KClass
internal val SerialDescriptor.jsonType
get() = when (this.kind) {
private fun SerialDescriptor.getJsonType() = when (this.kind) {
StructureKind.LIST -> "array"
PrimitiveKind.BYTE, PrimitiveKind.SHORT, PrimitiveKind.INT, PrimitiveKind.LONG,
PrimitiveKind.FLOAT, PrimitiveKind.DOUBLE -> "number"
PrimitiveKind.STRING, PrimitiveKind.CHAR, UnionKind.ENUM_KIND -> "string"
PrimitiveKind.BOOLEAN -> "boolean"
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> {
val list = ArrayList<SerialDescriptor>()
@ -60,13 +61,16 @@ private fun SerialModule.enumerate(type: KClass<*>): Sequence<SerialDescriptor>
*/
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.Point2D",
Meta::class.qualifiedName
"hep.dataforge.vis.spatial.Point2D?",
"hep.dataforge.meta.Meta",
"hep.dataforge.meta.Meta?"
)
) 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 ->
val elementName = descriptor.getElementName(index)
properties[elementName] = when (elementName) {
"templates" -> json {
"\$ref" to "#/definitions/hep.dataforge.vis.spatial.VisualGroup3D"
}
val elementSchema = when (elementName) {
"properties" -> json {
"\$ref" to "#/definitions/${Meta::class.qualifiedName}"
"\$ref" to "#/$definitionNode/hep.dataforge.meta.Meta"
}
"first", "second" -> json{
"\$ref" to "#/definitions/children"
"first", "second" -> json {
"\$ref" to "#/$definitionNode/children"
}
"styleSheet" -> json {
"type" to "object"
"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"
"additionalProperties" to json {
"\$ref" to "#/definitions/children"
"\$ref" to "#/$definitionNode/children"
}
}
else -> jsonSchema(child, context)
}
properties[elementName] = elementSchema
if (!descriptor.isElementOptional(index)) requiredProperties.add(elementName)
}
val jsonType = descriptor.jsonType
val jsonType = descriptor.getJsonType()
val objectData: MutableMap<String, JsonElement> = mutableMapOf(
"description" to JsonLiteral(descriptor.name),
"description" to JsonLiteral(descriptor.serialName),
"type" to JsonLiteral(jsonType)
)
if (isEnum) {
@ -118,6 +120,11 @@ private fun jsonSchema(descriptor: SerialDescriptor, context: SerialModule): Jso
}
when (jsonType) {
"object" -> {
if(descriptor.isVisualObject()) {
properties["type"] = json {
"const" to descriptor.serialName
}
}
objectData["properties"] = JsonObject(properties)
val required = requiredProperties.map { JsonLiteral(it) }
if (required.isNotEmpty()) {
@ -140,9 +147,9 @@ fun main() {
"children" to json {
"anyOf" to jsonArray {
context.enumerate(VisualObject3D::class).forEach {
if (it.name == "hep.dataforge.vis.spatial.VisualGroup3D") {
if (it.serialName == "hep.dataforge.vis.spatial.VisualGroup3D") {
+json {
"\$ref" to "#/definitions/${it.name}"
"\$ref" to "#/$definitionNode/${it.serialName}"
}
} else {
+jsonSchema(it, context)
@ -150,18 +157,47 @@ fun main() {
}
}
}
"hep.dataforge.vis.spatial.Point3D" to jsonSchema(Point3DSerializer.descriptor, context)
"hep.dataforge.vis.spatial.Point2D" to jsonSchema(Point2DSerializer.descriptor, context)
"hep.dataforge.vis.spatial.VisualGroup3D" to jsonSchema(VisualGroup3D.serializer().descriptor, context)
"hep.dataforge.meta.Meta" to json {
"type" to "object"
}
"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(
Json.indented.stringify(
JSON_PRETTY.stringify(
JsonObjectSerializer,
json {
"definitions" to definitions
"\$ref" to "#/definitions/hep.dataforge.vis.spatial.VisualGroup3D"
"\$defs" to definitions
"\$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 {
id("scientifik.mpp")
id("org.openjfx.javafxplugin")
id("application")
}
val fxVersion: String by rootProject.extra
fx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
kotlin {
jvm {
@ -27,11 +31,6 @@ kotlin {
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"
}
configure<JavaFXOptions> {
modules("javafx.controls")
val convertGdmlToJson by tasks.creating(JavaExec::class) {
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