forked from kscience/visionforge
Lines implemented
This commit is contained in:
parent
0fbad16be6
commit
ce80ffe91b
@ -4,6 +4,7 @@ import hep.dataforge.meta.*
|
|||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
import kotlin.jvm.JvmName
|
import kotlin.jvm.JvmName
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
@ -35,81 +36,79 @@ class VisualObjectDelegate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VisualObjectDelegateWrapper<T>(
|
class VisualObjectDelegateWrapper<T>(
|
||||||
|
val obj: VisualObject,
|
||||||
val key: Name?,
|
val key: Name?,
|
||||||
val default: T,
|
val default: T,
|
||||||
val inherited: Boolean,
|
val inherited: Boolean,
|
||||||
val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) },
|
val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) },
|
||||||
val read: (MetaItem<*>?) -> T?
|
val read: (MetaItem<*>?) -> T?
|
||||||
) : ReadWriteProperty<VisualObject, T> {
|
) : ReadWriteProperty<Any?, T> {
|
||||||
|
|
||||||
//private var cachedName: Name? = null
|
//private var cachedName: Name? = null
|
||||||
|
|
||||||
override fun getValue(thisRef: VisualObject, property: KProperty<*>): T {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
val name = key ?: property.name.asName()
|
val name = key ?: property.name.asName()
|
||||||
return if (inherited) {
|
return read(obj.getProperty(name,inherited))?:default
|
||||||
read(thisRef.getProperty(name))
|
|
||||||
} else {
|
|
||||||
read(thisRef.config[name])
|
|
||||||
} ?: default
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: T) {
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
val name = key ?: property.name.asName()
|
val name = key ?: property.name.asName()
|
||||||
thisRef.config[name] = value
|
obj.config[name] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.value }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.value }
|
||||||
|
|
||||||
fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.string }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.string }
|
||||||
|
|
||||||
fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.boolean }
|
||||||
|
|
||||||
fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.number }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.number }
|
||||||
|
|
||||||
fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.double }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.double }
|
||||||
|
|
||||||
fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.int }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.int }
|
||||||
|
|
||||||
|
|
||||||
fun VisualObject.node(key: String? = null, inherited: Boolean = true) =
|
fun VisualObject.node(key: String? = null, inherited: Boolean = true) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), null, inherited) { it.node }
|
VisualObjectDelegateWrapper(this, key?.toName(), null, inherited) { it.node }
|
||||||
|
|
||||||
fun VisualObject.item(key: String? = null, inherited: Boolean = true) =
|
fun VisualObject.item(key: String? = null, inherited: Boolean = true) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), null, inherited) { it }
|
VisualObjectDelegateWrapper(this, key?.toName(), null, inherited) { it }
|
||||||
|
|
||||||
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
|
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
|
||||||
|
|
||||||
@JvmName("safeString")
|
@JvmName("safeString")
|
||||||
fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.string }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.string }
|
||||||
|
|
||||||
@JvmName("safeBoolean")
|
@JvmName("safeBoolean")
|
||||||
fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.boolean }
|
||||||
|
|
||||||
@JvmName("safeNumber")
|
@JvmName("safeNumber")
|
||||||
fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.number }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.number }
|
||||||
|
|
||||||
@JvmName("safeDouble")
|
@JvmName("safeDouble")
|
||||||
fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.double }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.double }
|
||||||
|
|
||||||
@JvmName("safeInt")
|
@JvmName("safeInt")
|
||||||
fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) =
|
fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.int }
|
VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.int }
|
||||||
|
|
||||||
|
|
||||||
inline fun <reified E : Enum<E>> VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) =
|
inline fun <reified E : Enum<E>> VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(
|
VisualObjectDelegateWrapper(
|
||||||
|
this,
|
||||||
key?.let { NameToken(it).asName() },
|
key?.let { NameToken(it).asName() },
|
||||||
default,
|
default,
|
||||||
inherited
|
inherited
|
||||||
@ -121,11 +120,11 @@ fun <T> VisualObject.merge(
|
|||||||
key: String? = null,
|
key: String? = null,
|
||||||
transformer: (Sequence<MetaItem<*>>) -> T
|
transformer: (Sequence<MetaItem<*>>) -> T
|
||||||
): ReadOnlyProperty<VisualObject, T> {
|
): ReadOnlyProperty<VisualObject, T> {
|
||||||
return object : ReadOnlyProperty<VisualObject, T> {
|
return object : ReadOnlyProperty<Any?, T> {
|
||||||
override fun getValue(thisRef: VisualObject, property: KProperty<*>): T {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
val name = key?.asName() ?: property.name.asName()
|
val name = key?.toName() ?: property.name.asName()
|
||||||
val sequence = sequence<MetaItem<*>> {
|
val sequence = sequence<MetaItem<*>> {
|
||||||
var thisObj: VisualObject? = thisRef
|
var thisObj: VisualObject? = this@merge
|
||||||
while (thisObj != null) {
|
while (thisObj != null) {
|
||||||
thisObj.config[name]?.let { yield(it) }
|
thisObj.config[name]?.let { yield(it) }
|
||||||
thisObj = thisObj.parent
|
thisObj = thisObj.parent
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
@file:UseSerializers(Point3DSerializer::class)
|
||||||
|
|
||||||
|
package hep.dataforge.vis.spatial
|
||||||
|
|
||||||
|
import hep.dataforge.io.ConfigSerializer
|
||||||
|
import hep.dataforge.meta.Config
|
||||||
|
import hep.dataforge.vis.common.AbstractVisualObject
|
||||||
|
import hep.dataforge.vis.common.number
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.UseSerializers
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class PolyLine(var points: List<Point3D>) : AbstractVisualObject(), VisualObject3D {
|
||||||
|
@Serializable(ConfigSerializer::class)
|
||||||
|
override var properties: Config? = null
|
||||||
|
|
||||||
|
override var position: Point3D? = null
|
||||||
|
override var rotation: Point3D? = null
|
||||||
|
override var scale: Point3D? = null
|
||||||
|
|
||||||
|
//var lineType by string()
|
||||||
|
var thickness by number(1.0, key = "material.thickness")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun VisualGroup3D.polyline(vararg points: Point3D, name: String = "", action: PolyLine.() -> Unit = {}) =
|
||||||
|
PolyLine(points.toList()).apply(action).also { set(name, it) }
|
@ -4,6 +4,7 @@ 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.spatial.Material3D
|
import hep.dataforge.vis.spatial.Material3D
|
||||||
|
import info.laht.threekt.materials.LineBasicMaterial
|
||||||
import info.laht.threekt.materials.Material
|
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
|
||||||
@ -13,8 +14,37 @@ import info.laht.threekt.math.Color
|
|||||||
object Materials {
|
object Materials {
|
||||||
val DEFAULT_COLOR = Color(Colors.darkgreen)
|
val DEFAULT_COLOR = Color(Colors.darkgreen)
|
||||||
val DEFAULT = MeshPhongMaterial().apply {
|
val DEFAULT = MeshPhongMaterial().apply {
|
||||||
this.color.set(DEFAULT_COLOR)
|
color.set(DEFAULT_COLOR)
|
||||||
}
|
}
|
||||||
|
val DEFAULT_LINE_COLOR = Color(Colors.black)
|
||||||
|
val DEFAULT_LINE = LineBasicMaterial().apply {
|
||||||
|
color.set(DEFAULT_LINE_COLOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val materialCache = HashMap<Meta, Material>()
|
||||||
|
private val lineMaterialCache = HashMap<Meta, Material>()
|
||||||
|
|
||||||
|
fun getMaterial(meta: Meta): Material = materialCache.getOrPut(meta) {
|
||||||
|
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): Material = lineMaterialCache.getOrPut(meta) {
|
||||||
|
LineBasicMaterial().apply {
|
||||||
|
color = meta["color"]?.color() ?: DEFAULT_LINE_COLOR
|
||||||
|
opacity = meta["opacity"].double ?: 1.0
|
||||||
|
transparent = meta["transparent"].boolean ?: (opacity < 1.0)
|
||||||
|
linewidth = meta["thickness"].double ?: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,26 +71,26 @@ fun MetaItem<*>.color(): Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val materialCache = HashMap<Meta, Material>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Infer Three material based on meta item
|
* Infer Three material based on meta item
|
||||||
*/
|
*/
|
||||||
fun Meta?.jsMaterial(): Material {
|
fun Meta?.jsMaterial(): Material {
|
||||||
return if (this == null) {
|
return if (this == null) {
|
||||||
Materials.DEFAULT
|
Materials.DEFAULT
|
||||||
} else
|
} else {
|
||||||
//TODO add more options for material
|
Materials.getMaterial(this)
|
||||||
return materialCache.getOrPut(this) {
|
}
|
||||||
MeshBasicMaterial().apply {
|
|
||||||
color = get("color")?.color() ?: Materials.DEFAULT_COLOR
|
|
||||||
opacity = get("opacity")?.double ?: 1.0
|
|
||||||
transparent = get("transparent").boolean ?: (opacity < 1.0)
|
|
||||||
//node["specularColor"]?.let { specular = it.color() }
|
|
||||||
//side = 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Material3D?.jsMaterial(): Material = this?.config.jsMaterial()
|
fun Meta?.jsLineMaterial(): Material {
|
||||||
|
return if (this == null) {
|
||||||
|
Materials.DEFAULT_LINE
|
||||||
|
} else{
|
||||||
|
Materials.getLineMaterial(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Material3D?.jsMaterial(): Material = this?.config.jsMaterial()
|
||||||
|
fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial()
|
||||||
|
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
package hep.dataforge.vis.spatial.three
|
||||||
|
|
||||||
|
import hep.dataforge.meta.boolean
|
||||||
|
import hep.dataforge.meta.node
|
||||||
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.names.plus
|
||||||
|
import hep.dataforge.names.startsWith
|
||||||
|
import hep.dataforge.vis.spatial.Material3D
|
||||||
|
import hep.dataforge.vis.spatial.VisualObject3D
|
||||||
|
import hep.dataforge.vis.spatial.layer
|
||||||
|
import hep.dataforge.vis.spatial.material
|
||||||
|
import info.laht.threekt.core.BufferGeometry
|
||||||
|
import info.laht.threekt.geometries.EdgesGeometry
|
||||||
|
import info.laht.threekt.geometries.WireframeGeometry
|
||||||
|
import info.laht.threekt.objects.LineSegments
|
||||||
|
import info.laht.threekt.objects.Mesh
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic geometry-based factory
|
||||||
|
*/
|
||||||
|
abstract class MeshThreeFactory<T : VisualObject3D>(
|
||||||
|
override val type: KClass<out T>
|
||||||
|
) : ThreeFactory<T> {
|
||||||
|
/**
|
||||||
|
* Build a geometry for an object
|
||||||
|
*/
|
||||||
|
abstract fun buildGeometry(obj: T): BufferGeometry
|
||||||
|
|
||||||
|
private fun Mesh.applyEdges(obj: T) {
|
||||||
|
children.find { it.name == "edges" }?.let { remove(it) }
|
||||||
|
//inherited edges definition, enabled by default
|
||||||
|
if (obj.getProperty(EDGES_ENABLED_KEY).boolean != false) {
|
||||||
|
val material = obj.getProperty(EDGES_MATERIAL_KEY).node.jsLineMaterial()
|
||||||
|
add(
|
||||||
|
LineSegments(
|
||||||
|
EdgesGeometry(geometry as BufferGeometry),
|
||||||
|
material
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Mesh.applyWireFrame(obj: T) {
|
||||||
|
children.find { it.name == "wireframe" }?.let { remove(it) }
|
||||||
|
//inherited wireframe definition, disabled by default
|
||||||
|
if (obj.getProperty(WIREFRAME_ENABLED_KEY).boolean == true) {
|
||||||
|
val material = obj.getProperty(WIREFRAME_MATERIAL_KEY).node.jsLineMaterial()
|
||||||
|
add(
|
||||||
|
LineSegments(
|
||||||
|
WireframeGeometry(geometry as BufferGeometry),
|
||||||
|
material
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invoke(obj: T): Mesh {
|
||||||
|
//TODO add caching for geometries using templates
|
||||||
|
val geometry = buildGeometry(obj)
|
||||||
|
|
||||||
|
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||||
|
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
||||||
|
|
||||||
|
val mesh = Mesh(geometry, obj.material.jsMaterial()).apply {
|
||||||
|
matrixAutoUpdate = false
|
||||||
|
applyEdges(obj)
|
||||||
|
applyWireFrame(obj)
|
||||||
|
//set position for mesh
|
||||||
|
updatePosition(obj)
|
||||||
|
|
||||||
|
layers.enable(obj.layer)
|
||||||
|
children.forEach {
|
||||||
|
it.layers.enable(obj.layer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//add listener to object properties
|
||||||
|
obj.onPropertyChange(this) { name, _, _ ->
|
||||||
|
when {
|
||||||
|
name.startsWith(VisualObject3D.GEOMETRY_KEY) -> mesh.geometry = buildGeometry(obj)
|
||||||
|
name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
|
||||||
|
name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj)
|
||||||
|
else -> mesh.updateProperty(obj, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mesh
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val EDGES_KEY = "edges".asName()
|
||||||
|
val WIREFRAME_KEY = "wireframe".asName()
|
||||||
|
val ENABLED_KEY = "enabled".asName()
|
||||||
|
val EDGES_ENABLED_KEY = EDGES_KEY + ENABLED_KEY
|
||||||
|
val EDGES_MATERIAL_KEY = EDGES_KEY + Material3D.MATERIAL_KEY
|
||||||
|
val WIREFRAME_ENABLED_KEY = WIREFRAME_KEY + ENABLED_KEY
|
||||||
|
val WIREFRAME_MATERIAL_KEY = WIREFRAME_KEY + Material3D.MATERIAL_KEY
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,14 @@
|
|||||||
package hep.dataforge.vis.spatial.three
|
package hep.dataforge.vis.spatial.three
|
||||||
|
|
||||||
import hep.dataforge.meta.boolean
|
|
||||||
import hep.dataforge.meta.node
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.asName
|
|
||||||
import hep.dataforge.names.plus
|
|
||||||
import hep.dataforge.names.startsWith
|
import hep.dataforge.names.startsWith
|
||||||
import hep.dataforge.provider.Type
|
import hep.dataforge.provider.Type
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.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.VisualObject3D.Companion.GEOMETRY_KEY
|
|
||||||
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
||||||
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.EdgesGeometry
|
|
||||||
import info.laht.threekt.geometries.WireframeGeometry
|
|
||||||
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
|
||||||
|
|
||||||
@ -46,30 +38,6 @@ internal fun Object3D.updatePosition(obj: VisualObject3D) {
|
|||||||
updateMatrix()
|
updateMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun <T : VisualObject3D> Mesh.updateFrom(obj: T) {
|
|
||||||
matrixAutoUpdate = false
|
|
||||||
|
|
||||||
//inherited edges definition, enabled by default
|
|
||||||
if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) {
|
|
||||||
val material = obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node?.jsMaterial() ?: Materials.DEFAULT
|
|
||||||
add(LineSegments(EdgesGeometry(geometry as BufferGeometry), material))
|
|
||||||
}
|
|
||||||
|
|
||||||
//inherited wireframe definition, disabled by default
|
|
||||||
if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
|
|
||||||
val material = obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node?.jsMaterial() ?: Materials.DEFAULT
|
|
||||||
add(LineSegments(WireframeGeometry(geometry as BufferGeometry), material))
|
|
||||||
}
|
|
||||||
|
|
||||||
//set position for mesh
|
|
||||||
updatePosition(obj)
|
|
||||||
|
|
||||||
layers.enable(obj.layer)
|
|
||||||
children.forEach {
|
|
||||||
it.layers.enable(obj.layer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsafe invocation of a factory
|
* Unsafe invocation of a factory
|
||||||
*/
|
*/
|
||||||
@ -82,53 +50,6 @@ operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic geometry-based factory
|
|
||||||
*/
|
|
||||||
abstract class MeshThreeFactory<T : VisualObject3D>(override val type: KClass<out T>) : ThreeFactory<T> {
|
|
||||||
/**
|
|
||||||
* Build a geometry for an object
|
|
||||||
*/
|
|
||||||
abstract fun buildGeometry(obj: T): BufferGeometry
|
|
||||||
|
|
||||||
|
|
||||||
override fun invoke(obj: T): Mesh {
|
|
||||||
//create mesh from geometry
|
|
||||||
return buildMesh(obj) { buildGeometry(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val EDGES_KEY = "edges".asName()
|
|
||||||
val WIREFRAME_KEY = "wireframe".asName()
|
|
||||||
val ENABLED_KEY = "enabled".asName()
|
|
||||||
val EDGES_ENABLED_KEY = EDGES_KEY + ENABLED_KEY
|
|
||||||
val EDGES_MATERIAL_KEY = EDGES_KEY + MATERIAL_KEY
|
|
||||||
val WIREFRAME_ENABLED_KEY = WIREFRAME_KEY + ENABLED_KEY
|
|
||||||
val WIREFRAME_MATERIAL_KEY = WIREFRAME_KEY + MATERIAL_KEY
|
|
||||||
|
|
||||||
fun <T : VisualObject3D> buildMesh(obj: T, geometryBuilder: (T) -> BufferGeometry): Mesh {
|
|
||||||
//TODO add caching for geometries using templates
|
|
||||||
val geometry = geometryBuilder(obj)
|
|
||||||
|
|
||||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
|
||||||
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
|
||||||
|
|
||||||
val mesh = Mesh(geometry, obj.material.jsMaterial())
|
|
||||||
|
|
||||||
mesh.updateFrom(obj)
|
|
||||||
|
|
||||||
//add listener to object properties
|
|
||||||
obj.onPropertyChange(this) { name, _, _ ->
|
|
||||||
mesh.updateProperty(obj, name)
|
|
||||||
if (name.startsWith(GEOMETRY_KEY)) {
|
|
||||||
mesh.geometry = geometryBuilder(obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mesh
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Object3D.updateProperty(source: VisualObject, propertyName: Name) {
|
fun Object3D.updateProperty(source: VisualObject, propertyName: Name) {
|
||||||
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
||||||
//updated material
|
//updated material
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package hep.dataforge.vis.spatial.three
|
||||||
|
|
||||||
|
import hep.dataforge.vis.spatial.PolyLine
|
||||||
|
import hep.dataforge.vis.spatial.layer
|
||||||
|
import hep.dataforge.vis.spatial.material
|
||||||
|
import info.laht.threekt.core.Geometry
|
||||||
|
import info.laht.threekt.core.Object3D
|
||||||
|
import info.laht.threekt.objects.LineSegments
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
object ThreeLineFactory : ThreeFactory<PolyLine> {
|
||||||
|
override val type: KClass<out PolyLine> get() = PolyLine::class
|
||||||
|
|
||||||
|
override fun invoke(obj: PolyLine): Object3D {
|
||||||
|
val geometry = Geometry().apply {
|
||||||
|
vertices = obj.points.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
val material = obj.material.jsLineMaterial()
|
||||||
|
return LineSegments(geometry, material).apply {
|
||||||
|
|
||||||
|
updatePosition(obj)
|
||||||
|
layers.enable(obj.layer)
|
||||||
|
|
||||||
|
//add listener to object properties
|
||||||
|
obj.onPropertyChange(this) { propertyName, _, _ ->
|
||||||
|
updateProperty(obj, propertyName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,7 @@ class ThreePlugin : AbstractPlugin() {
|
|||||||
objectFactories[Convex::class] = ThreeConvexFactory
|
objectFactories[Convex::class] = ThreeConvexFactory
|
||||||
objectFactories[Sphere::class] = ThreeSphereFactory
|
objectFactories[Sphere::class] = ThreeSphereFactory
|
||||||
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
||||||
|
objectFactories[PolyLine::class] = ThreeLineFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findObjectFactory(type: KClass<out VisualObject3D>): ThreeFactory<*>? {
|
private fun findObjectFactory(type: KClass<out VisualObject3D>): ThreeFactory<*>? {
|
||||||
|
@ -28,7 +28,10 @@
|
|||||||
package info.laht.threekt.objects
|
package info.laht.threekt.objects
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
|
import info.laht.threekt.core.Geometry
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.materials.Material
|
import info.laht.threekt.materials.Material
|
||||||
|
|
||||||
open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D
|
open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D {
|
||||||
|
constructor(geometry: Geometry, material: Material)
|
||||||
|
}
|
@ -130,6 +130,21 @@ private class ThreeDemoApp : ApplicationBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
demo("lines", "Track / line segments") {
|
||||||
|
sphere(100) {
|
||||||
|
color(Colors.blue)
|
||||||
|
detail = 50
|
||||||
|
opacity = 0.4
|
||||||
|
}
|
||||||
|
repeat(20) {
|
||||||
|
polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) {
|
||||||
|
thickness = 208.0
|
||||||
|
rotationX = it * PI2 / 20
|
||||||
|
color(Colors.green)
|
||||||
|
//rotationY = it * PI2 / 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user