forked from kscience/visionforge
Refactor react ThreeCanvas to functional component
This commit is contained in:
parent
3edb00b6bf
commit
aaad836567
@ -7,7 +7,6 @@ import hep.dataforge.vision.Vision
|
|||||||
import hep.dataforge.vision.VisionGroup
|
import hep.dataforge.vision.VisionGroup
|
||||||
import hep.dataforge.vision.bootstrap.*
|
import hep.dataforge.vision.bootstrap.*
|
||||||
import hep.dataforge.vision.gdml.toVision
|
import hep.dataforge.vision.gdml.toVision
|
||||||
import hep.dataforge.vision.react.component
|
|
||||||
import hep.dataforge.vision.react.objectTree
|
import hep.dataforge.vision.react.objectTree
|
||||||
import hep.dataforge.vision.solid.Solid
|
import hep.dataforge.vision.solid.Solid
|
||||||
import hep.dataforge.vision.solid.SolidGroup
|
import hep.dataforge.vision.solid.SolidGroup
|
||||||
@ -22,11 +21,8 @@ import kscience.gdml.GDML
|
|||||||
import kscience.gdml.decodeFromString
|
import kscience.gdml.decodeFromString
|
||||||
import org.w3c.files.FileReader
|
import org.w3c.files.FileReader
|
||||||
import org.w3c.files.get
|
import org.w3c.files.get
|
||||||
import react.RProps
|
import react.*
|
||||||
import react.dom.h1
|
import react.dom.h1
|
||||||
import react.getValue
|
|
||||||
import react.setValue
|
|
||||||
import react.useState
|
|
||||||
import styled.css
|
import styled.css
|
||||||
|
|
||||||
external interface GDMLAppProps : RProps {
|
external interface GDMLAppProps : RProps {
|
||||||
@ -44,7 +40,7 @@ external interface GDMLAppProps : RProps {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
val GDMLApp = component<GDMLAppProps> { props ->
|
val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
|
||||||
var selected by useState { props.selected }
|
var selected by useState { props.selected }
|
||||||
var canvas: ThreeCanvas? by useState { null }
|
var canvas: ThreeCanvas? by useState { null }
|
||||||
var vision: Vision? by useState { props.rootObject }
|
var vision: Vision? by useState { props.rootObject }
|
||||||
@ -113,11 +109,10 @@ val GDMLApp = component<GDMLAppProps> { props ->
|
|||||||
+"order-xl-2"
|
+"order-xl-2"
|
||||||
}
|
}
|
||||||
//canvas
|
//canvas
|
||||||
(vision as? Solid)?.let { visual3D ->
|
child(ThreeCanvasComponent) {
|
||||||
child(ThreeCanvasComponent::class) {
|
|
||||||
attrs {
|
attrs {
|
||||||
this.context = props.context
|
this.context = props.context
|
||||||
this.obj = visual3D
|
this.obj = vision as? Solid
|
||||||
this.selected = selected
|
this.selected = selected
|
||||||
this.clickCallback = select
|
this.clickCallback = select
|
||||||
this.canvasCallback = {
|
this.canvasCallback = {
|
||||||
@ -126,7 +121,6 @@ val GDMLApp = component<GDMLAppProps> { props ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
gridColumn(3, maxSize = GridMaxSize.XL) {
|
gridColumn(3, maxSize = GridMaxSize.XL) {
|
||||||
css {
|
css {
|
||||||
+"order-3"
|
+"order-3"
|
||||||
|
@ -8,7 +8,6 @@ import hep.dataforge.names.length
|
|||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
import hep.dataforge.vision.bootstrap.canvasControls
|
import hep.dataforge.vision.bootstrap.canvasControls
|
||||||
import hep.dataforge.vision.bootstrap.card
|
import hep.dataforge.vision.bootstrap.card
|
||||||
import hep.dataforge.vision.react.component
|
|
||||||
import hep.dataforge.vision.react.configEditor
|
import hep.dataforge.vision.react.configEditor
|
||||||
import hep.dataforge.vision.react.objectTree
|
import hep.dataforge.vision.react.objectTree
|
||||||
import hep.dataforge.vision.solid.specifications.Camera
|
import hep.dataforge.vision.solid.specifications.Camera
|
||||||
@ -20,11 +19,10 @@ import io.ktor.client.request.get
|
|||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import react.RProps
|
import react.*
|
||||||
import react.dom.*
|
import react.dom.*
|
||||||
import react.getValue
|
import styled.css
|
||||||
import react.setValue
|
import styled.styledDiv
|
||||||
import react.useState
|
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
|
||||||
external interface MMAppProps : RProps {
|
external interface MMAppProps : RProps {
|
||||||
@ -43,7 +41,7 @@ private val canvasConfig = Canvas3DOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
val MMApp = component<MMAppProps> { props ->
|
val MMApp = functionalComponent<MMAppProps>("Muon monitor") { props ->
|
||||||
var selected by useState { props.selected }
|
var selected by useState { props.selected }
|
||||||
var canvas: ThreeCanvas? by useState { null }
|
var canvas: ThreeCanvas? by useState { null }
|
||||||
|
|
||||||
@ -59,7 +57,12 @@ val MMApp = component<MMAppProps> { props ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
div("row") {
|
div("row") {
|
||||||
div("col-lg-3 px-0 overflow-auto") {
|
styledDiv {
|
||||||
|
css {
|
||||||
|
+"col-lg-3"
|
||||||
|
+"px-0"
|
||||||
|
+"overflow-auto"
|
||||||
|
}
|
||||||
//tree
|
//tree
|
||||||
card("Object tree") {
|
card("Object tree") {
|
||||||
objectTree(root, selected, select)
|
objectTree(root, selected, select)
|
||||||
@ -67,7 +70,7 @@ val MMApp = component<MMAppProps> { props ->
|
|||||||
}
|
}
|
||||||
div("col-lg-6") {
|
div("col-lg-6") {
|
||||||
//canvas
|
//canvas
|
||||||
child(ThreeCanvasComponent::class) {
|
child(ThreeCanvasComponent) {
|
||||||
attrs {
|
attrs {
|
||||||
this.context = props.context
|
this.context = props.context
|
||||||
this.obj = root
|
this.obj = root
|
||||||
|
@ -3,16 +3,25 @@ import hep.dataforge.js.Application
|
|||||||
import hep.dataforge.js.startApplication
|
import hep.dataforge.js.startApplication
|
||||||
import hep.dataforge.vision.bootstrap.visionPropertyEditor
|
import hep.dataforge.vision.bootstrap.visionPropertyEditor
|
||||||
import hep.dataforge.vision.react.objectTree
|
import hep.dataforge.vision.react.objectTree
|
||||||
import hep.dataforge.vision.solid.Point3D
|
import hep.dataforge.vision.solid.*
|
||||||
import hep.dataforge.vision.solid.SolidGroup
|
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||||
import hep.dataforge.vision.solid.box
|
import hep.dataforge.vision.solid.three.ThreeCanvasComponent
|
||||||
import hep.dataforge.vision.solid.group
|
|
||||||
import hep.dataforge.vision.solid.three.ThreePlugin
|
import hep.dataforge.vision.solid.three.ThreePlugin
|
||||||
import hep.dataforge.vision.solid.three.threeCanvas
|
import kotlinx.browser.document
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
|
import react.RBuilder
|
||||||
|
import react.child
|
||||||
import react.dom.div
|
import react.dom.div
|
||||||
import react.dom.render
|
import react.dom.render
|
||||||
import kotlin.browser.document
|
|
||||||
|
public fun RBuilder.threeCanvas(object3D: Solid, options: Canvas3DOptions.() -> Unit = {}) {
|
||||||
|
child(ThreeCanvasComponent) {
|
||||||
|
attrs {
|
||||||
|
this.obj = object3D
|
||||||
|
this.options = Canvas3DOptions.invoke(options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class PlayGroundApp : Application {
|
private class PlayGroundApp : Application {
|
||||||
|
|
||||||
|
@ -19,29 +19,29 @@ public external interface ConfigEditorItemProps : RProps {
|
|||||||
/**
|
/**
|
||||||
* Root config object - always non null
|
* Root config object - always non null
|
||||||
*/
|
*/
|
||||||
var root: Config
|
public var root: Config
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full path to the displayed node in [root]. Could be empty
|
* Full path to the displayed node in [root]. Could be empty
|
||||||
*/
|
*/
|
||||||
var name: Name
|
public var name: Name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root default
|
* Root default
|
||||||
*/
|
*/
|
||||||
var default: Meta?
|
public var default: Meta?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root descriptor
|
* Root descriptor
|
||||||
*/
|
*/
|
||||||
var descriptor: NodeDescriptor?
|
public var descriptor: NodeDescriptor?
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ConfigEditorItem: FunctionalComponent<ConfigEditorItemProps> = component { props ->
|
private val ConfigEditorItem: FunctionalComponent<ConfigEditorItemProps> = functionalComponent("ConfigEditorItem") { props ->
|
||||||
configEditorItem(props)
|
configEditorItem(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RFBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||||
var expanded: Boolean by useState { true }
|
var expanded: Boolean by useState { true }
|
||||||
var item: MetaItem<Config>? by useState { props.root[props.name] }
|
var item: MetaItem<Config>? by useState { props.root[props.name] }
|
||||||
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
||||||
@ -198,7 +198,7 @@ public external interface ConfigEditorProps : RProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
public val ConfigEditor: FunctionalComponent<ConfigEditorProps> = component { props ->
|
public val ConfigEditor: FunctionalComponent<ConfigEditorProps> = functionalComponent("ConfigEditor") { props ->
|
||||||
child(ConfigEditorItem) {
|
child(ConfigEditorItem) {
|
||||||
attrs {
|
attrs {
|
||||||
this.key = ""
|
this.key = ""
|
||||||
@ -223,7 +223,7 @@ public fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = nu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) {
|
public fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) {
|
||||||
child(ConfigEditor) {
|
child(ConfigEditor) {
|
||||||
attrs {
|
attrs {
|
||||||
this.key = key?.toString() ?: ""
|
this.key = key?.toString() ?: ""
|
||||||
@ -234,7 +234,7 @@ fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, de
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RBuilder.configEditor(
|
public fun RBuilder.configEditor(
|
||||||
obj: Configurable,
|
obj: Configurable,
|
||||||
descriptor: NodeDescriptor? = obj.descriptor,
|
descriptor: NodeDescriptor? = obj.descriptor,
|
||||||
default: Meta? = null,
|
default: Meta? = null,
|
||||||
|
@ -21,24 +21,24 @@ public external interface MetaViewerProps : RProps {
|
|||||||
/**
|
/**
|
||||||
* Root meta
|
* Root meta
|
||||||
*/
|
*/
|
||||||
var root: Meta
|
public var root: Meta
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full path to the displayed node in [root]. Could be empty
|
* Full path to the displayed node in [root]. Could be empty
|
||||||
*/
|
*/
|
||||||
var name: Name
|
public var name: Name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root descriptor
|
* Root descriptor
|
||||||
*/
|
*/
|
||||||
var descriptor: NodeDescriptor?
|
public var descriptor: NodeDescriptor?
|
||||||
}
|
}
|
||||||
|
|
||||||
private val MetaViewerItem: FunctionalComponent<MetaViewerProps> = component { props ->
|
private val MetaViewerItem: FunctionalComponent<MetaViewerProps> = functionalComponent("MetaViewerItem") { props ->
|
||||||
metaViewerItem(props)
|
metaViewerItem(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RFBuilder.metaViewerItem(props: MetaViewerProps) {
|
private fun RBuilder.metaViewerItem(props: MetaViewerProps) {
|
||||||
var expanded: Boolean by useState { true }
|
var expanded: Boolean by useState { true }
|
||||||
val item = props.root[props.name]
|
val item = props.root[props.name]
|
||||||
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
||||||
@ -137,7 +137,7 @@ private fun RFBuilder.metaViewerItem(props: MetaViewerProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
val MetaViewer = component<MetaViewerProps> { props ->
|
public val MetaViewer:FunctionalComponent<MetaViewerProps> = functionalComponent<MetaViewerProps>("MetaViewer") { props ->
|
||||||
child(MetaViewerItem) {
|
child(MetaViewerItem) {
|
||||||
attrs {
|
attrs {
|
||||||
this.key = ""
|
this.key = ""
|
||||||
@ -148,7 +148,7 @@ val MetaViewer = component<MetaViewerProps> { props ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RBuilder.metaViewer(meta: Meta, descriptor: NodeDescriptor? = null, key: Any? = null) {
|
public fun RBuilder.metaViewer(meta: Meta, descriptor: NodeDescriptor? = null, key: Any? = null) {
|
||||||
child(MetaViewer) {
|
child(MetaViewer) {
|
||||||
attrs {
|
attrs {
|
||||||
this.key = key?.toString() ?: ""
|
this.key = key?.toString() ?: ""
|
||||||
|
@ -19,7 +19,7 @@ public external interface ObjectTreeProps : RProps {
|
|||||||
public var clickCallback: (Name) -> Unit
|
public var clickCallback: (Name) -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
|
private fun RBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||||
var expanded: Boolean by useState { props.selected?.startsWith(props.name) ?: false }
|
var expanded: Boolean by useState { props.selected?.startsWith(props.name) ?: false }
|
||||||
|
|
||||||
val onClick: (Event) -> Unit = {
|
val onClick: (Event) -> Unit = {
|
||||||
@ -103,11 +103,11 @@ private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
val ObjectTree: FunctionalComponent<ObjectTreeProps> = component { props ->
|
public val ObjectTree: FunctionalComponent<ObjectTreeProps> = functionalComponent("ObjectTree") { props ->
|
||||||
objectTree(props)
|
objectTree(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RBuilder.objectTree(
|
public fun RBuilder.objectTree(
|
||||||
vision: Vision,
|
vision: Vision,
|
||||||
selected: Name? = null,
|
selected: Name? = null,
|
||||||
clickCallback: (Name) -> Unit = {}
|
clickCallback: (Name) -> Unit = {}
|
||||||
|
@ -4,11 +4,11 @@ import kotlinx.css.*
|
|||||||
import kotlinx.css.properties.*
|
import kotlinx.css.properties.*
|
||||||
import styled.StyleSheet
|
import styled.StyleSheet
|
||||||
|
|
||||||
object TreeStyles : StyleSheet("treeStyles", true) {
|
public object TreeStyles : StyleSheet("treeStyles", true) {
|
||||||
/**
|
/**
|
||||||
* Remove default bullets
|
* Remove default bullets
|
||||||
*/
|
*/
|
||||||
val tree by css {
|
public val tree by css {
|
||||||
paddingLeft = 8.px
|
paddingLeft = 8.px
|
||||||
marginLeft = 0.px
|
marginLeft = 0.px
|
||||||
listStyleType = ListStyleType.none
|
listStyleType = ListStyleType.none
|
||||||
@ -17,7 +17,7 @@ object TreeStyles : StyleSheet("treeStyles", true) {
|
|||||||
/**
|
/**
|
||||||
* Style the caret/arrow
|
* Style the caret/arrow
|
||||||
*/
|
*/
|
||||||
val treeCaret by css {
|
public val treeCaret by css {
|
||||||
cursor = Cursor.pointer
|
cursor = Cursor.pointer
|
||||||
userSelect = UserSelect.none
|
userSelect = UserSelect.none
|
||||||
/* Create the caret/arrow with a unicode, and style it */
|
/* Create the caret/arrow with a unicode, and style it */
|
||||||
|
@ -6,11 +6,12 @@ import kotlinx.css.display
|
|||||||
import kotlinx.css.flexDirection
|
import kotlinx.css.flexDirection
|
||||||
import kotlinx.html.DIV
|
import kotlinx.html.DIV
|
||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
|
import react.ReactElement
|
||||||
import styled.StyledDOMBuilder
|
import styled.StyledDOMBuilder
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
|
|
||||||
inline fun RBuilder.flexColumn(block: StyledDOMBuilder<DIV>.() -> Unit) =
|
public inline fun RBuilder.flexColumn(block: StyledDOMBuilder<DIV>.() -> Unit): ReactElement =
|
||||||
styledDiv {
|
styledDiv {
|
||||||
css {
|
css {
|
||||||
display = Display.flex
|
display = Display.flex
|
||||||
@ -19,7 +20,7 @@ inline fun RBuilder.flexColumn(block: StyledDOMBuilder<DIV>.() -> Unit) =
|
|||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun RBuilder.flexRow(block: StyledDOMBuilder<DIV>.() -> Unit) =
|
public inline fun RBuilder.flexRow(block: StyledDOMBuilder<DIV>.() -> Unit): ReactElement =
|
||||||
styledDiv {
|
styledDiv {
|
||||||
css {
|
css {
|
||||||
display = Display.flex
|
display = Display.flex
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
package hep.dataforge.vision.react
|
|
||||||
|
|
||||||
import react.*
|
|
||||||
|
|
||||||
public class RFBuilder : RBuilder()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get functional component from [func]
|
|
||||||
*/
|
|
||||||
public inline fun <P : RProps> component(
|
|
||||||
crossinline func: RFBuilder.(props: P) -> Unit,
|
|
||||||
): FunctionalComponent<P> {
|
|
||||||
return { props: P ->
|
|
||||||
val nodes = RFBuilder().apply { func(props) }.childList
|
|
||||||
when (nodes.size) {
|
|
||||||
0 -> null
|
|
||||||
1 -> nodes.first()
|
|
||||||
else -> createElement(Fragment, kotlinext.js.js {}, *nodes.toTypedArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
//public fun <T> RFBuilder.memoize(vararg deps: dynamic, builder: () -> T): T = useMemo(builder, deps)
|
|
||||||
|
|
@ -13,9 +13,9 @@ import kotlinx.coroutines.Job
|
|||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
|
|
||||||
expect class Counter() {
|
public expect class Counter() {
|
||||||
fun get(): Int
|
public fun get(): Int
|
||||||
fun incrementAndGet(): Int
|
public fun incrementAndGet(): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Point3D?.safePlus(other: Point3D?): Point3D? = if (this == null && other == null) {
|
private fun Point3D?.safePlus(other: Point3D?): Point3D? = if (this == null && other == null) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package hep.dataforge.vision.gdml
|
package hep.dataforge.vision.gdml
|
||||||
|
|
||||||
actual class Counter {
|
public actual class Counter {
|
||||||
private var count: Int = 0
|
private var count: Int = 0
|
||||||
actual fun get(): Int = count
|
public actual fun get(): Int = count
|
||||||
|
|
||||||
actual fun incrementAndGet(): Int = count++
|
public actual fun incrementAndGet(): Int = count++
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ import java.nio.file.Files
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
actual typealias Counter = AtomicInteger
|
public actual typealias Counter = AtomicInteger
|
||||||
|
|
||||||
fun GDML.Companion.readFile(file: Path): GDML {
|
fun GDML.Companion.readFile(file: Path): GDML {
|
||||||
val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8")
|
val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8")
|
||||||
|
@ -29,11 +29,11 @@ import info.laht.threekt.math.Vector2
|
|||||||
import info.laht.threekt.objects.LineSegments
|
import info.laht.threekt.objects.LineSegments
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
import info.laht.threekt.scenes.Scene
|
import info.laht.threekt.scenes.Scene
|
||||||
|
import kotlinx.browser.window
|
||||||
|
import kotlinx.dom.clear
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import org.w3c.dom.events.MouseEvent
|
import org.w3c.dom.events.MouseEvent
|
||||||
import kotlin.browser.window
|
|
||||||
import kotlin.dom.clear
|
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
@ -41,11 +41,16 @@ import kotlin.math.sin
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Canvas3DOptions) : Renderer<Solid> {
|
public class ThreeCanvas(
|
||||||
|
element: HTMLElement,
|
||||||
|
public val three: ThreePlugin,
|
||||||
|
public val options: Canvas3DOptions,
|
||||||
|
public val onClick: ((Name?) -> Unit)? = null,
|
||||||
|
) : Renderer<Solid> {
|
||||||
|
|
||||||
override val context: Context get() = three.context
|
override val context: Context get() = three.context
|
||||||
|
|
||||||
var content: Solid? = null
|
public var content: Solid? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private var root: Object3D? = null
|
private var root: Object3D? = null
|
||||||
@ -53,17 +58,15 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
|
|||||||
private val raycaster = Raycaster()
|
private val raycaster = Raycaster()
|
||||||
private val mousePosition: Vector2 = Vector2()
|
private val mousePosition: Vector2 = Vector2()
|
||||||
|
|
||||||
var onClick: ((Name?) -> Unit)? = null
|
public val axes: AxesHelper = AxesHelper(options.axes.size.toInt()).apply {
|
||||||
|
|
||||||
val axes = AxesHelper(options.axes.size.toInt()).apply {
|
|
||||||
visible = options.axes.visible
|
visible = options.axes.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
val scene: Scene = Scene().apply {
|
public val scene: Scene = Scene().apply {
|
||||||
add(axes)
|
add(axes)
|
||||||
}
|
}
|
||||||
|
|
||||||
val camera = buildCamera(options.camera)
|
public val camera: PerspectiveCamera = buildCamera(options.camera)
|
||||||
|
|
||||||
private var picked: Object3D? = null
|
private var picked: Object3D? = null
|
||||||
|
|
||||||
@ -166,7 +169,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
public fun clear() {
|
||||||
scene.children.find { it.name == "@root" }?.let {
|
scene.children.find { it.name == "@root" }?.let {
|
||||||
scene.remove(it)
|
scene.remove(it)
|
||||||
}
|
}
|
||||||
@ -192,7 +195,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
|
|||||||
private fun Object3D.toggleHighlight(
|
private fun Object3D.toggleHighlight(
|
||||||
highlight: Boolean,
|
highlight: Boolean,
|
||||||
edgesName: String,
|
edgesName: String,
|
||||||
material: LineBasicMaterial = SELECTED_MATERIAL
|
material: LineBasicMaterial = SELECTED_MATERIAL,
|
||||||
) {
|
) {
|
||||||
if (userData[DO_NOT_HIGHLIGHT_TAG] == true) {
|
if (userData[DO_NOT_HIGHLIGHT_TAG] == true) {
|
||||||
return
|
return
|
||||||
@ -220,7 +223,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
|
|||||||
/**
|
/**
|
||||||
* Toggle highlight for element with given name
|
* Toggle highlight for element with given name
|
||||||
*/
|
*/
|
||||||
fun select(name: Name?) {
|
public fun select(name: Name?) {
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
selected?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
|
selected?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
|
||||||
selected = null
|
selected = null
|
||||||
@ -234,15 +237,21 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
public companion object {
|
||||||
const val DO_NOT_HIGHLIGHT_TAG = "doNotHighlight"
|
public const val DO_NOT_HIGHLIGHT_TAG = "doNotHighlight"
|
||||||
private const val HIGHLIGHT_NAME = "@highlight"
|
private const val HIGHLIGHT_NAME = "@highlight"
|
||||||
private const val SELECT_NAME = "@select"
|
private const val SELECT_NAME = "@select"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ThreePlugin.output(element: HTMLElement, spec: Canvas3DOptions = Canvas3DOptions.empty()): ThreeCanvas =
|
public fun ThreePlugin.output(
|
||||||
ThreeCanvas(element, this, spec)
|
element: HTMLElement,
|
||||||
|
spec: Canvas3DOptions = Canvas3DOptions.empty(),
|
||||||
|
onClick: ((Name?) -> Unit)? = null,
|
||||||
|
): ThreeCanvas = ThreeCanvas(element, this, spec, onClick)
|
||||||
|
|
||||||
fun ThreePlugin.render(element: HTMLElement, obj: Solid, spec: Canvas3DOptions = Canvas3DOptions.empty()): Unit =
|
public fun ThreePlugin.render(
|
||||||
output(element, spec).render(obj)
|
element: HTMLElement, obj: Solid,
|
||||||
|
spec: Canvas3DOptions = Canvas3DOptions.empty(),
|
||||||
|
onClick: ((Name?) -> Unit)? = null,
|
||||||
|
): Unit = output(element, spec, onClick).render(obj)
|
@ -6,67 +6,88 @@ import hep.dataforge.vision.solid.Solid
|
|||||||
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import react.RComponent
|
|
||||||
import react.RProps
|
|
||||||
import react.RState
|
|
||||||
import react.dom.div
|
import react.dom.div
|
||||||
import react.dom.findDOMNode
|
|
||||||
|
|
||||||
public external interface ThreeCanvasProps : RProps {
|
public external interface ThreeCanvasProps : RProps {
|
||||||
var context: Context
|
public var context: Context
|
||||||
var obj: Solid
|
public var obj: Solid?
|
||||||
var options: Canvas3DOptions?
|
public var options: Canvas3DOptions?
|
||||||
var selected: Name?
|
public var selected: Name?
|
||||||
var clickCallback: (Name?) -> Unit
|
public var clickCallback: (Name?) -> Unit
|
||||||
var canvasCallback: ((ThreeCanvas?) -> Unit)?
|
public var canvasCallback: ((ThreeCanvas?) -> Unit)?
|
||||||
}
|
}
|
||||||
|
|
||||||
public external interface ThreeCanvasState : RState {
|
public external interface ThreeCanvasState : RState {
|
||||||
var element: Element?
|
public var element: Element?
|
||||||
// var canvas: ThreeCanvas?
|
// var canvas: ThreeCanvas?
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsExport
|
public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functionalComponent(
|
||||||
public class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() {
|
"ThreeCanvasComponent"
|
||||||
|
) { props ->
|
||||||
|
val elementRef = useRef<Element?>(null)
|
||||||
|
var canvas by useState<ThreeCanvas?>(null)
|
||||||
|
|
||||||
private var canvas: ThreeCanvas? = null
|
useEffect(listOf(props.context, props.obj, props.options, elementRef)) {
|
||||||
|
if (canvas == null) {
|
||||||
override fun componentDidMount() {
|
val element = elementRef.current as? HTMLElement ?: error("Canvas element not found")
|
||||||
if(canvas == null) {
|
|
||||||
val element = state.element as? HTMLElement ?: error("Canvas element not found")
|
|
||||||
val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin)
|
val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin)
|
||||||
canvas = three.output(element, props.options ?: Canvas3DOptions.empty()).apply {
|
val newCanvas = three.output(element, props.options ?: Canvas3DOptions.empty(), props.clickCallback)
|
||||||
onClick = props.clickCallback
|
props.canvasCallback?.invoke(newCanvas)
|
||||||
|
canvas = newCanvas
|
||||||
}
|
}
|
||||||
props.canvasCallback?.invoke(canvas)
|
|
||||||
}
|
|
||||||
canvas?.render(props.obj)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun componentDidUpdate(prevProps: ThreeCanvasProps, prevState: ThreeCanvasState, snapshot: Any) {
|
useEffect(listOf(canvas, props.obj)) {
|
||||||
if (prevProps.obj != props.obj) {
|
props.obj?.let { obj ->
|
||||||
componentDidMount()
|
if (canvas?.content != obj) {
|
||||||
|
canvas?.render(obj)
|
||||||
}
|
}
|
||||||
if (prevProps.selected != props.selected) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(listOf(canvas, props.selected)) {
|
||||||
canvas?.select(props.selected)
|
canvas?.select(props.selected)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun RBuilder.render() {
|
|
||||||
div {
|
div {
|
||||||
ref {
|
ref = elementRef
|
||||||
state.element = findDOMNode(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun RBuilder.threeCanvas(object3D: Solid, options: Canvas3DOptions.() -> Unit = {}) {
|
//public class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() {
|
||||||
child(ThreeCanvasComponent::class) {
|
//
|
||||||
attrs {
|
// private var canvas: ThreeCanvas? = null
|
||||||
this.obj = object3D
|
//
|
||||||
this.options = Canvas3DOptions.invoke(options)
|
// override fun componentDidMount() {
|
||||||
}
|
// props.obj?.let { obj ->
|
||||||
}
|
// 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 ?: Canvas3DOptions.empty()).apply {
|
||||||
|
// onClick = props.clickCallback
|
||||||
|
// }
|
||||||
|
// props.canvasCallback?.invoke(canvas)
|
||||||
|
// }
|
||||||
|
// canvas?.render(obj)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
Loading…
Reference in New Issue
Block a user