Some doc and Sphere geometry #11 #18
17
.github/workflows/gradle.yml
vendored
Normal file
17
.github/workflows/gradle.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: Gradle build
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Set up JDK 11
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew build
|
@ -113,3 +113,11 @@ tailrec fun VisualObject.findStyle(name: String): Meta? =
|
|||||||
|
|
||||||
fun VisualObject.findAllStyles(): Laminate = Laminate(styles.mapNotNull(::findStyle))
|
fun VisualObject.findAllStyles(): Laminate = Laminate(styles.mapNotNull(::findStyle))
|
||||||
|
|
||||||
|
//operator fun VisualObject.get(name: Name): VisualObject?{
|
||||||
|
// return when {
|
||||||
|
// name.isEmpty() -> this
|
||||||
|
// this is VisualGroup -> this[name]
|
||||||
|
// else -> null
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package hep.dataforge.vis.js.editor
|
package hep.dataforge.vis.js.editor
|
||||||
|
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.vis.common.VisualGroup
|
import hep.dataforge.vis.common.VisualGroup
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import hep.dataforge.vis.common.isEmpty
|
import hep.dataforge.vis.common.isEmpty
|
||||||
@ -12,61 +13,64 @@ import org.w3c.dom.HTMLElement
|
|||||||
import org.w3c.dom.HTMLSpanElement
|
import org.w3c.dom.HTMLSpanElement
|
||||||
import kotlin.dom.clear
|
import kotlin.dom.clear
|
||||||
|
|
||||||
fun Element.objectTree(
|
fun Element.displayObjectTree(
|
||||||
token: NameToken,
|
|
||||||
obj: VisualObject,
|
obj: VisualObject,
|
||||||
clickCallback: (VisualObject) -> Unit = {}
|
clickCallback: (Name) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
clear()
|
clear()
|
||||||
append {
|
append {
|
||||||
card("Object tree") {
|
card("Object tree") {
|
||||||
subTree(token, obj, clickCallback)
|
subTree(Name.EMPTY, obj, clickCallback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TagConsumer<HTMLElement>.subTree(
|
private fun TagConsumer<HTMLElement>.subTree(
|
||||||
token: NameToken,
|
fullName: Name,
|
||||||
obj: VisualObject,
|
obj: VisualObject,
|
||||||
clickCallback: (VisualObject) -> Unit
|
clickCallback: (Name) -> Unit
|
||||||
) {
|
) {
|
||||||
|
// val fullName = parentName + token
|
||||||
|
val token = fullName.last()?.toString()?:"World"
|
||||||
|
|
||||||
if (obj is VisualGroup && !obj.isEmpty) {
|
//display as node if any child is visible
|
||||||
|
if (obj is VisualGroup && obj.children.keys.any { !it.body.startsWith("@") }) {
|
||||||
lateinit var toggle: HTMLSpanElement
|
lateinit var toggle: HTMLSpanElement
|
||||||
div("d-inline-block text-truncate") {
|
div("d-inline-block text-truncate") {
|
||||||
toggle = span("objTree-caret")
|
toggle = span("objTree-caret")
|
||||||
label("objTree-label") {
|
label("objTree-label") {
|
||||||
+token.toString()
|
+token
|
||||||
onClickFunction = { clickCallback(obj) }
|
onClickFunction = { clickCallback(fullName) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val subtree = ul("objTree-subtree")
|
val subtree = ul("objTree-subtree")
|
||||||
toggle.onclick = {
|
toggle.onclick = {
|
||||||
toggle.classList.toggle("objTree-caret-down")
|
toggle.classList.toggle("objTree-caret-down")
|
||||||
subtree.apply {
|
subtree.apply {
|
||||||
|
//If expanded, add children dynamically
|
||||||
if (toggle.classList.contains("objTree-caret-down")) {
|
if (toggle.classList.contains("objTree-caret-down")) {
|
||||||
obj.children.entries
|
obj.children.entries
|
||||||
.filter { !it.key.toString().startsWith("@") }
|
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
||||||
.sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true }
|
.sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true }
|
||||||
.forEach { (token, child) ->
|
.forEach { (childToken, child) ->
|
||||||
append {
|
append {
|
||||||
li().apply {
|
li().apply {
|
||||||
subTree(token, child, clickCallback)
|
subTree(fullName + childToken, child, clickCallback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// if not, clear them to conserve memory on very long lists
|
||||||
this.clear()
|
this.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//jQuery(subtree).asDynamic().collapse("toggle")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
div("d-inline-block text-truncate") {
|
div("d-inline-block text-truncate") {
|
||||||
span("objTree-leaf")
|
span("objTree-leaf")
|
||||||
label("objTree-label") {
|
label("objTree-label") {
|
||||||
+token.toString()
|
+token
|
||||||
onClickFunction = { clickCallback(obj) }
|
onClickFunction = { clickCallback(fullName) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,23 +5,43 @@ import hep.dataforge.js.jsObject
|
|||||||
import hep.dataforge.meta.DynamicMeta
|
import hep.dataforge.meta.DynamicMeta
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.update
|
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.VisualObject
|
||||||
import hep.dataforge.vis.common.findStyle
|
import hep.dataforge.vis.common.findStyle
|
||||||
import kotlinx.html.dom.append
|
import kotlinx.html.dom.append
|
||||||
import kotlinx.html.js.div
|
import kotlinx.html.js.*
|
||||||
import kotlinx.html.js.h4
|
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
import kotlin.collections.forEach
|
||||||
|
import kotlin.collections.isNotEmpty
|
||||||
|
import kotlin.collections.set
|
||||||
import kotlin.dom.clear
|
import kotlin.dom.clear
|
||||||
|
|
||||||
//FIXME something rotten in JS-Meta converter
|
//FIXME something rotten in JS-Meta converter
|
||||||
fun Meta.toDynamic() = JSON.parse<dynamic>(toJson().toString())
|
fun Meta.toDynamic() = JSON.parse<dynamic>(toJson().toString())
|
||||||
|
|
||||||
//TODO add node descriptor instead of configuring property selector
|
//TODO add node descriptor instead of configuring property selector
|
||||||
fun Element.propertyEditor(item: VisualObject?, propertySelector: (VisualObject) -> Meta = { it.config }) {
|
fun Element.displayPropertyEditor(
|
||||||
|
name: Name,
|
||||||
|
item: VisualObject,
|
||||||
|
propertySelector: (VisualObject) -> Meta = { it.config }
|
||||||
|
) {
|
||||||
clear()
|
clear()
|
||||||
if (item != null) {
|
|
||||||
append {
|
append {
|
||||||
card("Properties") {
|
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 dMeta: dynamic = propertySelector(item).toDynamic()
|
||||||
val options: JSONEditorOptions = jsObject {
|
val options: JSONEditorOptions = jsObject {
|
||||||
mode = "form"
|
mode = "form"
|
||||||
@ -53,4 +73,3 @@ fun Element.propertyEditor(item: VisualObject?, propertySelector: (VisualObject)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -49,6 +49,7 @@ class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
Box::class with Box.serializer()
|
Box::class with Box.serializer()
|
||||||
Convex::class with Convex.serializer()
|
Convex::class with Convex.serializer()
|
||||||
Extruded::class with Extruded.serializer()
|
Extruded::class with Extruded.serializer()
|
||||||
|
addSubclass(PolyLine.serializer())
|
||||||
addSubclass(Label3D.serializer())
|
addSubclass(Label3D.serializer())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import hep.dataforge.vis.common.VisualObject
|
|||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.IGNORE_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.IGNORE_KEY
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.SELECTED_KEY
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
||||||
import kotlinx.serialization.UseSerializers
|
import kotlinx.serialization.UseSerializers
|
||||||
|
|
||||||
@ -121,9 +120,9 @@ var VisualObject.ignore: Boolean?
|
|||||||
get() = getProperty(IGNORE_KEY,false).boolean
|
get() = getProperty(IGNORE_KEY,false).boolean
|
||||||
set(value) = setProperty(IGNORE_KEY, value)
|
set(value) = setProperty(IGNORE_KEY, value)
|
||||||
|
|
||||||
var VisualObject.selected: Boolean?
|
//var VisualObject.selected: Boolean?
|
||||||
get() = getProperty(SELECTED_KEY).boolean
|
// get() = getProperty(SELECTED_KEY).boolean
|
||||||
set(value) = setProperty(SELECTED_KEY, value)
|
// set(value) = setProperty(SELECTED_KEY, value)
|
||||||
|
|
||||||
private fun VisualObject3D.position(): Point3D =
|
private fun VisualObject3D.position(): Point3D =
|
||||||
position ?: Point3D(0.0, 0.0, 0.0).also { position = it }
|
position ?: Point3D(0.0, 0.0, 0.0).also { position = it }
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package hep.dataforge.vis.spatial.three
|
package hep.dataforge.vis.spatial.three
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import hep.dataforge.meta.boolean
|
import hep.dataforge.meta.boolean
|
||||||
import hep.dataforge.meta.get
|
|
||||||
import hep.dataforge.meta.node
|
import hep.dataforge.meta.node
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
@ -10,10 +8,10 @@ import hep.dataforge.names.startsWith
|
|||||||
import hep.dataforge.vis.spatial.Material3D
|
import hep.dataforge.vis.spatial.Material3D
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
import hep.dataforge.vis.spatial.VisualObject3D
|
||||||
import hep.dataforge.vis.spatial.layer
|
import hep.dataforge.vis.spatial.layer
|
||||||
|
import hep.dataforge.vis.spatial.three.ThreeMaterials.getMaterial
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.geometries.EdgesGeometry
|
import info.laht.threekt.geometries.EdgesGeometry
|
||||||
import info.laht.threekt.geometries.WireframeGeometry
|
import info.laht.threekt.geometries.WireframeGeometry
|
||||||
import info.laht.threekt.materials.MeshBasicMaterial
|
|
||||||
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 kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -37,7 +35,7 @@ abstract class MeshThreeFactory<in T : VisualObject3D>(
|
|||||||
|
|
||||||
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
||||||
|
|
||||||
val mesh = Mesh(geometry, MeshBasicMaterial()).apply {
|
val mesh = Mesh(geometry, getMaterial(obj)).apply {
|
||||||
matrixAutoUpdate = false
|
matrixAutoUpdate = false
|
||||||
applyEdges(obj)
|
applyEdges(obj)
|
||||||
applyWireFrame(obj)
|
applyWireFrame(obj)
|
||||||
@ -45,9 +43,6 @@ abstract class MeshThreeFactory<in T : VisualObject3D>(
|
|||||||
//set position for mesh
|
//set position for mesh
|
||||||
updatePosition(obj)
|
updatePosition(obj)
|
||||||
|
|
||||||
//set color for mesh
|
|
||||||
updateMaterial(obj)
|
|
||||||
|
|
||||||
layers.enable(obj.layer)
|
layers.enable(obj.layer)
|
||||||
children.forEach {
|
children.forEach {
|
||||||
it.layers.enable(obj.layer)
|
it.layers.enable(obj.layer)
|
||||||
|
@ -4,33 +4,55 @@ import hep.dataforge.context.Context
|
|||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.get
|
import hep.dataforge.meta.get
|
||||||
import hep.dataforge.meta.string
|
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.output.Renderer
|
||||||
import hep.dataforge.vis.common.Colors
|
import hep.dataforge.vis.common.Colors
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
import hep.dataforge.vis.spatial.VisualObject3D
|
||||||
import hep.dataforge.vis.spatial.specifications.CameraSpec
|
import hep.dataforge.vis.spatial.specifications.CameraSpec
|
||||||
import hep.dataforge.vis.spatial.specifications.CanvasSpec
|
import hep.dataforge.vis.spatial.specifications.CanvasSpec
|
||||||
import hep.dataforge.vis.spatial.specifications.ControlsSpec
|
import hep.dataforge.vis.spatial.specifications.ControlsSpec
|
||||||
|
import hep.dataforge.vis.spatial.three.ThreeMaterials.HIGHLIGHT_MATERIAL
|
||||||
import info.laht.threekt.WebGLRenderer
|
import info.laht.threekt.WebGLRenderer
|
||||||
import info.laht.threekt.cameras.PerspectiveCamera
|
import info.laht.threekt.cameras.PerspectiveCamera
|
||||||
|
import info.laht.threekt.core.BufferGeometry
|
||||||
|
import info.laht.threekt.core.Object3D
|
||||||
|
import info.laht.threekt.core.Raycaster
|
||||||
import info.laht.threekt.external.controls.OrbitControls
|
import info.laht.threekt.external.controls.OrbitControls
|
||||||
import info.laht.threekt.external.controls.TrackballControls
|
import info.laht.threekt.external.controls.TrackballControls
|
||||||
|
import info.laht.threekt.geometries.EdgesGeometry
|
||||||
import info.laht.threekt.helpers.AxesHelper
|
import info.laht.threekt.helpers.AxesHelper
|
||||||
|
import info.laht.threekt.math.Vector2
|
||||||
|
import info.laht.threekt.objects.LineSegments
|
||||||
|
import info.laht.threekt.objects.Mesh
|
||||||
import info.laht.threekt.scenes.Scene
|
import info.laht.threekt.scenes.Scene
|
||||||
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 kotlin.browser.window
|
import kotlin.browser.window
|
||||||
import kotlin.dom.clear
|
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
|
||||||
|
|
||||||
class ThreeCanvas(val three: ThreePlugin, val spec: CanvasSpec) : Renderer<VisualObject3D> {
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val spec: CanvasSpec) : Renderer<VisualObject3D> {
|
||||||
|
|
||||||
override val context: Context get() = three.context
|
override val context: Context get() = three.context
|
||||||
|
|
||||||
var content: VisualObject3D? = null
|
var content: VisualObject3D? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
private var root: Object3D? = null
|
||||||
|
|
||||||
|
private val raycaster = Raycaster()
|
||||||
|
private val mousePosition: Vector2 = Vector2()
|
||||||
|
|
||||||
|
var clickListener: ((Name) -> Unit)? = null
|
||||||
|
|
||||||
val axes = AxesHelper(spec.axes.size.toInt()).apply {
|
val axes = AxesHelper(spec.axes.size.toInt()).apply {
|
||||||
visible = spec.axes.visible
|
visible = spec.axes.visible
|
||||||
}
|
}
|
||||||
@ -41,6 +63,85 @@ class ThreeCanvas(val three: ThreePlugin, val spec: CanvasSpec) : Renderer<Visua
|
|||||||
|
|
||||||
val camera = buildCamera(spec.camera)
|
val camera = buildCamera(spec.camera)
|
||||||
|
|
||||||
|
init {
|
||||||
|
element.clear()
|
||||||
|
|
||||||
|
//Attach listener to track mouse changes
|
||||||
|
element.addEventListener("mousemove", { event ->
|
||||||
|
(event as? MouseEvent)?.run {
|
||||||
|
val rect = element.getBoundingClientRect()
|
||||||
|
mousePosition.x = ((event.clientX - rect.left) / element.clientWidth) * 2 - 1
|
||||||
|
mousePosition.y = -((event.clientY - rect.top) / element.clientHeight) * 2 + 1
|
||||||
|
}
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
element.addEventListener("mousedown", { event ->
|
||||||
|
val mesh = pick()
|
||||||
|
if (mesh != null) {
|
||||||
|
val name = mesh.fullName()
|
||||||
|
clickListener?.invoke(name)
|
||||||
|
}
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
camera.aspect = 1.0
|
||||||
|
|
||||||
|
val renderer = WebGLRenderer { antialias = true }.apply {
|
||||||
|
setClearColor(Colors.skyblue, 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
addControls(renderer.domElement, spec.controls)
|
||||||
|
|
||||||
|
fun animate() {
|
||||||
|
val mesh = pick()
|
||||||
|
|
||||||
|
if (mesh != null && highlighted != mesh) {
|
||||||
|
highlighted?.toggleHighlight(false)
|
||||||
|
mesh.toggleHighlight(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.requestAnimationFrame {
|
||||||
|
animate()
|
||||||
|
}
|
||||||
|
renderer.render(scene, camera)
|
||||||
|
}
|
||||||
|
|
||||||
|
element.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
renderer.setSize(max(spec.minSize, element.offsetWidth), max(spec.minSize, element.offsetWidth))
|
||||||
|
|
||||||
|
element.onresize = {
|
||||||
|
renderer.setSize(element.offsetWidth, element.offsetWidth)
|
||||||
|
camera.updateProjectionMatrix()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
private fun Object3D.fullName(): Name {
|
||||||
|
if (root == null) error("Can't resolve element name without the root")
|
||||||
|
return if (parent == root) {
|
||||||
|
name.toName()
|
||||||
|
} else {
|
||||||
|
(parent?.fullName() ?: Name.EMPTY) + name.toName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildCamera(spec: CameraSpec) = PerspectiveCamera(
|
private fun buildCamera(spec: CameraSpec) = PerspectiveCamera(
|
||||||
spec.fov,
|
spec.fov,
|
||||||
1.0,
|
1.0,
|
||||||
@ -59,47 +160,56 @@ class ThreeCanvas(val three: ThreePlugin, val spec: CanvasSpec) : Renderer<Visua
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun attach(element: HTMLElement) {
|
|
||||||
element.clear()
|
|
||||||
|
|
||||||
camera.aspect = 1.0
|
|
||||||
|
|
||||||
val renderer = WebGLRenderer { antialias = true }.apply {
|
|
||||||
setClearColor(Colors.skyblue, 1)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
addControls(renderer.domElement, spec.controls)
|
|
||||||
|
|
||||||
fun animate() {
|
|
||||||
window.requestAnimationFrame {
|
|
||||||
animate()
|
|
||||||
}
|
|
||||||
renderer.render(scene, camera)
|
|
||||||
}
|
|
||||||
|
|
||||||
element.appendChild(renderer.domElement)
|
|
||||||
|
|
||||||
renderer.setSize(max(spec.minSize, element.offsetWidth), max(spec.minSize, element.offsetWidth))
|
|
||||||
|
|
||||||
element.onresize = {
|
|
||||||
renderer.setSize(element.offsetWidth, element.offsetWidth)
|
|
||||||
camera.updateProjectionMatrix()
|
|
||||||
}
|
|
||||||
|
|
||||||
animate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun render(obj: VisualObject3D, meta: Meta) {
|
override fun render(obj: VisualObject3D, meta: Meta) {
|
||||||
content = obj
|
//clear old root
|
||||||
|
scene.children.find { it.name == "@root" }?.let {
|
||||||
|
scene.remove(it)
|
||||||
|
}
|
||||||
|
|
||||||
val object3D = three.buildObject3D(obj)
|
val object3D = three.buildObject3D(obj)
|
||||||
|
object3D.name = "@root"
|
||||||
scene.add(object3D)
|
scene.add(object3D)
|
||||||
|
content = obj
|
||||||
|
root = object3D
|
||||||
|
}
|
||||||
|
|
||||||
|
private var highlighted: Mesh? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle highlight for the given [Mesh] object
|
||||||
|
*/
|
||||||
|
private fun Mesh.toggleHighlight(highlight: Boolean) {
|
||||||
|
if (highlight) {
|
||||||
|
val edges = LineSegments(
|
||||||
|
EdgesGeometry(geometry as BufferGeometry),
|
||||||
|
HIGHLIGHT_MATERIAL
|
||||||
|
).apply {
|
||||||
|
name = "@highlight"
|
||||||
|
}
|
||||||
|
add(edges)
|
||||||
|
highlighted = this
|
||||||
|
} else {
|
||||||
|
val highlightEdges = children.find { it.name == "@highlight" }
|
||||||
|
highlightEdges?.let { remove(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ThreePlugin.output(element: HTMLElement? = null, spec: CanvasSpec = CanvasSpec.empty()): ThreeCanvas =
|
/**
|
||||||
ThreeCanvas(this, spec).apply {
|
* Toggle highlight for element with given name
|
||||||
if (element != null) {
|
*/
|
||||||
attach(element)
|
fun highlight(name: Name?) {
|
||||||
|
if (name == null) {
|
||||||
|
highlighted?.toggleHighlight(false)
|
||||||
|
highlighted = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val mesh = root?.findChild(name) as? Mesh
|
||||||
|
if (mesh != null && highlighted != mesh) {
|
||||||
|
highlighted?.toggleHighlight(false)
|
||||||
|
mesh.toggleHighlight(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ThreePlugin.output(element: HTMLElement, spec: CanvasSpec = CanvasSpec.empty()): ThreeCanvas =
|
||||||
|
ThreeCanvas(element, this, spec)
|
@ -0,0 +1,56 @@
|
|||||||
|
package hep.dataforge.vis.spatial.three
|
||||||
|
|
||||||
|
import hep.dataforge.vis.spatial.Label3D
|
||||||
|
import hep.dataforge.vis.spatial.color
|
||||||
|
import info.laht.threekt.DoubleSide
|
||||||
|
import info.laht.threekt.core.Object3D
|
||||||
|
import info.laht.threekt.geometries.PlaneBufferGeometry
|
||||||
|
import info.laht.threekt.materials.MeshBasicMaterial
|
||||||
|
import info.laht.threekt.objects.Mesh
|
||||||
|
import info.laht.threekt.textures.Texture
|
||||||
|
import org.w3c.dom.CanvasRenderingContext2D
|
||||||
|
import org.w3c.dom.CanvasTextBaseline
|
||||||
|
import org.w3c.dom.HTMLCanvasElement
|
||||||
|
import org.w3c.dom.MIDDLE
|
||||||
|
import kotlin.browser.document
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using example from http://stemkoski.github.io/Three.js/Texture-From-Canvas.html
|
||||||
|
*/
|
||||||
|
object ThreeCanvasLabelFactory: ThreeFactory<Label3D> {
|
||||||
|
override val type: KClass<in Label3D> get() = Label3D::class
|
||||||
|
|
||||||
|
override fun invoke(obj: Label3D): Object3D {
|
||||||
|
val canvas = document.createElement("canvas") as HTMLCanvasElement
|
||||||
|
val context = canvas.getContext("2d") as CanvasRenderingContext2D
|
||||||
|
context.font = "Bold ${obj.fontSize}pt ${obj.fontFamily}"
|
||||||
|
context.fillStyle = obj.color ?: "black"
|
||||||
|
context.textBaseline = CanvasTextBaseline.MIDDLE
|
||||||
|
val metrics = context.measureText(obj.text)
|
||||||
|
//canvas.width = metrics.width.toInt()
|
||||||
|
|
||||||
|
|
||||||
|
context.fillText(obj.text, (canvas.width - metrics.width)/2, 0.5*canvas.height)
|
||||||
|
|
||||||
|
|
||||||
|
// canvas contents will be used for a texture
|
||||||
|
val texture = Texture(canvas)
|
||||||
|
texture.needsUpdate = true
|
||||||
|
|
||||||
|
val material = MeshBasicMaterial().apply {
|
||||||
|
map = texture
|
||||||
|
side = DoubleSide
|
||||||
|
transparent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val mesh = Mesh(
|
||||||
|
PlaneBufferGeometry(canvas.width, canvas.height),
|
||||||
|
material
|
||||||
|
)
|
||||||
|
|
||||||
|
mesh.updatePosition(obj)
|
||||||
|
|
||||||
|
return mesh
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import hep.dataforge.vis.common.VisualObject
|
|||||||
import hep.dataforge.vis.spatial.*
|
import hep.dataforge.vis.spatial.*
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
|
||||||
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
||||||
|
import hep.dataforge.vis.spatial.three.ThreeMaterials.getMaterial
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
@ -53,14 +54,13 @@ fun Object3D.updatePosition(obj: VisualObject3D) {
|
|||||||
/**
|
/**
|
||||||
* Update non-position non-geometry property
|
* Update non-position non-geometry property
|
||||||
*/
|
*/
|
||||||
fun Object3D.updateProperty(source: VisualObject, propertyName: Name) {
|
fun Object3D.updateProperty(source: VisualObject3D, propertyName: Name) {
|
||||||
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
||||||
updateMaterial(source)
|
this.material = getMaterial(source)
|
||||||
} else if (
|
} else if (
|
||||||
source is VisualObject3D &&
|
propertyName.startsWith(VisualObject3D.position)
|
||||||
(propertyName.startsWith(VisualObject3D.position)
|
|
||||||
|| propertyName.startsWith(VisualObject3D.rotation)
|
|| propertyName.startsWith(VisualObject3D.rotation)
|
||||||
|| propertyName.startsWith(VisualObject3D.scale))
|
|| propertyName.startsWith(VisualObject3D.scale)
|
||||||
) {
|
) {
|
||||||
//update position of mesh using this object
|
//update position of mesh using this object
|
||||||
updatePosition(source)
|
updatePosition(source)
|
||||||
|
@ -34,7 +34,7 @@ class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
|
|||||||
override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) {
|
override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) {
|
||||||
val face = Face3(append(vertex1), append(vertex2), append(vertex3), normal ?: Vector3(0, 0, 0))
|
val face = Face3(append(vertex1), append(vertex2), append(vertex3), normal ?: Vector3(0, 0, 0))
|
||||||
meta["materialIndex"].int?.let { face.materialIndex = it }
|
meta["materialIndex"].int?.let { face.materialIndex = it }
|
||||||
meta["color"]?.color()?.let { face.color = it }
|
meta["color"]?.getColor()?.let { face.color = it }
|
||||||
faces.add(face)
|
faces.add(face)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,56 +1,33 @@
|
|||||||
package hep.dataforge.vis.spatial.three
|
package hep.dataforge.vis.spatial.three
|
||||||
|
|
||||||
|
import hep.dataforge.js.jsObject
|
||||||
|
import hep.dataforge.meta.MetaItem
|
||||||
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.vis.spatial.Label3D
|
import hep.dataforge.vis.spatial.Label3D
|
||||||
import hep.dataforge.vis.spatial.color
|
import hep.dataforge.vis.spatial.three.ThreeMaterials.getMaterial
|
||||||
import info.laht.threekt.DoubleSide
|
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.geometries.PlaneBufferGeometry
|
import info.laht.threekt.geometries.TextBufferGeometry
|
||||||
import info.laht.threekt.materials.MeshBasicMaterial
|
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
import info.laht.threekt.textures.Texture
|
|
||||||
import org.w3c.dom.CanvasRenderingContext2D
|
|
||||||
import org.w3c.dom.CanvasTextBaseline
|
|
||||||
import org.w3c.dom.HTMLCanvasElement
|
|
||||||
import org.w3c.dom.MIDDLE
|
|
||||||
import kotlin.browser.document
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using example from http://stemkoski.github.io/Three.js/Texture-From-Canvas.html
|
*
|
||||||
*/
|
*/
|
||||||
object ThreeLabelFactory : ThreeFactory<Label3D> {
|
object ThreeLabelFactory : ThreeFactory<Label3D> {
|
||||||
override val type: KClass<in Label3D> get() = Label3D::class
|
override val type: KClass<in Label3D> get() = Label3D::class
|
||||||
|
|
||||||
override fun invoke(obj: Label3D): Object3D {
|
override fun invoke(obj: Label3D): Object3D {
|
||||||
val canvas = document.createElement("canvas") as HTMLCanvasElement
|
val textGeo = TextBufferGeometry( obj.text, jsObject {
|
||||||
val context = canvas.getContext("2d") as CanvasRenderingContext2D
|
font = obj.fontFamily
|
||||||
context.font = "Bold ${obj.fontSize}pt ${obj.fontFamily}"
|
size = 20
|
||||||
context.fillStyle = obj.color ?: "black"
|
height = 1
|
||||||
context.textBaseline = CanvasTextBaseline.MIDDLE
|
curveSegments = 1
|
||||||
val metrics = context.measureText(obj.text)
|
} )
|
||||||
//canvas.width = metrics.width.toInt()
|
return Mesh(textGeo, getMaterial(obj)).apply {
|
||||||
|
updatePosition(obj)
|
||||||
|
obj.onPropertyChange(this@ThreeLabelFactory){name: Name, before: MetaItem<*>?, after: MetaItem<*>? ->
|
||||||
context.fillText(obj.text, (canvas.width - metrics.width)/2, 0.5*canvas.height)
|
//TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
// canvas contents will be used for a texture
|
|
||||||
val texture = Texture(canvas)
|
|
||||||
texture.needsUpdate = true
|
|
||||||
|
|
||||||
val material = MeshBasicMaterial().apply {
|
|
||||||
map = texture
|
|
||||||
side = DoubleSide
|
|
||||||
transparent = true
|
|
||||||
}
|
|
||||||
|
|
||||||
val mesh = Mesh(
|
|
||||||
PlaneBufferGeometry(canvas.width, canvas.height),
|
|
||||||
material
|
|
||||||
)
|
|
||||||
|
|
||||||
mesh.updatePosition(obj)
|
|
||||||
|
|
||||||
return mesh
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,18 +3,18 @@ package hep.dataforge.vis.spatial.three
|
|||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.values.ValueType
|
import hep.dataforge.values.ValueType
|
||||||
import hep.dataforge.vis.common.Colors
|
import hep.dataforge.vis.common.Colors
|
||||||
import hep.dataforge.vis.common.VisualObject
|
|
||||||
import hep.dataforge.vis.spatial.Material3D
|
import hep.dataforge.vis.spatial.Material3D
|
||||||
|
import hep.dataforge.vis.spatial.VisualObject3D
|
||||||
import info.laht.threekt.materials.LineBasicMaterial
|
import info.laht.threekt.materials.LineBasicMaterial
|
||||||
|
import info.laht.threekt.materials.Material
|
||||||
import info.laht.threekt.materials.MeshBasicMaterial
|
import info.laht.threekt.materials.MeshBasicMaterial
|
||||||
import info.laht.threekt.materials.MeshPhongMaterial
|
import info.laht.threekt.materials.MeshPhongMaterial
|
||||||
import info.laht.threekt.math.Color
|
import info.laht.threekt.math.Color
|
||||||
import info.laht.threekt.objects.Mesh
|
|
||||||
|
|
||||||
|
|
||||||
object ThreeMaterials {
|
object ThreeMaterials {
|
||||||
val DEFAULT_COLOR = Color(Colors.darkgreen)
|
val DEFAULT_COLOR = Color(Colors.darkgreen)
|
||||||
val DEFAULT = MeshPhongMaterial().apply {
|
val DEFAULT = MeshBasicMaterial().apply {
|
||||||
color.set(DEFAULT_COLOR)
|
color.set(DEFAULT_COLOR)
|
||||||
}
|
}
|
||||||
val DEFAULT_LINE_COLOR = Color(Colors.black)
|
val DEFAULT_LINE_COLOR = Color(Colors.black)
|
||||||
@ -22,34 +22,49 @@ object ThreeMaterials {
|
|||||||
color.set(DEFAULT_LINE_COLOR)
|
color.set(DEFAULT_LINE_COLOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val HIGHLIGHT_MATERIAL = LineBasicMaterial().apply {
|
||||||
|
color.set(Colors.ivory)
|
||||||
|
linewidth = 8.0
|
||||||
|
}
|
||||||
|
|
||||||
// private val materialCache = HashMap<Meta, Material>()
|
fun getLineMaterial(meta: Meta?): LineBasicMaterial {
|
||||||
private val lineMaterialCache = HashMap<Meta?, LineBasicMaterial>()
|
if (meta == null) return DEFAULT_LINE
|
||||||
|
return LineBasicMaterial().apply {
|
||||||
|
color = meta[Material3D.COLOR_KEY]?.getColor() ?: DEFAULT_LINE_COLOR
|
||||||
// fun buildMaterial(meta: Meta): Material =
|
|
||||||
// MeshBasicMaterial().apply {
|
|
||||||
// color = meta["color"]?.color() ?: DEFAULT_COLOR
|
|
||||||
// opacity = meta["opacity"]?.double ?: 1.0
|
|
||||||
// transparent = meta["transparent"].boolean ?: (opacity < 1.0)
|
|
||||||
// //node["specularColor"]?.let { specular = it.color() }
|
|
||||||
// //side = 2
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun getLineMaterial(meta: Meta?): LineBasicMaterial = lineMaterialCache.getOrPut(meta) {
|
|
||||||
LineBasicMaterial().apply {
|
|
||||||
color = meta[Material3D.COLOR_KEY]?.color() ?: DEFAULT_LINE_COLOR
|
|
||||||
opacity = meta[Material3D.OPACITY_KEY].double ?: 1.0
|
opacity = meta[Material3D.OPACITY_KEY].double ?: 1.0
|
||||||
transparent = opacity < 1.0
|
transparent = opacity < 1.0
|
||||||
linewidth = meta["thickness"].double ?: 1.0
|
linewidth = meta["thickness"].double ?: 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getMaterial(visualObject3D: VisualObject3D): Material {
|
||||||
|
val meta = visualObject3D.getProperty(Material3D.MATERIAL_KEY).node ?: return ThreeMaterials.DEFAULT
|
||||||
|
return if (meta[Material3D.SPECULAR_COLOR] != null) {
|
||||||
|
MeshPhongMaterial().apply {
|
||||||
|
color = meta[Material3D.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR
|
||||||
|
specular = meta[Material3D.SPECULAR_COLOR]!!.getColor()
|
||||||
|
opacity = meta[Material3D.OPACITY_KEY]?.double ?: 1.0
|
||||||
|
transparent = opacity < 1.0
|
||||||
|
wireframe = meta[Material3D.WIREFRAME_KEY].boolean ?: false
|
||||||
|
needsUpdate = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MeshBasicMaterial().apply {
|
||||||
|
color = meta[Material3D.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR
|
||||||
|
opacity = meta[Material3D.OPACITY_KEY]?.double ?: 1.0
|
||||||
|
transparent = opacity < 1.0
|
||||||
|
wireframe = meta[Material3D.WIREFRAME_KEY].boolean ?: false
|
||||||
|
needsUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Infer color based on meta item
|
* Infer color based on meta item
|
||||||
*/
|
*/
|
||||||
fun MetaItem<*>.color(): Color {
|
fun MetaItem<*>.getColor(): Color {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is MetaItem.ValueItem -> if (this.value.type == ValueType.NUMBER) {
|
is MetaItem.ValueItem -> if (this.value.type == ValueType.NUMBER) {
|
||||||
val int = value.number.toInt()
|
val int = value.number.toInt()
|
||||||
@ -67,47 +82,3 @@ fun MetaItem<*>.color(): Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///**
|
|
||||||
// * Infer Three material based on meta item
|
|
||||||
// */
|
|
||||||
//fun Meta?.jsMaterial(): Material {
|
|
||||||
// return if (this == null) {
|
|
||||||
// ThreeMaterials.DEFAULT
|
|
||||||
// } else {
|
|
||||||
// ThreeMaterials.buildMaterial(this)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//fun Meta?.jsLineMaterial(): Material {
|
|
||||||
// return if (this == null) {
|
|
||||||
// ThreeMaterials.DEFAULT_LINE
|
|
||||||
// } else {
|
|
||||||
// ThreeMaterials.buildLineMaterial(this)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
//fun Material3D?.jsMaterial(): Material = this?.config.jsMaterial()
|
|
||||||
//fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial()
|
|
||||||
|
|
||||||
fun Mesh.updateMaterial(obj: VisualObject) {
|
|
||||||
val meta = obj.getProperty(Material3D.MATERIAL_KEY).node ?: EmptyMeta
|
|
||||||
material = if(meta[Material3D.SPECULAR_COLOR]!= null){
|
|
||||||
MeshPhongMaterial().apply {
|
|
||||||
color = meta[Material3D.COLOR_KEY]?.color() ?: ThreeMaterials.DEFAULT_COLOR
|
|
||||||
specular = meta[Material3D.SPECULAR_COLOR]!!.color()
|
|
||||||
opacity = meta[Material3D.OPACITY_KEY]?.double ?: 1.0
|
|
||||||
transparent = opacity < 1.0
|
|
||||||
wireframe = meta[Material3D.WIREFRAME_KEY].boolean ?: false
|
|
||||||
needsUpdate = true
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
MeshBasicMaterial().apply {
|
|
||||||
color = meta[Material3D.COLOR_KEY]?.color() ?: ThreeMaterials.DEFAULT_COLOR
|
|
||||||
opacity = meta[Material3D.OPACITY_KEY]?.double ?: 1.0
|
|
||||||
transparent = opacity < 1.0
|
|
||||||
wireframe = meta[Material3D.WIREFRAME_KEY].boolean ?: false
|
|
||||||
needsUpdate = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -24,7 +24,7 @@ class ThreePlugin : AbstractPlugin() {
|
|||||||
objectFactories[Sphere::class] = ThreeSphereFactory
|
objectFactories[Sphere::class] = ThreeSphereFactory
|
||||||
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
||||||
objectFactories[PolyLine::class] = ThreeLineFactory
|
objectFactories[PolyLine::class] = ThreeLineFactory
|
||||||
objectFactories[Label3D::class] = ThreeLabelFactory
|
objectFactories[Label3D::class] = ThreeCanvasLabelFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@ -25,7 +25,7 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
|
|||||||
if (name.first()?.body == PROXY_CHILD_PROPERTY_PREFIX) {
|
if (name.first()?.body == PROXY_CHILD_PROPERTY_PREFIX) {
|
||||||
val childName = name.first()?.index?.toName() ?: error("Wrong syntax for proxy child property: '$name'")
|
val childName = name.first()?.index?.toName() ?: error("Wrong syntax for proxy child property: '$name'")
|
||||||
val propertyName = name.cutFirst()
|
val propertyName = name.cutFirst()
|
||||||
val proxyChild = obj[childName] ?: error("Proxy child with name '$childName' not found")
|
val proxyChild = obj[childName] as? VisualObject3D ?: error("Proxy child with name '$childName' not found or not a 3D object")
|
||||||
val child = object3D.findChild(childName)?: error("Object child with name '$childName' not found")
|
val child = object3D.findChild(childName)?: error("Object child with name '$childName' not found")
|
||||||
child.updateProperty(proxyChild, propertyName)
|
child.updateProperty(proxyChild, propertyName)
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,7 +25,7 @@ private fun saveData(event: Event, fileName: String, mimeType: String = "text/pl
|
|||||||
fileSaver.saveAs(blob, fileName)
|
fileSaver.saveAs(blob, fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Element.threeSettings(canvas: ThreeCanvas, block: TagConsumer<HTMLElement>.() -> Unit = {}) {
|
fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer<HTMLElement>.() -> Unit = {}) {
|
||||||
clear()
|
clear()
|
||||||
append {
|
append {
|
||||||
card("Settings") {
|
card("Settings") {
|
||||||
|
@ -50,7 +50,7 @@ external interface Intersect {
|
|||||||
var `object`: Object3D
|
var `object`: Object3D
|
||||||
}
|
}
|
||||||
|
|
||||||
external class Raycaster {
|
external class Raycaster() {
|
||||||
|
|
||||||
constructor(origin: Vector3, direction: Vector3, near: Number, far: Number)
|
constructor(origin: Vector3, direction: Vector3, near: Number, far: Number)
|
||||||
|
|
||||||
@ -62,8 +62,8 @@ external class Raycaster {
|
|||||||
|
|
||||||
fun setFromCamera(coord: Vector2, camera: Camera)
|
fun setFromCamera(coord: Vector2, camera: Camera)
|
||||||
|
|
||||||
fun intersectObject(object3D: Object3D, recursive: Boolean): List<Intersect>
|
fun intersectObject(object3D: Object3D, recursive: Boolean): Array<Intersect>
|
||||||
|
|
||||||
fun intersectObjects(objects: List<Object3D>, recursive: Boolean): List<Intersect>
|
fun intersectObjects(objects: List<Object3D>, recursive: Boolean): Array<Intersect>
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
@file:Suppress(
|
||||||
|
"INTERFACE_WITH_SUPERCLASS",
|
||||||
|
"OVERRIDING_FINAL_MEMBER",
|
||||||
|
"RETURN_TYPE_MISMATCH_ON_OVERRIDE",
|
||||||
|
"CONFLICTING_OVERLOADS",
|
||||||
|
"EXTERNAL_DELEGATION"
|
||||||
|
)
|
||||||
|
@file:JsModule("three")
|
||||||
|
@file:JsNonModule
|
||||||
|
|
||||||
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
|
import info.laht.threekt.core.BufferGeometry
|
||||||
|
import info.laht.threekt.core.Geometry
|
||||||
|
import info.laht.threekt.extras.core.Shape
|
||||||
|
import info.laht.threekt.math.Vector2
|
||||||
|
|
||||||
|
external interface ExtrudeGeometryOptions {
|
||||||
|
var curveSegments: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var steps: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var depth: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelEnabled: Boolean?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelThickness: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelSize: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelOffset: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelSegments: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var extrudePath: Any?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var UVGenerator: UVGenerator?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
}
|
||||||
|
|
||||||
|
external interface UVGenerator {
|
||||||
|
fun generateTopUV(
|
||||||
|
geometry: ExtrudeBufferGeometry,
|
||||||
|
vertices: Array<Number>,
|
||||||
|
indexA: Number,
|
||||||
|
indexB: Number,
|
||||||
|
indexC: Number
|
||||||
|
): Array<Vector2>
|
||||||
|
|
||||||
|
fun generateSideWallUV(
|
||||||
|
geometry: ExtrudeBufferGeometry,
|
||||||
|
vertices: Array<Number>,
|
||||||
|
indexA: Number,
|
||||||
|
indexB: Number,
|
||||||
|
indexC: Number,
|
||||||
|
indexD: Number
|
||||||
|
): Array<Vector2>
|
||||||
|
}
|
||||||
|
|
||||||
|
external open class ExtrudeBufferGeometry : BufferGeometry {
|
||||||
|
constructor(shapes: Shape, options: ExtrudeGeometryOptions?)
|
||||||
|
constructor(shapes: Array<Shape>, options: ExtrudeGeometryOptions?)
|
||||||
|
|
||||||
|
open fun addShapeList(shapes: Array<Shape>, options: Any? = definedExternally)
|
||||||
|
open fun addShape(shape: Shape, options: Any? = definedExternally)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var WorldUVGenerator: UVGenerator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
external open class ExtrudeGeometry : Geometry {
|
||||||
|
constructor(shapes: Shape, options: ExtrudeGeometryOptions?)
|
||||||
|
constructor(shapes: Array<Shape>, options: ExtrudeGeometryOptions?)
|
||||||
|
|
||||||
|
open fun addShapeList(shapes: Array<Shape>, options: Any? = definedExternally)
|
||||||
|
open fun addShape(shape: Shape, options: Any? = definedExternally)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var WorldUVGenerator: UVGenerator
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
@file:JsModule("three")
|
||||||
|
@file:JsNonModule
|
||||||
|
|
||||||
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
|
|
||||||
|
external interface TextGeometryParameters {
|
||||||
|
var font: Any?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var size: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var height: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var curveSegments: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelEnabled: Boolean?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelThickness: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelSize: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelOffset: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
var bevelSegments: Number?
|
||||||
|
get() = definedExternally
|
||||||
|
set(value) = definedExternally
|
||||||
|
}
|
||||||
|
|
||||||
|
external class TextBufferGeometry(text: String, parameters: TextGeometryParameters? = definedExternally) : ExtrudeBufferGeometry {
|
||||||
|
val parameters: TextGeometryParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
external class TextGeometry(text: String, parameters: TextGeometryParameters? = definedExternally) : ExtrudeGeometry {
|
||||||
|
val parameters: TextGeometryParameters
|
||||||
|
}
|
@ -2,12 +2,15 @@ package hep.dataforge.vis.spatial.gdml.demo
|
|||||||
|
|
||||||
import hep.dataforge.context.Global
|
import hep.dataforge.context.Global
|
||||||
import hep.dataforge.js.Application
|
import hep.dataforge.js.Application
|
||||||
import hep.dataforge.vis.js.editor.objectTree
|
|
||||||
import hep.dataforge.js.startApplication
|
import hep.dataforge.js.startApplication
|
||||||
import hep.dataforge.meta.buildMeta
|
import hep.dataforge.meta.buildMeta
|
||||||
import hep.dataforge.meta.withBottom
|
import hep.dataforge.meta.withBottom
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.vis.js.editor.propertyEditor
|
import hep.dataforge.names.isEmpty
|
||||||
|
import hep.dataforge.vis.common.VisualGroup
|
||||||
|
import hep.dataforge.vis.common.VisualObject
|
||||||
|
import hep.dataforge.vis.js.editor.displayObjectTree
|
||||||
|
import hep.dataforge.vis.js.editor.displayPropertyEditor
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY
|
||||||
@ -19,8 +22,8 @@ import hep.dataforge.vis.spatial.gdml.GDMLTransformer
|
|||||||
import hep.dataforge.vis.spatial.gdml.LUnit
|
import hep.dataforge.vis.spatial.gdml.LUnit
|
||||||
import hep.dataforge.vis.spatial.gdml.toVisual
|
import hep.dataforge.vis.spatial.gdml.toVisual
|
||||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||||
|
import hep.dataforge.vis.spatial.three.displayCanvasControls
|
||||||
import hep.dataforge.vis.spatial.three.output
|
import hep.dataforge.vis.spatial.three.output
|
||||||
import hep.dataforge.vis.spatial.three.threeSettings
|
|
||||||
import hep.dataforge.vis.spatial.visible
|
import hep.dataforge.vis.spatial.visible
|
||||||
import kotlinx.html.dom.append
|
import kotlinx.html.dom.append
|
||||||
import kotlinx.html.js.p
|
import kotlinx.html.js.p
|
||||||
@ -154,13 +157,18 @@ private class GDMLDemoApp : Application {
|
|||||||
message("Rendering")
|
message("Rendering")
|
||||||
|
|
||||||
//output.camera.layers.enable(1)
|
//output.camera.layers.enable(1)
|
||||||
val output = three.output(canvasElement as HTMLElement)
|
val canvas = three.output(canvasElement as HTMLElement)
|
||||||
|
|
||||||
output.camera.layers.set(0)
|
canvas.camera.layers.set(0)
|
||||||
configElement.threeSettings(output)
|
configElement.displayCanvasControls(canvas)
|
||||||
//tree.visualObjectTree(visual, editor::propertyEditor)
|
//tree.visualObjectTree(visual, editor::propertyEditor)
|
||||||
treeElement.objectTree(NameToken("World"), visual) {
|
fun selectElement(name: Name) {
|
||||||
editorElement.propertyEditor(it) { item ->
|
val child: VisualObject = when {
|
||||||
|
name.isEmpty() -> visual
|
||||||
|
visual is VisualGroup -> visual[name] ?: return
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
editorElement.displayPropertyEditor(name, child) { item ->
|
||||||
//val descriptorMeta = Material3D.descriptor
|
//val descriptorMeta = Material3D.descriptor
|
||||||
|
|
||||||
val properties = item.allProperties()
|
val properties = item.allProperties()
|
||||||
@ -176,8 +184,18 @@ private class GDMLDemoApp : Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canvas.clickListener = ::selectElement
|
||||||
|
|
||||||
output.render(visual)
|
//tree.visualObjectTree(visual, editor::propertyEditor)
|
||||||
|
treeElement.displayObjectTree(visual) { name ->
|
||||||
|
selectElement(name)
|
||||||
|
canvas.highlight(name)
|
||||||
|
}
|
||||||
|
canvas.render(visual)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
canvas.render(visual)
|
||||||
message(null)
|
message(null)
|
||||||
spinner(false)
|
spinner(false)
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,13 @@ import hep.dataforge.js.Application
|
|||||||
import hep.dataforge.js.startApplication
|
import hep.dataforge.js.startApplication
|
||||||
import hep.dataforge.meta.buildMeta
|
import hep.dataforge.meta.buildMeta
|
||||||
import hep.dataforge.meta.withBottom
|
import hep.dataforge.meta.withBottom
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.isEmpty
|
||||||
|
import hep.dataforge.vis.common.VisualGroup
|
||||||
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import hep.dataforge.vis.js.editor.card
|
import hep.dataforge.vis.js.editor.card
|
||||||
import hep.dataforge.vis.js.editor.objectTree
|
import hep.dataforge.vis.js.editor.displayObjectTree
|
||||||
import hep.dataforge.vis.js.editor.propertyEditor
|
import hep.dataforge.vis.js.editor.displayPropertyEditor
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY
|
||||||
@ -16,8 +19,8 @@ import hep.dataforge.vis.spatial.Visual3DPlugin
|
|||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
import hep.dataforge.vis.spatial.VisualObject3D
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
||||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||||
|
import hep.dataforge.vis.spatial.three.displayCanvasControls
|
||||||
import hep.dataforge.vis.spatial.three.output
|
import hep.dataforge.vis.spatial.three.output
|
||||||
import hep.dataforge.vis.spatial.three.threeSettings
|
|
||||||
import hep.dataforge.vis.spatial.visible
|
import hep.dataforge.vis.spatial.visible
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.json.JsonFeature
|
import io.ktor.client.features.json.JsonFeature
|
||||||
@ -48,22 +51,21 @@ private class GDMLDemoApp : Application {
|
|||||||
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
||||||
|
|
||||||
val canvasElement = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page")
|
val canvasElement = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page")
|
||||||
val settingsElement =
|
val settingsElement = document.getElementById("settings")
|
||||||
document.getElementById("settings") ?: error("Element with id 'settings' not found on page")
|
?: error("Element with id 'settings' not found on page")
|
||||||
val treeElement = document.getElementById("tree") ?: error("Element with id 'tree' not found on page")
|
val treeElement = document.getElementById("tree") ?: error("Element with id 'tree' not found on page")
|
||||||
val editorElement = document.getElementById("editor") ?: error("Element with id 'editor' not found on page")
|
val editorElement = document.getElementById("editor") ?: error("Element with id 'editor' not found on page")
|
||||||
canvasElement.clear()
|
|
||||||
|
|
||||||
canvasElement.clear()
|
canvasElement.clear()
|
||||||
val visual: VisualObject3D = model.root
|
val visual: VisualObject3D = model.root
|
||||||
|
|
||||||
//output.camera.layers.enable(1)
|
//output.camera.layers.enable(1)
|
||||||
val output = three.output(canvasElement as HTMLElement)
|
val canvas = three.output(canvasElement as HTMLElement)
|
||||||
|
|
||||||
output.camera.layers.set(0)
|
canvas.camera.layers.set(0)
|
||||||
output.camera.position.z = -2000.0
|
canvas.camera.position.z = -2000.0
|
||||||
output.camera.position.y = 500.0
|
canvas.camera.position.y = 500.0
|
||||||
settingsElement.threeSettings(output){
|
settingsElement.displayCanvasControls(canvas) {
|
||||||
card("Events") {
|
card("Events") {
|
||||||
button {
|
button {
|
||||||
+"Next"
|
+"Next"
|
||||||
@ -82,9 +84,15 @@ private class GDMLDemoApp : Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//tree.visualObjectTree(visual, editor::propertyEditor)
|
|
||||||
treeElement.objectTree(NameToken("World"), visual) {
|
|
||||||
editorElement.propertyEditor(it) { item ->
|
fun selectElement(name: Name) {
|
||||||
|
val child: VisualObject = when {
|
||||||
|
name.isEmpty() -> visual
|
||||||
|
visual is VisualGroup -> visual[name] ?: return
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
editorElement.displayPropertyEditor(name, child) { item ->
|
||||||
//val descriptorMeta = Material3D.descriptor
|
//val descriptorMeta = Material3D.descriptor
|
||||||
|
|
||||||
val properties = item.allProperties()
|
val properties = item.allProperties()
|
||||||
@ -100,9 +108,14 @@ private class GDMLDemoApp : Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canvas.clickListener = ::selectElement
|
||||||
|
|
||||||
output.render(visual)
|
//tree.visualObjectTree(visual, editor::propertyEditor)
|
||||||
|
treeElement.displayObjectTree(visual) { name ->
|
||||||
|
selectElement(name)
|
||||||
|
canvas.highlight(name)
|
||||||
|
}
|
||||||
|
canvas.render(visual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,13 +19,15 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-3">
|
<div class="col-lg-3">
|
||||||
<div class="row" id ="settings"></div>
|
|
||||||
<div class="row" id="tree"></div>
|
<div class="row" id="tree"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="container" id = "canvas"></div>
|
<div id="canvas"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="row" id="settings"></div>
|
||||||
|
<div class="row" id="editor"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-3" id="editor"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
@ -12,10 +12,10 @@ import hep.dataforge.vis.spatial.*
|
|||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.GEOMETRY_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.GEOMETRY_KEY
|
||||||
import hep.dataforge.vis.spatial.demo.VariableBoxThreeFactory.Z_SIZE_KEY
|
import hep.dataforge.vis.spatial.demo.VariableBoxThreeFactory.Z_SIZE_KEY
|
||||||
import hep.dataforge.vis.spatial.three.*
|
import hep.dataforge.vis.spatial.three.*
|
||||||
|
import hep.dataforge.vis.spatial.three.ThreeMaterials.getMaterial
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.geometries.BoxBufferGeometry
|
import info.laht.threekt.geometries.BoxBufferGeometry
|
||||||
import info.laht.threekt.materials.MeshBasicMaterial
|
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
import kotlinx.serialization.UseSerializers
|
import kotlinx.serialization.UseSerializers
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
@ -69,16 +69,13 @@ private object VariableBoxThreeFactory : ThreeFactory<VisualObject3D> {
|
|||||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||||
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
||||||
|
|
||||||
val mesh = Mesh(geometry, MeshBasicMaterial()).apply {
|
val mesh = Mesh(geometry, getMaterial(obj)).apply {
|
||||||
applyEdges(obj)
|
applyEdges(obj)
|
||||||
applyWireFrame(obj)
|
applyWireFrame(obj)
|
||||||
|
|
||||||
//set position for mesh
|
//set position for mesh
|
||||||
updatePosition(obj)
|
updatePosition(obj)
|
||||||
|
|
||||||
//set color for mesh
|
|
||||||
updateMaterial(obj)
|
|
||||||
|
|
||||||
layers.enable(obj.layer)
|
layers.enable(obj.layer)
|
||||||
children.forEach {
|
children.forEach {
|
||||||
it.layers.enable(obj.layer)
|
it.layers.enable(obj.layer)
|
||||||
|
Loading…
Reference in New Issue
Block a user