Added manual tube. Fixed some bugs.

This commit is contained in:
Alexander Nozik 2019-08-01 22:07:12 +03:00
parent cf5a4fd7f6
commit 073ae8a353
16 changed files with 210 additions and 56 deletions

View File

@ -81,7 +81,11 @@ abstract class AbstractVisualObject(override val parent: VisualObject?) : Visual
} }
private var _config: Config? = null private var _config: Config? = null
override val config: Config get() = _config ?: Config().also { _config = it } override val config: Config
get() = _config ?: Config().also { config ->
_config = config
config.onChange(this, ::propertyChanged)
}
override fun setProperty(name: Name, value: Any?) { override fun setProperty(name: Name, value: Any?) {
config[name] = value config[name] = value
@ -120,6 +124,13 @@ open class VisualGroup<T : VisualObject>(parent: VisualObject?) : AbstractVisual
} }
} }
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
super.propertyChanged(name, before, after)
forEach {
it.propertyChanged(name, before, after)
}
}
private data class Listener<T : VisualObject>(val owner: Any?, val callback: (Name?, T?) -> Unit) private data class Listener<T : VisualObject>(val owner: Any?, val callback: (Name?, T?) -> Unit)
private val listeners = HashSet<Listener<T>>() private val listeners = HashSet<Listener<T>>()
@ -164,10 +175,12 @@ open class VisualGroup<T : VisualObject>(parent: VisualObject?) : AbstractVisual
* Get named child by name * Get named child by name
*/ */
operator fun get(name: Name): T? = namedChildren[name] operator fun get(name: Name): T? = namedChildren[name]
/** /**
* Get named child by string * Get named child by string
*/ */
operator fun get(key: String): T? = namedChildren[key] operator fun get(key: String): T? = namedChildren[key]
/** /**
* Get an unnamed child * Get an unnamed child
*/ */

View File

@ -33,7 +33,7 @@ class RendererDemoView : View() {
} }
} }
var color by group.config.number(1530).int var color by group.config.number(1530)
GlobalScope.launch { GlobalScope.launch {
val random = Random(111) val random = Random(111)

View File

@ -45,10 +45,14 @@ private fun VisualGroup3D.addSolid(
val aScale = solid.ascale() val aScale = solid.ascale()
return when (solid) { return when (solid) {
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name) is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
is GDMLTube -> cylinder(solid.rmax * lScale, solid.z * lScale, name) { is GDMLTube -> tube(
startAngle = solid.startphi * aScale solid.rmax * lScale,
angle = solid.deltaphi * aScale solid.z * lScale,
} solid.rmin * lScale,
solid.startphi * aScale,
solid.deltaphi * aScale,
name
)
is GDMLXtru -> extrude(name) { is GDMLXtru -> extrude(name) {
shape { shape {
solid.vertices.forEach { solid.vertices.forEach {
@ -193,8 +197,7 @@ private fun volume(
?: context.templates.addSolid(context, solid, solid.name) { ?: context.templates.addSolid(context, solid, solid.name) {
this.material = context.resolveColor(group, material, solid) this.material = context.resolveColor(group, material, solid)
} }
val wrapper = Proxy3D(this,cachedSolid) proxy(cachedSolid, solid.name)
add(wrapper)
} }
when (val vol = group.placement) { when (val vol = group.placement) {

View File

@ -90,11 +90,11 @@ private class GDMLDemoApp : ApplicationBase() {
launch { message("Converting GDML into DF-VIS format") } launch { message("Converting GDML into DF-VIS format") }
val visual = gdml.toVisual { val visual = gdml.toVisual {
lUnit = LUnit.CM lUnit = LUnit.CM
// acceptSolid = { solid -> acceptSolid = { solid ->
// !solid.name.startsWith("ecal") !solid.name.startsWith("ecal")
// && !solid.name.startsWith("V") // && !solid.name.startsWith("V")
// && !solid.name.startsWith("U") // && !solid.name.startsWith("U")
// } }
} }
launch { message("Rendering") } launch { message("Rendering") }
val output = three.output(canvas) val output = three.output(canvas)

View File

@ -31,7 +31,12 @@ private class ThreeDemoApp : ApplicationBase() {
} }
sphere(50.0) { sphere(50.0) {
x = 110 x = 110
detail = 200 detail = 16
}
tube(50, height = 10, innerRadius = 25, angle = PI) {
y = 110
detail = 16
rotationX = PI/4
} }
} }

View File

@ -3,12 +3,10 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.spatial.Box import hep.dataforge.vis.spatial.Box
import hep.dataforge.vis.spatial.detail import hep.dataforge.vis.spatial.detail
import info.laht.threekt.geometries.BoxBufferGeometry import info.laht.threekt.geometries.BoxBufferGeometry
import kotlin.math.pow
object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) { object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
override fun buildGeometry(obj: Box) = override fun buildGeometry(obj: Box) =
obj.detail?.let { obj.detail?.let { detail ->
val segments = it.toDouble().pow(1.0 / 3.0).toInt() BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail)
BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, segments, segments, segments)
} ?: BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize) } ?: BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize)
} }

View File

@ -36,6 +36,7 @@ interface ThreeFactory<T : VisualObject3D> {
* Update position, rotation and visibility * Update position, rotation and visibility
*/ */
internal fun Object3D.updatePosition(obj: VisualObject3D) { internal fun Object3D.updatePosition(obj: VisualObject3D) {
visible = obj.visible ?: true
position.set(obj.x, obj.y, obj.z) position.set(obj.x, obj.y, obj.z)
setRotationFromEuler(obj.euler) setRotationFromEuler(obj.euler)
scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) scale.set(obj.scaleX, obj.scaleY, obj.scaleZ)
@ -125,7 +126,7 @@ abstract class MeshThreeFactory<T : VisualObject3D>(override val type: KClass<ou
//update position of mesh using this object //update position of mesh using this object
mesh.updatePosition(obj) mesh.updatePosition(obj)
} else if (name == VisualObject3D.VISIBLE_KEY) { } else if (name == VisualObject3D.VISIBLE_KEY) {
obj.visible = obj.visible ?: true mesh.visible = obj.visible ?: true
} else { } else {
//full update //full update
mesh.geometry = geometryBuilder(obj) mesh.geometry = geometryBuilder(obj)

View File

@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.spatial.Proxy3D import hep.dataforge.vis.spatial.Proxy3D
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
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
@ -16,8 +15,8 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy3D> {
three.buildObject3D(obj.template) as Mesh three.buildObject3D(obj.template) as Mesh
} }
val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material) //val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material)
//val mesh = templateMesh.clone() val mesh = templateMesh.clone()
mesh.updatePosition(obj) mesh.updatePosition(obj)
return mesh return mesh

View File

@ -4,20 +4,18 @@ import hep.dataforge.vis.spatial.Sphere
import hep.dataforge.vis.spatial.detail import hep.dataforge.vis.spatial.detail
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.SphereBufferGeometry import info.laht.threekt.geometries.SphereBufferGeometry
import kotlin.math.pow
object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) { object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) {
override fun buildGeometry(obj: Sphere): BufferGeometry { override fun buildGeometry(obj: Sphere): BufferGeometry {
return obj.detail?.let { return obj.detail?.let {detail ->
val segments = it.toDouble().pow(0.5).toInt()
SphereBufferGeometry( SphereBufferGeometry(
radius = obj.radius, radius = obj.radius,
phiStart = obj.phiStart, phiStart = obj.phiStart,
phiLength = obj.phi, phiLength = obj.phi,
thetaStart = obj.thetaStart, thetaStart = obj.thetaStart,
thetaLength = obj.theta, thetaLength = obj.theta,
widthSegments = segments, widthSegments = detail,
heightSegments = segments heightSegments = detail
) )
}?: SphereBufferGeometry( }?: SphereBufferGeometry(
radius = obj.radius, radius = obj.radius,

View File

@ -7,9 +7,9 @@ class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize
//TODO add helper for color configuration //TODO add helper for color configuration
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val dx = xSize.toDouble() / 2 val dx = xSize.toFloat() / 2
val dy = ySize.toDouble() / 2 val dy = ySize.toFloat() / 2
val dz = zSize.toDouble() / 2 val dz = zSize.toFloat() / 2
val node1 = Point3D(-dx, -dy, -dz) val node1 = Point3D(-dx, -dy, -dz)
val node2 = Point3D(dx, -dy, -dz) val node2 = Point3D(dx, -dy, -dz)
val node3 = Point3D(dx, dy, -dz) val node3 = Point3D(dx, dy, -dz)

View File

@ -1,17 +1,19 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.number
import kotlin.math.PI import kotlin.math.PI
/** /**
* A cylinder or cut cone segment * A cylinder or cut cone segment
*/ */
class Cylinder(parent: VisualObject?, var radius: Number, var height: Number) : VisualLeaf3D(parent) { class Cylinder(
var upperRadius by number(radius) parent: VisualObject?,
var startAngle by number(0.0) var radius: Number,
var angle by number(2 * PI) var height: Number,
} var upperRadius: Number = radius,
var startAngle: Number = 0f,
var angle: Number = 2 * PI
) : VisualLeaf3D(parent)
fun VisualGroup3D.cylinder( fun VisualGroup3D.cylinder(
r: Number, r: Number,

View File

@ -46,19 +46,6 @@ class Extruded(parent: VisualObject?) : VisualLeaf3D(parent), Shape {
//TODO send invalidation signal //TODO send invalidation signal
} }
private fun <T : Any> GeometryBuilder<T>.cap(shape: List<Point3D>) {
//FIXME won't work for non-convex shapes
val center = Point3D(
shape.map { it.x.toDouble() }.average(),
shape.map { it.y.toDouble() }.average(),
shape.map { it.z.toDouble() }.average()
)
for(i in 0 until (shape.size - 1)){
face(shape[i], shape[i+1], center, null)
}
face(shape.last(), shape.first(),center,null)
}
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val shape: Shape2D = shape val shape: Shape2D = shape

View File

@ -8,8 +8,8 @@ data class Point2D(val x: Number, val y: Number) : MetaRepr {
"y" to y "y" to y
} }
companion object{ companion object {
fun from(meta: Meta): Point2D{ fun from(meta: Meta): Point2D {
return Point2D(meta["x"].number ?: 0, meta["y"].number ?: 0) return Point2D(meta["x"].number ?: 0, meta["y"].number ?: 0)
} }
} }
@ -22,12 +22,12 @@ data class Point3D(val x: Number, val y: Number, val z: Number) : MetaRepr {
"z" to z "z" to z
} }
companion object{ companion object {
fun from(meta: Meta): Point3D{ fun from(meta: Meta): Point3D {
return Point3D(meta["x"].number ?: 0, meta["y"].number ?: 0, meta["y"].number ?: 0) return Point3D(meta["x"].number ?: 0, meta["y"].number ?: 0, meta["y"].number ?: 0)
} }
val zero = Point3D(0,0,0) val zero = Point3D(0, 0, 0)
} }
} }
@ -42,7 +42,7 @@ interface GeometryBuilder<T : Any> {
* @param normal optional external normal to the face * @param normal optional external normal to the face
* @param meta optional additional platform-specific parameters like color or texture index * @param meta optional additional platform-specific parameters like color or texture index
*/ */
fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta = EmptyMeta) fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = EmptyMeta)
fun build(): T fun build(): T
} }
@ -62,3 +62,16 @@ fun GeometryBuilder<*>.face4(
interface Shape : VisualObject3D { interface Shape : VisualObject3D {
fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>)
} }
fun <T : Any> GeometryBuilder<T>.cap(shape: List<Point3D>, normal: Point3D? = null) {
//FIXME won't work for non-convex shapes
val center = Point3D(
shape.map { it.x.toDouble() }.average(),
shape.map { it.y.toDouble() }.average(),
shape.map { it.z.toDouble() }.average()
)
for (i in 0 until (shape.size - 1)) {
face(shape[i], shape[i + 1], center, normal)
}
face(shape.last(), shape.first(), center, normal)
}

View File

@ -0,0 +1,131 @@
package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.VisualObject
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
/**
* Stright tube segment
*/
class Tube(
parent: VisualObject?,
var radius: Float,
var height: Float,
var innerRadius: Float = 0f,
var startAngle: Float = 0f,
var angle: Float = PI2
) : VisualLeaf3D(parent), Shape {
init {
require(radius > 0)
require(height > 0)
require(innerRadius >= 0)
require(startAngle >= 0)
require(angle in (0f..(PI2)))
}
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val segments = detail ?: 8
require(segments >= 4) { "The number of segments in tube is too small" }
val angleStep = angle / (segments - 1)
fun shape(r: Float, z: Float): List<Point3D> {
return (0 until segments).map { i ->
Point3D(r * cos(startAngle + angleStep * i), r * sin(startAngle + angleStep * i), z)
}
}
geometryBuilder.apply {
//creating shape in x-y plane with z = 0
val bottomOuterPoints = shape(radius, 0f)
val upperOuterPoints = shape(radius, height)
//outer face
(1 until segments).forEach {
face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], upperOuterPoints[it], upperOuterPoints[it - 1])
}
if (angle == PI2) {
face4(bottomOuterPoints.last(), bottomOuterPoints[0], upperOuterPoints[0], upperOuterPoints.last())
}
if (innerRadius == 0f) {
val zeroBottom = Point3D(0f, 0f, 0f)
val zeroTop = Point3D(0f, 0f, height)
(1 until segments).forEach {
face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it])
face(upperOuterPoints[it - 1], upperOuterPoints[it], zeroTop)
}
if (angle == PI2) {
face(bottomOuterPoints.last(), zeroBottom, bottomOuterPoints[0])
face(upperOuterPoints.last(), upperOuterPoints[0], zeroTop)
} else {
face4(zeroTop, zeroBottom, bottomOuterPoints[0], upperOuterPoints[0])
face4(zeroTop, zeroBottom, bottomOuterPoints.last(), upperOuterPoints.last())
}
} else {
val bottomInnerPoints = shape(innerRadius, 0f)
val upperInnerPoints = shape(innerRadius, height)
//outer face
(1 until segments).forEach {
// inner surface
face4(
bottomInnerPoints[it],
bottomInnerPoints[it - 1],
upperInnerPoints[it - 1],
upperInnerPoints[it]
)
//bottom cup
face4(
bottomInnerPoints[it - 1],
bottomInnerPoints[it],
bottomOuterPoints[it],
bottomOuterPoints[it - 1]
)
//upper cup
face4(
upperInnerPoints[it],
upperInnerPoints[it - 1],
upperOuterPoints[it - 1],
upperOuterPoints[it]
)
}
if (angle == PI2) {
face4(bottomInnerPoints[0], bottomInnerPoints.last(), upperInnerPoints.last(), upperInnerPoints[0])
face4(
bottomInnerPoints.last(),
bottomInnerPoints[0],
bottomOuterPoints[0],
bottomOuterPoints.last()
)
face4(upperInnerPoints[0], upperInnerPoints.last(), upperOuterPoints.last(), upperOuterPoints[0])
} else{
face4(bottomInnerPoints[0],bottomOuterPoints[0],upperOuterPoints[0],upperInnerPoints[0])
face4(bottomOuterPoints.last(),bottomInnerPoints.last(),upperInnerPoints.last(),upperOuterPoints.last())
}
}
}
}
}
fun VisualGroup3D.tube(
r: Number,
height: Number,
innerRadius: Number = 0f,
startAngle: Number = 0f,
angle: Number = 2 * PI,
name: String? = null,
block: Tube.() -> Unit = {}
): Tube {
val tube = Tube(
this,
r.toFloat(),
height.toFloat(),
innerRadius.toFloat(),
startAngle.toFloat(),
angle.toFloat()
).apply(
block
)
return tube.also { set(name, it) }
}

View File

@ -105,10 +105,10 @@ var VisualObject3D.rotationOrder: RotationOrder
/** /**
* Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited
*/ */
var VisualObject3D.detail: Int? var VisualObject3D.detail: Int?
get() = getProperty(DETAIL_KEY).int get() = getProperty(DETAIL_KEY,false).int
set(value) = setProperty(DETAIL_KEY, value) set(value) = setProperty(DETAIL_KEY, value)
var VisualObject3D.material: Meta? var VisualObject3D.material: Meta?

View File

@ -1,5 +1,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import kotlin.math.PI
object World { object World {
const val CAMERA_INITIAL_DISTANCE = -500.0 const val CAMERA_INITIAL_DISTANCE = -500.0
const val CAMERA_INITIAL_X_ANGLE = -50.0 const val CAMERA_INITIAL_X_ANGLE = -50.0
@ -8,3 +10,5 @@ object World {
const val CAMERA_NEAR_CLIP = 0.1 const val CAMERA_NEAR_CLIP = 0.1
const val CAMERA_FAR_CLIP = 10000.0 const val CAMERA_FAR_CLIP = 10000.0
} }
const val PI2: Float = 2 * PI.toFloat()