Add multi-select value chooser for layers

This commit is contained in:
Alexander Nozik 2021-05-08 23:14:15 +03:00
parent 2798439582
commit faf3fa8512
20 changed files with 295 additions and 152 deletions

View File

@ -5,7 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Three js demo for particle physics</title>
<script type="text/javascript" src="gdml.js"></script>
<link rel="stylesheet" href="css/custom-bootstrap.css">
<link rel="stylesheet" href="css/fileDrop.css">
</head>
<body class="application">

View File

@ -2,7 +2,7 @@ package space.kscience.visionforge.examples
import space.kscience.dataforge.context.Context
import space.kscience.gdml.*
import space.kscience.visionforge.gdml.gdml
import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.solid.Solids
fun main() {
@ -12,7 +12,7 @@ fun main() {
context.makeVisionFile {
vision("canvas") {
gdml {
Gdml {
// geometry variables
val worldSize = 500
// chamber
@ -220,6 +220,8 @@ fun main() {
}
}
}
}.toVision {
this.solidAction
}
}
}

View File

@ -10,9 +10,7 @@ fun main() {
plugin(Solids)
}
context.makeVisionFile{
vision("canvas") {
GdmlShowCase.babyIaxo().toVision()
}
context.makeVisionFile {
vision("canvas") { GdmlShowCase.babyIaxo().toVision() }
}
}

View File

@ -1,10 +1,6 @@
package space.kscience.visionforge.gdml.jupyter
import kotlinx.html.div
import kotlinx.html.id
import kotlinx.html.script
import kotlinx.html.stream.createHTML
import kotlinx.html.unsafe
import org.jetbrains.kotlinx.jupyter.api.HTML
import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
@ -16,7 +12,7 @@ import space.kscience.visionforge.Vision
import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.html.HtmlVisionFragment
import space.kscience.visionforge.html.Page
import space.kscience.visionforge.html.embedVisionFragment
import space.kscience.visionforge.html.embedAndRenderVisionFragment
import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.visionManager
@ -30,17 +26,9 @@ internal class GdmlForJupyter : JupyterIntegration() {
private var counter = 0
private fun produceHtmlVisionString(fragment: HtmlVisionFragment) = createHTML().div {
val id = "visionforge.vision[${counter++}]"
div {
this.id = id
embedVisionFragment(context.visionManager, fragment = fragment)
}
script {
type = "text/javascript"
unsafe { +"VisionForge.renderVisionsAt(\"$id\");" }
}
}
private fun produceHtmlVisionString(fragment: HtmlVisionFragment) = createHTML().apply {
embedAndRenderVisionFragment(context.visionManager, counter++, fragment)
}.finalize()
override fun Builder.onLoaded() {
@ -62,19 +50,12 @@ internal class GdmlForJupyter : JupyterIntegration() {
"space.kscience.visionforge.gdml.jupyter.*"
)
render<Gdml> { gdmlModel ->
val fragment = HtmlVisionFragment {
vision(gdmlModel.toVision())
}
HTML(produceHtmlVisionString(fragment))
render<Vision> { vision ->
HTML(produceHtmlVisionString { vision(vision) })
}
render<Vision> { vision ->
val fragment = HtmlVisionFragment {
vision(vision)
}
HTML(produceHtmlVisionString(fragment))
render<Gdml> { gdmlModel ->
HTML(produceHtmlVisionString { vision(gdmlModel.toVision()) })
}
render<Page> { page ->

View File

@ -8,5 +8,6 @@ dependencies{
api(project(":visionforge-solid"))
api("org.jetbrains:kotlin-styled:5.2.3-$kotlinWrappersVersion")
api("org.jetbrains:kotlin-react-dom:17.0.2-$kotlinWrappersVersion")
// implementation(npm("react-select","4.3.0"))
implementation(project(":visionforge-threejs"))
}

View File

@ -0,0 +1,42 @@
package space.kscience.visionforge.react
import kotlinx.html.js.onChangeFunction
import org.w3c.dom.HTMLOptionElement
import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.asList
import org.w3c.dom.events.Event
import react.FunctionalComponent
import react.dom.option
import react.dom.select
import react.functionalComponent
import react.useState
import space.kscience.dataforge.meta.value
import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.string
@JsExport
public val MultiSelectChooser: FunctionalComponent<ValueChooserProps> =
functionalComponent("MultiSelectChooser") { props ->
var selectedItems by useState { props.item.value?.list ?: emptyList() }
val onChange: (Event) -> Unit = { event: Event ->
val newSelected= (event.target as HTMLSelectElement).selectedOptions.asList()
.map { (it as HTMLOptionElement).value.asValue() }
props.valueChanged?.invoke(newSelected.asValue())
selectedItems = newSelected
}
select {
attrs {
multiple = true
values = selectedItems.mapTo(HashSet()) { it.string }
onChangeFunction = onChange
}
props.descriptor?.allowedValues?.forEach { optionValue ->
option {
+optionValue.string
}
}
}
}

View File

@ -152,6 +152,7 @@ public val ValueChooser: FunctionalComponent<ValueChooserProps> = functionalComp
when {
rawInput -> child(StringValueChooser, props)
descriptor?.widgetType == "color" -> child(ColorValueChooser, props)
descriptor?.widgetType == "multiSelect" -> child(MultiSelectChooser, props)
type == ValueType.BOOLEAN -> child(BooleanValueChooser, props)
type == ValueType.NUMBER -> child(NumberValueChooser, props)
descriptor?.allowedValues?.isNotEmpty() ?: false -> child(ComboValueChooser, props)

View File

@ -2,7 +2,8 @@ package space.kscience.visionforge
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaBuilder
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.Specification
import kotlin.properties.ReadOnlyProperty
/**
@ -23,11 +24,23 @@ public fun Vision.useStyle(reference: StyleReference) {
useStyle(reference.name)
}
@DFExperimental
@VisionBuilder
public fun VisionGroup.style(builder: MetaBuilder.() -> Unit): ReadOnlyProperty<Any?, StyleReference> =
ReadOnlyProperty { _, property ->
val styleName = property.name
styleSheet.define(styleName, Meta(builder))
StyleReference(this, styleName)
}
public fun VisionGroup.style(
styleKey: String? = null,
builder: MetaBuilder.() -> Unit,
): ReadOnlyProperty<Any?, StyleReference> = ReadOnlyProperty { _, property ->
val styleName = styleKey ?: property.name
styleSheet.define(styleName, Meta(builder))
StyleReference(this, styleName)
}
@VisionBuilder
public fun <T : Scheme> VisionGroup.style(
spec: Specification<T>,
styleKey: String? = null,
builder: T.() -> Unit,
): ReadOnlyProperty<Any?, StyleReference> = ReadOnlyProperty { _, property ->
val styleName = styleKey ?: property.name
styleSheet.define(styleName, spec(builder).toMeta())
StyleReference(this, styleName)
}

View File

@ -2,6 +2,7 @@ package space.kscience.visionforge.html
import kotlinx.html.*
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionManager
@ -35,20 +36,19 @@ public fun FlowContent.embedVisionFragment(
fragment: HtmlVisionFragment,
): Map<Name, Vision> = consumer.embedVisionFragment(manager, idPrefix, fragment)
public typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit
public fun FlowContent.renderVisionFragment(
renderer: DIV.(name: Name, vision: Vision, meta: Meta) -> Unit,
idPrefix: String? = null,
fragment: HtmlVisionFragment,
): Map<Name, Vision> {
val visionMap = HashMap<Name, Vision>()
val consumer = object : VisionTagConsumer<Any?>(consumer, idPrefix) {
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
visionMap[name] = vision
renderer(name, vision, outputMeta)
internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById"
@DFExperimental
public fun TagConsumer<*>.embedAndRenderVisionFragment(manager: VisionManager, id: Any, fragment: HtmlVisionFragment){
div {
div {
this.id = id.toString()
embedVisionFragment(manager, fragment = fragment)
}
script {
type = "text/javascript"
unsafe { +"window.${RENDER_FUNCTION_NAME}(\"$id\");" }
}
}
fragment(consumer)
return visionMap
}

View File

@ -66,7 +66,9 @@ public abstract class VisionTagConsumer<R>(
}
}
}
vision?.let { renderVision(name, it, outputMeta) }
vision?.let {
renderVision(name, it, outputMeta)
}
}
@OptIn(DFExperimental::class)

View File

@ -4,7 +4,7 @@ import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.meta.descriptors.NodeDescriptorBuilder
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
import space.kscience.dataforge.meta.descriptors.ValueDescriptorBuilder
import space.kscience.dataforge.meta.toConfig
import space.kscience.dataforge.values.ValueType
import kotlin.reflect.KProperty1
@ -15,7 +15,7 @@ import kotlin.reflect.typeOf
*/
public inline fun <S : Scheme, reified T> NodeDescriptorBuilder.value(
property: KProperty1<S, T>,
noinline block: ValueDescriptor.() -> Unit = {},
noinline block: ValueDescriptorBuilder.() -> Unit = {},
) {
when (typeOf<T>()) {
typeOf<Number>(), typeOf<Int>(), typeOf<Double>(), typeOf<Short>(), typeOf<Long>(), typeOf<Float>() ->
@ -54,7 +54,7 @@ public fun NodeDescriptor.copy(block: NodeDescriptorBuilder.() -> Unit = {}): No
public inline fun <S : Scheme, reified T : Scheme> NodeDescriptorBuilder.scheme(
property: KProperty1<S, T>,
spec: SchemeSpec<T>,
noinline block: NodeDescriptor.() -> Unit = {},
noinline block: NodeDescriptorBuilder.() -> Unit = {},
) {
spec.descriptor?.let { descriptor ->
item(property.name, descriptor.copy(block))

View File

@ -2,13 +2,35 @@ package space.kscience.visionforge.html
import kotlinx.html.*
import kotlinx.html.stream.createHTML
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.set
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionBase
import space.kscience.visionforge.configure
import space.kscience.visionforge.meta
import kotlin.test.Test
typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit
fun FlowContent.renderVisionFragment(
renderer: DIV.(name: Name, vision: Vision, meta: Meta) -> Unit,
idPrefix: String? = null,
fragment: HtmlVisionFragment,
): Map<Name, Vision> {
val visionMap = HashMap<Name, Vision>()
val consumer = object : VisionTagConsumer<Any?>(consumer, idPrefix) {
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
visionMap[name] = vision
renderer(name, vision, outputMeta)
}
}
fragment(consumer)
return visionMap
}
@DFExperimental
class HtmlTagTest {
@ -46,15 +68,11 @@ class HtmlTagTest {
}
}
val groupRenderer: HtmlVisionRenderer = { _, _, _ ->
p { +"This is group" }
}
@Test
fun testStringRender() {
println(
createHTML().div {
createHTML().div{
renderVisionFragment(simpleVisionRenderer, fragment = fragment)
}
)

View File

@ -7,6 +7,7 @@ import org.w3c.dom.url.URL
import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.visionforge.html.RENDER_FUNCTION_NAME
import space.kscience.visionforge.html.VisionTagConsumer
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_CONNECT_ATTRIBUTE
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_ENDPOINT_ATTRIBUTE
@ -221,7 +222,7 @@ public fun runVisionClient(contextBuilder: ContextBuilder.() -> Unit) {
//plugin(VisionClient)
}
val visionClient = context.fetch(VisionClient)
window.asDynamic()["renderAllVisionsById"] = visionClient::renderAllVisionsById
window.asDynamic()[RENDER_FUNCTION_NAME] = visionClient::renderAllVisionsById
visionClient.renderAllVisions()
}

View File

@ -25,8 +25,7 @@ private inline operator fun Number.times(d: Double) = toDouble() * d
@Suppress("NOTHING_TO_INLINE")
private inline operator fun Number.times(f: Float) = toFloat() * f
public class GdmlTransformerSettings {
public val random: Random = Random(222)
public class GdmlTransformer {
public enum class Action {
ADD,
@ -40,12 +39,70 @@ public class GdmlTransformerSettings {
public var solidAction: (GdmlSolid) -> Action = { Action.PROTOTYPE }
public var volumeAction: (GdmlGroup) -> Action = { Action.PROTOTYPE }
public var paint: SolidMaterial.(material: GdmlMaterial) -> Unit = { _ ->
color(random.nextInt(16777216))
internal val styleCache = HashMap<Name, Meta>()
public fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
styleCache.getOrPut(name.toName()) {
Meta(builder)
}
useStyle(name)
}
public fun Solid.opaque() {
useStyle("opaque") {
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
"edges.enabled" put true
}
}
/**
* Configure paint for given solid with given [GdmlMaterial]
*/
public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit =
{ material, _ -> color(randomColor(material)) }
private set
public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) {
configurePaint = block
}
/**
* Configure given solid
*/
public var configureSolid: Solid.(parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial) -> Unit =
{ parent, solid, material ->
val styleName = "materials.${material.name}"
if (parent.physVolumes.isNotEmpty()) opaque()
useStyle(styleName) {
val vfMaterial = SolidMaterial().apply {
configurePaint(material, solid)
}
MATERIAL_KEY put vfMaterial.toMeta()
"Gdml.material" put material.name
}
}
private set
public companion object {
private val random: Random = Random(222)
private val colorCache = HashMap<GdmlMaterial, Int>()
/**
* Use random color and cache it based on the material. Meaning that colors are random, but always the same for the
* same material.
*/
public fun randomColor(material: GdmlMaterial): Int {
return colorCache.getOrPut(material) { random.nextInt(16777216) }
}
}
}
private class GdmlTransformer(val settings: GdmlTransformerSettings) {
private class GdmlTransformerEnv(val settings: GdmlTransformer) {
//private val materialCache = HashMap<GdmlMaterial, Meta>()
/**
@ -57,9 +114,13 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
setProperty("edges.enabled", false)
}
private val referenceStore = HashMap<Name, MutableList<SolidReferenceGroup>>()
fun Solid.configureSolid(root: Gdml, parent: GdmlVolume, solid: GdmlSolid) {
val material = parent.materialref.resolve(root) ?: GdmlElement(parent.materialref.ref)
settings.run { configureSolid(parent, solid, material) }
}
private fun proxySolid(root: Gdml, group: SolidGroup, solid: GdmlSolid, name: String): SolidReferenceGroup {
val templateName = solidsName + name
if (proto[templateName] == null) {
@ -85,38 +146,6 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
return ref
}
private val styleCache = HashMap<Name, Meta>()
var solidConfiguration: Solid.(parent: GdmlVolume, solid: GdmlSolid) -> Unit = { parent, _ ->
if (parent.physVolumes.isNotEmpty()) {
useStyle("opaque") {
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
"edges.enabled" put true
}
}
}
fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
styleCache.getOrPut(name.toName()) {
Meta(builder)
}
useStyle(name)
}
fun configureSolid(root: Gdml, obj: Solid, parent: GdmlVolume, solid: GdmlSolid) {
val material = parent.materialref.resolve(root) ?: GdmlElement(parent.materialref.ref)
val styleName = "materials.${material.name}"
obj.useStyle(styleName) {
val vfMaterial = settings.run { SolidMaterial().apply { paint(material) } }
MATERIAL_KEY put vfMaterial.toMeta()
"Gdml.material" put material.name
}
obj.solidConfiguration(parent, solid)
}
fun <T : Solid> T.withPosition(
newPos: GdmlPosition? = null,
newRotation: GdmlRotation? = null,
@ -314,13 +343,13 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
): Solid? {
require(name != "") { "Can't use empty solid name. Use null instead." }
return when (settings.solidAction(solid)) {
GdmlTransformerSettings.Action.ADD -> {
GdmlTransformer.Action.ADD -> {
addSolid(root, solid, name)
}
GdmlTransformerSettings.Action.PROTOTYPE -> {
GdmlTransformer.Action.PROTOTYPE -> {
proxySolid(root, this, solid, name ?: solid.name)
}
GdmlTransformerSettings.Action.REJECT -> {
GdmlTransformer.Action.REJECT -> {
//ignore
null
}
@ -339,21 +368,21 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
val solid = volume.solidref.resolve(root)
?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined")
addSolidWithCaching(root, solid, physVolume.name)?.apply {
configureSolid(root, this, volume, solid)
configureSolid(root, volume, solid)
withPosition(root, physVolume)
}
return
}
when (settings.volumeAction(volume)) {
GdmlTransformerSettings.Action.ADD -> {
GdmlTransformer.Action.ADD -> {
val group: SolidGroup = volume(root, volume)
this[physVolume.name] = group.withPosition(root, physVolume)
}
GdmlTransformerSettings.Action.PROTOTYPE -> {
GdmlTransformer.Action.PROTOTYPE -> {
proxyVolume(root, this, physVolume, volume)
}
GdmlTransformerSettings.Action.REJECT -> {
GdmlTransformer.Action.REJECT -> {
//ignore
}
}
@ -379,7 +408,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
addSolidWithCaching(root, solid, null)?.apply {
configureSolid(root, this, group, solid)
this.configureSolid(root, group, solid)
}
when (val vol: GdmlPlacement? = group.placement) {
@ -394,10 +423,10 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
}
private fun finalize(final: SolidGroup): SolidGroup {
//final.prototypes = proto
final.useStyle("gdml") {
val rootStyle by final.style("gdml") {
Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY
}
final.useStyle(rootStyle)
//inline prototypes
// referenceStore.forEach { (protoName, list) ->
@ -419,7 +448,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
set(token.asName(), item as? Solid)
}
}
styleCache.forEach {
settings.styleCache.forEach {
final.styleSheet {
define(it.key.toString(), it.value)
}
@ -432,15 +461,16 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
}
public fun Gdml.toVision(block: GdmlTransformerSettings.() -> Unit = {}): SolidGroup {
val context = GdmlTransformer(GdmlTransformerSettings().apply(block))
public fun Gdml.toVision(block: GdmlTransformer.() -> Unit = {}): SolidGroup {
val settings = GdmlTransformer().apply(block)
val context = GdmlTransformerEnv(settings)
return context.transform(this)
}
/**
* Append Gdml node to the group
*/
public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlTransformerSettings.() -> Unit = {}) {
public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlTransformer.() -> Unit = {}) {
val visual = gdml.toVision(transformer)
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
set(key, visual)

View File

@ -12,7 +12,7 @@ public fun SolidGroup.gdml(
file: Path,
key: String = "",
usePreprocessor: Boolean = false,
transformer: GdmlTransformerSettings.() -> Unit = {},
transformer: GdmlTransformer.() -> Unit = {},
) {
val gdml = Gdml.decodeFromFile(file, usePreprocessor)
gdml(gdml, key, transformer)

View File

@ -89,7 +89,7 @@ public class VisionServer internal constructor(
script {
attributes["class"] = OUTPUT_DATA_CLASS
unsafe {
+visionManager.encodeToString(vision)
+"\n${visionManager.encodeToString(vision)}\n"
}
}
}

View File

@ -18,9 +18,15 @@ public class Axes : Scheme() {
override val descriptor: NodeDescriptor by lazy {
NodeDescriptor {
value(Axes::visible)
value(Axes::size)
value(Axes::width)
value(Axes::visible){
default(false)
}
value(Axes::size){
default(AXIS_SIZE)
}
value(Axes::width){
default(AXIS_WIDTH)
}
}
}
}

View File

@ -30,12 +30,24 @@ public class Camera : Scheme() {
override val descriptor: NodeDescriptor by lazy {
NodeDescriptor {
value(Camera::fov)
value(Camera::nearClip)
value(Camera::farClip)
value(Camera::distance)
value(Camera::azimuth)
value(Camera::zenith)
value(Camera::fov){
default(FIELD_OF_VIEW)
}
value(Camera::nearClip){
default(NEAR_CLIP)
}
value(Camera::farClip){
default(FAR_CLIP)
}
value(Camera::distance){
default(INITIAL_DISTANCE)
}
value(Camera::azimuth){
default(INITIAL_AZIMUTH)
}
value(Camera::latitude){
default(INITIAL_LATITUDE)
}
}
}
}

View File

@ -3,8 +3,11 @@ package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.hide
import space.kscience.visionforge.scheme
import space.kscience.visionforge.value
import space.kscience.visionforge.widgetType
public class Canvas3DOptions : Scheme() {
public var axes: Axes by spec(Axes)
@ -20,6 +23,8 @@ public class Canvas3DOptions : Scheme() {
public var maxWith: Number by number { maxSize }
public var maxHeight: Number by number { maxSize }
public var layers: List<Number> by numberList(0)
public var onSelect: ((Name?) -> Unit)? = null
@ -28,14 +33,37 @@ public class Canvas3DOptions : Scheme() {
NodeDescriptor {
scheme(Canvas3DOptions::axes, Axes)
scheme(Canvas3DOptions::light, Light)
scheme(Canvas3DOptions::camera, Camera)
scheme(Canvas3DOptions::controls, Controls)
value(Canvas3DOptions::minSize)
value(Canvas3DOptions::minWith)
value(Canvas3DOptions::minHeight)
value(Canvas3DOptions::maxSize)
value(Canvas3DOptions::maxWith)
value(Canvas3DOptions::maxHeight)
scheme(Canvas3DOptions::camera, Camera) {
hide()
}
scheme(Canvas3DOptions::controls, Controls) {
hide()
}
value(Canvas3DOptions::minSize) {
hide()
}
value(Canvas3DOptions::minWith) {
hide()
}
value(Canvas3DOptions::minHeight) {
hide()
}
value(Canvas3DOptions::maxSize) {
hide()
}
value(Canvas3DOptions::maxWith) {
hide()
}
value(Canvas3DOptions::maxHeight) {
hide()
}
value(Canvas3DOptions::layers) {
type(ValueType.NUMBER)
multiple = true
default(listOf(0))
widgetType = "multiSelect"
allow(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
}
}
}
}

View File

@ -52,7 +52,7 @@ public class ThreeCanvas(
private set
private val scene: Scene = Scene().apply {
options.useProperty(Canvas3DOptions::axes){axesConfig->
options.useProperty(Canvas3DOptions::axes) { axesConfig ->
getObjectByName(AXES_NAME)?.let { remove(it) }
val axesObject = AxesHelper(axes.size.toInt()).apply { visible = axes.visible }
axesObject.name = AXES_NAME
@ -60,7 +60,7 @@ public class ThreeCanvas(
}
//Set up light
options.useProperty(Canvas3DOptions::light){lightConfig->
options.useProperty(Canvas3DOptions::light) { lightConfig ->
//remove old light if present
getObjectByName(LIGHT_NAME)?.let { remove(it) }
//add new light
@ -70,8 +70,29 @@ public class ThreeCanvas(
}
}
public var camera: PerspectiveCamera = buildCamera(options.camera)
private set
private fun buildCamera(spec: Camera) = PerspectiveCamera(
spec.fov,
1.0,
spec.nearClip,
spec.farClip
).apply {
translateX(spec.distance * sin(spec.zenith) * sin(spec.azimuth))
translateY(spec.distance * cos(spec.zenith))
translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth))
options.useProperty(Canvas3DOptions::layers) { selectedLayers ->
(0..31).forEach {
if (it in selectedLayers) {
this@apply.layers.enable(it)
} else{
this@apply.layers.disable(it)
}
}
}
}
public val camera: PerspectiveCamera = buildCamera(options.camera)
private var picked: Object3D? = null
@ -127,7 +148,6 @@ public class ThreeCanvas(
mousePosition.x = ((event.clientX - rect.left) / canvas.clientWidth) * 2 - 1
mousePosition.y = -((event.clientY - rect.top) / canvas.clientHeight) * 2 + 1
}
event.preventDefault()
}, false)
canvas.onresize = {
@ -185,17 +205,6 @@ public class ThreeCanvas(
private fun buildLight(spec: Light?): info.laht.threekt.lights.Light = AmbientLight(0x404040)
private fun buildCamera(spec: Camera) = PerspectiveCamera(
spec.fov,
1.0,
spec.nearClip,
spec.farClip
).apply {
translateX(spec.distance * sin(spec.zenith) * sin(spec.azimuth))
translateY(spec.distance * cos(spec.zenith))
translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth))
}
private fun addControls(element: Node, controls: Controls) {
when (controls["type"].string) {
"trackball" -> TrackballControls(camera, element)