forked from kscience/visionforge
Added manual tube. Fixed some bugs.
This commit is contained in:
parent
cf5a4fd7f6
commit
073ae8a353
@ -81,7 +81,11 @@ abstract class AbstractVisualObject(override val parent: VisualObject?) : Visual
|
||||
}
|
||||
|
||||
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?) {
|
||||
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 val listeners = HashSet<Listener<T>>()
|
||||
@ -164,10 +175,12 @@ open class VisualGroup<T : VisualObject>(parent: VisualObject?) : AbstractVisual
|
||||
* Get named child by name
|
||||
*/
|
||||
operator fun get(name: Name): T? = namedChildren[name]
|
||||
|
||||
/**
|
||||
* Get named child by string
|
||||
*/
|
||||
operator fun get(key: String): T? = namedChildren[key]
|
||||
|
||||
/**
|
||||
* Get an unnamed child
|
||||
*/
|
||||
|
@ -33,7 +33,7 @@ class RendererDemoView : View() {
|
||||
}
|
||||
}
|
||||
|
||||
var color by group.config.number(1530).int
|
||||
var color by group.config.number(1530)
|
||||
|
||||
GlobalScope.launch {
|
||||
val random = Random(111)
|
||||
|
@ -45,10 +45,14 @@ private fun VisualGroup3D.addSolid(
|
||||
val aScale = solid.ascale()
|
||||
return when (solid) {
|
||||
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
|
||||
is GDMLTube -> cylinder(solid.rmax * lScale, solid.z * lScale, name) {
|
||||
startAngle = solid.startphi * aScale
|
||||
angle = solid.deltaphi * aScale
|
||||
}
|
||||
is GDMLTube -> tube(
|
||||
solid.rmax * lScale,
|
||||
solid.z * lScale,
|
||||
solid.rmin * lScale,
|
||||
solid.startphi * aScale,
|
||||
solid.deltaphi * aScale,
|
||||
name
|
||||
)
|
||||
is GDMLXtru -> extrude(name) {
|
||||
shape {
|
||||
solid.vertices.forEach {
|
||||
@ -193,8 +197,7 @@ private fun volume(
|
||||
?: context.templates.addSolid(context, solid, solid.name) {
|
||||
this.material = context.resolveColor(group, material, solid)
|
||||
}
|
||||
val wrapper = Proxy3D(this,cachedSolid)
|
||||
add(wrapper)
|
||||
proxy(cachedSolid, solid.name)
|
||||
}
|
||||
|
||||
when (val vol = group.placement) {
|
||||
|
@ -90,11 +90,11 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
launch { message("Converting GDML into DF-VIS format") }
|
||||
val visual = gdml.toVisual {
|
||||
lUnit = LUnit.CM
|
||||
// acceptSolid = { solid ->
|
||||
// !solid.name.startsWith("ecal")
|
||||
acceptSolid = { solid ->
|
||||
!solid.name.startsWith("ecal")
|
||||
// && !solid.name.startsWith("V")
|
||||
// && !solid.name.startsWith("U")
|
||||
// }
|
||||
}
|
||||
}
|
||||
launch { message("Rendering") }
|
||||
val output = three.output(canvas)
|
||||
|
@ -31,7 +31,12 @@ private class ThreeDemoApp : ApplicationBase() {
|
||||
}
|
||||
sphere(50.0) {
|
||||
x = 110
|
||||
detail = 200
|
||||
detail = 16
|
||||
}
|
||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||
y = 110
|
||||
detail = 16
|
||||
rotationX = PI/4
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,10 @@ package hep.dataforge.vis.spatial.three
|
||||
import hep.dataforge.vis.spatial.Box
|
||||
import hep.dataforge.vis.spatial.detail
|
||||
import info.laht.threekt.geometries.BoxBufferGeometry
|
||||
import kotlin.math.pow
|
||||
|
||||
object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
|
||||
override fun buildGeometry(obj: Box) =
|
||||
obj.detail?.let {
|
||||
val segments = it.toDouble().pow(1.0 / 3.0).toInt()
|
||||
BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, segments, segments, segments)
|
||||
obj.detail?.let { detail ->
|
||||
BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail)
|
||||
} ?: BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize)
|
||||
}
|
@ -36,6 +36,7 @@ interface ThreeFactory<T : VisualObject3D> {
|
||||
* Update position, rotation and visibility
|
||||
*/
|
||||
internal fun Object3D.updatePosition(obj: VisualObject3D) {
|
||||
visible = obj.visible ?: true
|
||||
position.set(obj.x, obj.y, obj.z)
|
||||
setRotationFromEuler(obj.euler)
|
||||
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
|
||||
mesh.updatePosition(obj)
|
||||
} else if (name == VisualObject3D.VISIBLE_KEY) {
|
||||
obj.visible = obj.visible ?: true
|
||||
mesh.visible = obj.visible ?: true
|
||||
} else {
|
||||
//full update
|
||||
mesh.geometry = geometryBuilder(obj)
|
||||
|
@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial.three
|
||||
|
||||
import hep.dataforge.vis.spatial.Proxy3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.objects.Mesh
|
||||
|
||||
@ -16,8 +15,8 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy3D> {
|
||||
three.buildObject3D(obj.template) as Mesh
|
||||
}
|
||||
|
||||
val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material)
|
||||
//val mesh = templateMesh.clone()
|
||||
//val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material)
|
||||
val mesh = templateMesh.clone()
|
||||
|
||||
mesh.updatePosition(obj)
|
||||
return mesh
|
||||
|
@ -4,20 +4,18 @@ import hep.dataforge.vis.spatial.Sphere
|
||||
import hep.dataforge.vis.spatial.detail
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.geometries.SphereBufferGeometry
|
||||
import kotlin.math.pow
|
||||
|
||||
object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) {
|
||||
override fun buildGeometry(obj: Sphere): BufferGeometry {
|
||||
return obj.detail?.let {
|
||||
val segments = it.toDouble().pow(0.5).toInt()
|
||||
return obj.detail?.let {detail ->
|
||||
SphereBufferGeometry(
|
||||
radius = obj.radius,
|
||||
phiStart = obj.phiStart,
|
||||
phiLength = obj.phi,
|
||||
thetaStart = obj.thetaStart,
|
||||
thetaLength = obj.theta,
|
||||
widthSegments = segments,
|
||||
heightSegments = segments
|
||||
widthSegments = detail,
|
||||
heightSegments = detail
|
||||
)
|
||||
}?: SphereBufferGeometry(
|
||||
radius = obj.radius,
|
||||
|
@ -7,9 +7,9 @@ class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize
|
||||
|
||||
//TODO add helper for color configuration
|
||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||
val dx = xSize.toDouble() / 2
|
||||
val dy = ySize.toDouble() / 2
|
||||
val dz = zSize.toDouble() / 2
|
||||
val dx = xSize.toFloat() / 2
|
||||
val dy = ySize.toFloat() / 2
|
||||
val dz = zSize.toFloat() / 2
|
||||
val node1 = Point3D(-dx, -dy, -dz)
|
||||
val node2 = Point3D(dx, -dy, -dz)
|
||||
val node3 = Point3D(dx, dy, -dz)
|
||||
|
@ -1,17 +1,19 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.number
|
||||
import kotlin.math.PI
|
||||
|
||||
/**
|
||||
* A cylinder or cut cone segment
|
||||
*/
|
||||
class Cylinder(parent: VisualObject?, var radius: Number, var height: Number) : VisualLeaf3D(parent) {
|
||||
var upperRadius by number(radius)
|
||||
var startAngle by number(0.0)
|
||||
var angle by number(2 * PI)
|
||||
}
|
||||
class Cylinder(
|
||||
parent: VisualObject?,
|
||||
var radius: Number,
|
||||
var height: Number,
|
||||
var upperRadius: Number = radius,
|
||||
var startAngle: Number = 0f,
|
||||
var angle: Number = 2 * PI
|
||||
) : VisualLeaf3D(parent)
|
||||
|
||||
fun VisualGroup3D.cylinder(
|
||||
r: Number,
|
||||
|
@ -46,19 +46,6 @@ class Extruded(parent: VisualObject?) : VisualLeaf3D(parent), Shape {
|
||||
//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>) {
|
||||
val shape: Shape2D = shape
|
||||
|
||||
|
@ -8,8 +8,8 @@ data class Point2D(val x: Number, val y: Number) : MetaRepr {
|
||||
"y" to y
|
||||
}
|
||||
|
||||
companion object{
|
||||
fun from(meta: Meta): Point2D{
|
||||
companion object {
|
||||
fun from(meta: Meta): Point2D {
|
||||
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
|
||||
}
|
||||
|
||||
companion object{
|
||||
fun from(meta: Meta): Point3D{
|
||||
companion object {
|
||||
fun from(meta: Meta): Point3D {
|
||||
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 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
|
||||
}
|
||||
@ -61,4 +61,17 @@ fun GeometryBuilder<*>.face4(
|
||||
|
||||
interface Shape : VisualObject3D {
|
||||
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)
|
||||
}
|
@ -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) }
|
||||
}
|
@ -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?
|
||||
get() = getProperty(DETAIL_KEY).int
|
||||
get() = getProperty(DETAIL_KEY,false).int
|
||||
set(value) = setProperty(DETAIL_KEY, value)
|
||||
|
||||
var VisualObject3D.material: Meta?
|
||||
|
@ -1,5 +1,7 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import kotlin.math.PI
|
||||
|
||||
object World {
|
||||
const val CAMERA_INITIAL_DISTANCE = -500.0
|
||||
const val CAMERA_INITIAL_X_ANGLE = -50.0
|
||||
@ -7,4 +9,6 @@ object World {
|
||||
const val CAMERA_INITIAL_Z_ANGLE = -210.0
|
||||
const val CAMERA_NEAR_CLIP = 0.1
|
||||
const val CAMERA_FAR_CLIP = 10000.0
|
||||
}
|
||||
}
|
||||
|
||||
const val PI2: Float = 2 * PI.toFloat()
|
Loading…
Reference in New Issue
Block a user