GDML demo renders something
This commit is contained in:
parent
d3500c3a57
commit
e31ad5ece1
@ -0,0 +1,76 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import scientifik.gdml.GDMLPosition
|
||||
import scientifik.gdml.GDMLRotation
|
||||
import scientifik.gdml.GDMLSolid
|
||||
import kotlin.math.PI
|
||||
|
||||
enum class LUnit(val value: Double) {
|
||||
MM(1.0),
|
||||
CM(10.0),
|
||||
M(1000.0)
|
||||
}
|
||||
|
||||
enum class AUnit(val value: Double) {
|
||||
DEG(PI / 180),
|
||||
RAD(1.0),
|
||||
RADIAN(1.0)
|
||||
}
|
||||
|
||||
fun GDMLPosition.unit(): LUnit = LUnit.valueOf(unit.toUpperCase())
|
||||
|
||||
fun GDMLPosition.x(unit: LUnit): Double = if (unit.name == this.unit) {
|
||||
x.toDouble()
|
||||
} else {
|
||||
x.toDouble() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLPosition.y(unit: LUnit): Double = if (unit.name == this.unit) {
|
||||
y.toDouble()
|
||||
} else {
|
||||
y.toDouble() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLPosition.z(unit: LUnit): Double = if (unit.name == this.unit) {
|
||||
z.toDouble()
|
||||
} else {
|
||||
z.toDouble() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLRotation.unit(): AUnit = AUnit.valueOf(unit.toUpperCase())
|
||||
|
||||
fun GDMLRotation.x(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) {
|
||||
x.toDouble()
|
||||
} else {
|
||||
x.toDouble() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLRotation.y(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) {
|
||||
y.toDouble()
|
||||
} else {
|
||||
y.toDouble() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLRotation.z(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) {
|
||||
z.toDouble()
|
||||
} else {
|
||||
z.toDouble() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLSolid.lscale(unit: LUnit): Double {
|
||||
val solidUnit = lunit?.let { LUnit.valueOf(it.toUpperCase()) } ?: return 1.0
|
||||
return if (solidUnit == unit) {
|
||||
1.0
|
||||
} else {
|
||||
solidUnit.value / unit.value
|
||||
}
|
||||
}
|
||||
|
||||
fun GDMLSolid.ascale(unit: AUnit = AUnit.RAD): Double {
|
||||
val solidUnit = aunit?.let { AUnit.valueOf(it.toUpperCase()) } ?: return 1.0
|
||||
return if (solidUnit == unit) {
|
||||
1.0
|
||||
} else {
|
||||
solidUnit.value / unit.value
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.color
|
||||
@ -9,66 +9,64 @@ import hep.dataforge.vis.spatial.*
|
||||
import scientifik.gdml.*
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
private fun VisualObject.withPosition(
|
||||
lUnit: LUnit,
|
||||
pos: GDMLPosition? = null,
|
||||
rotation: GDMLRotation? = null,
|
||||
scale: GDMLScale? = null
|
||||
): VisualObject =
|
||||
apply {
|
||||
// if( this is VisualObject3D){
|
||||
// pos?.let {
|
||||
// x = pos.x
|
||||
// y = pos.y
|
||||
// z = pos.z
|
||||
// }
|
||||
// rotation?.let {
|
||||
// rotationX = rotation.x
|
||||
// rotationY = rotation.y
|
||||
// rotationZ = rotation.z
|
||||
// }
|
||||
// } else {
|
||||
pos?.let {
|
||||
x = pos.x
|
||||
y = pos.y
|
||||
z = pos.z
|
||||
}
|
||||
rotation?.let {
|
||||
rotationX = rotation.x
|
||||
rotationY = rotation.y
|
||||
rotationZ = rotation.z
|
||||
}
|
||||
//}
|
||||
scale?.let {
|
||||
scaleX = scale.x
|
||||
scaleY = scale.y
|
||||
scaleZ = scale.z
|
||||
}
|
||||
//TODO convert units if needed
|
||||
): VisualObject = apply {
|
||||
pos?.let {
|
||||
x = pos.x(lUnit)
|
||||
y = pos.y(lUnit)
|
||||
z = pos.z(lUnit)
|
||||
}
|
||||
rotation?.let {
|
||||
rotationX = rotation.x()
|
||||
rotationY = rotation.y()
|
||||
rotationZ = rotation.z()
|
||||
}
|
||||
scale?.let {
|
||||
scaleX = scale.x
|
||||
scaleY = scale.y
|
||||
scaleZ = scale.z
|
||||
}
|
||||
//TODO convert units if needed
|
||||
}
|
||||
|
||||
private inline operator fun Number.times(d: Double) = toDouble() * d
|
||||
|
||||
|
||||
private fun VisualGroup.addSolid(
|
||||
root: GDML,
|
||||
solid: GDMLSolid,
|
||||
lUnit: LUnit,
|
||||
name: String? = null,
|
||||
block: VisualObject.() -> Unit = {}
|
||||
): VisualObject {
|
||||
val lScale = solid.lscale(lUnit)
|
||||
val aScale = solid.ascale()
|
||||
return when (solid) {
|
||||
is GDMLBox -> box(solid.x, solid.y, solid.z, name)
|
||||
is GDMLTube -> cylinder(solid.rmax, solid.z, name) {
|
||||
startAngle = solid.startphi
|
||||
angle = solid.deltaphi
|
||||
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 GDMLXtru -> extrude(name) {
|
||||
shape {
|
||||
solid.vertices.forEach {
|
||||
point(it.x, it.y)
|
||||
point(it.x * lScale, it.y * lScale)
|
||||
}
|
||||
}
|
||||
solid.sections.sortedBy { it.zOrder }.forEach { section ->
|
||||
layer(section.zPosition, section.xOffset, section.yOffset, section.scalingFactor)
|
||||
layer(
|
||||
section.zPosition * lScale,
|
||||
section.xOffset * lScale,
|
||||
section.yOffset * lScale,
|
||||
section.scalingFactor
|
||||
)
|
||||
}
|
||||
}
|
||||
is GDMLScaledSolid -> {
|
||||
@ -76,31 +74,31 @@ private fun VisualGroup.addSolid(
|
||||
val innerSolid = solid.solidref.resolve(root)
|
||||
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
|
||||
|
||||
addSolid(root, innerSolid) {
|
||||
addSolid(root, innerSolid, lUnit) {
|
||||
block()
|
||||
scaleX = scaleX.toDouble() * solid.scale.x.toDouble()
|
||||
scaleY = scaleY.toDouble() * solid.scale.y.toDouble()
|
||||
scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble()
|
||||
}
|
||||
}
|
||||
is GDMLSphere -> sphere(solid.rmax, solid.deltaphi, solid.deltatheta, name) {
|
||||
phiStart = solid.startphi.toDouble()
|
||||
thetaStart = solid.starttheta.toDouble()
|
||||
is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) {
|
||||
phiStart = solid.startphi * aScale
|
||||
thetaStart = solid.starttheta * aScale
|
||||
}
|
||||
is GDMLOrb -> sphere(solid.r, name = name)
|
||||
is GDMLOrb -> sphere(solid.r * lScale, name = name)
|
||||
is GDMLPolyhedra -> extrude(name) {
|
||||
//getting the radius of first
|
||||
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
|
||||
val baseRadius = solid.planes.first().rmax.toDouble()
|
||||
val baseRadius = solid.planes.first().rmax * lScale
|
||||
shape {
|
||||
(0..solid.numsides).forEach {
|
||||
val phi = solid.deltaphi.toDouble() / solid.numsides * it + solid.startphi.toDouble()
|
||||
baseRadius * cos(phi) to baseRadius * sin(phi)
|
||||
val phi = solid.deltaphi * aScale / solid.numsides * it + solid.startphi * aScale
|
||||
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
||||
}
|
||||
}
|
||||
solid.planes.forEach { plane ->
|
||||
//scaling all radii relative to first layer radius
|
||||
layer(plane.z, scale = plane.rmax.toDouble() / baseRadius)
|
||||
layer(plane.z * lScale, scale = plane.rmax * lScale / baseRadius)
|
||||
}
|
||||
}
|
||||
is GDMLBoolSolid -> {
|
||||
@ -113,19 +111,59 @@ private fun VisualGroup.addSolid(
|
||||
}
|
||||
|
||||
return composite(type, name) {
|
||||
addSolid(root, first) {
|
||||
withPosition(solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null)
|
||||
addSolid(root, first, lUnit) {
|
||||
withPosition(lUnit, solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null)
|
||||
}
|
||||
addSolid(root, second, lUnit) {
|
||||
withPosition(lUnit, solid.resolvePosition(root), solid.resolveRotation(root), null)
|
||||
}
|
||||
addSolid(root, second)
|
||||
withPosition(solid.resolvePosition(root), solid.resolveRotation(root), null)
|
||||
}
|
||||
}
|
||||
}.apply(block)
|
||||
}
|
||||
|
||||
private fun VisualGroup.addPhysicalVolume(
|
||||
root: GDML,
|
||||
physVolume: GDMLPhysVolume,
|
||||
lUnit: LUnit,
|
||||
resolveColor: GDMLMaterial.() -> Meta
|
||||
) {
|
||||
val volume: GDMLGroup = physVolume.volumeref.resolve(root)
|
||||
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
|
||||
|
||||
addVolume(
|
||||
root,
|
||||
volume,
|
||||
lUnit,
|
||||
physVolume.resolvePosition(root),
|
||||
physVolume.resolveRotation(root),
|
||||
physVolume.resolveScale(root),
|
||||
resolveColor
|
||||
)
|
||||
}
|
||||
|
||||
private fun VisualGroup.addDivisionVolume(
|
||||
root: GDML,
|
||||
divisionVolume: GDMLDivisionVolume,
|
||||
lUnit: LUnit,
|
||||
resolveColor: GDMLMaterial.() -> Meta
|
||||
) {
|
||||
val volume: GDMLGroup = divisionVolume.volumeref.resolve(root)
|
||||
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
|
||||
|
||||
//TODO add divisions
|
||||
addVolume(
|
||||
root,
|
||||
volume,
|
||||
lUnit,
|
||||
resolveColor = resolveColor
|
||||
)
|
||||
}
|
||||
|
||||
private fun VisualGroup.addVolume(
|
||||
root: GDML,
|
||||
group: GDMLGroup,
|
||||
lUnit: LUnit,
|
||||
position: GDMLPosition? = null,
|
||||
rotation: GDMLRotation? = null,
|
||||
scale: GDMLScale? = null,
|
||||
@ -133,40 +171,37 @@ private fun VisualGroup.addVolume(
|
||||
) {
|
||||
|
||||
group(group.name) {
|
||||
withPosition(position, rotation, scale)
|
||||
withPosition(lUnit, position, rotation, scale)
|
||||
|
||||
if (group is GDMLVolume) {
|
||||
val solid = group.solidref.resolve(root)
|
||||
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
|
||||
val material = group.materialref.resolve(root)
|
||||
?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined")
|
||||
val material = group.materialref.resolve(root) ?: GDMLElement(group.materialref.ref)
|
||||
//?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined")
|
||||
|
||||
addSolid(root, solid, solid.name) {
|
||||
addSolid(root, solid, lUnit, solid.name) {
|
||||
color(material.resolveColor())
|
||||
}
|
||||
//TODO render placements
|
||||
|
||||
when (val vol = group.placement) {
|
||||
is GDMLPhysVolume -> addPhysicalVolume(root, vol, lUnit, resolveColor)
|
||||
is GDMLDivisionVolume -> addDivisionVolume(root, vol, lUnit, resolveColor)
|
||||
}
|
||||
}
|
||||
|
||||
group.physVolumes.forEach { physVolume ->
|
||||
val volume: GDMLGroup = physVolume.volumeref.resolve(root)
|
||||
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
|
||||
|
||||
addVolume(
|
||||
root,
|
||||
volume,
|
||||
physVolume.resolvePosition(root),
|
||||
physVolume.resolveRotation(root),
|
||||
physVolume.resolveScale(root),
|
||||
resolveColor
|
||||
)
|
||||
addPhysicalVolume(root, physVolume, lUnit, resolveColor)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun GDML.toVisual(lUnit: LUnit = LUnit.MM): VisualGroup {
|
||||
val cache = HashMap<GDMLMaterial, Meta>()
|
||||
val random = Random(111)
|
||||
|
||||
fun GDML.toVisual(): VisualGroup {
|
||||
//TODO add materials cache
|
||||
fun GDMLMaterial.color(): Meta = EmptyMeta
|
||||
return VisualGroup().also { it.addVolume(this, world) { color() } }
|
||||
fun GDMLMaterial.color(): Meta = cache.getOrPut(this) {
|
||||
buildMeta { "color" to random.nextInt(0, Int.MAX_VALUE) }
|
||||
}
|
||||
|
||||
return VisualGroup().also { it.addVolume(this, world, lUnit) { color() } }
|
||||
}
|
@ -3,9 +3,11 @@ package hep.dataforge.vis.spatial.gdml.demo
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.vis.hmr.ApplicationBase
|
||||
import hep.dataforge.vis.hmr.startApplication
|
||||
import hep.dataforge.vis.spatial.gdml.LUnit
|
||||
import hep.dataforge.vis.spatial.gdml.toVisual
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
@ -32,7 +34,7 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
/**
|
||||
* Load data from text file
|
||||
*/
|
||||
private fun loadData(event: Event, block: suspend (String) -> Unit) {
|
||||
private fun loadData(event: Event, block: suspend CoroutineScope.(String) -> Unit) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
|
||||
@ -51,6 +53,25 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun spinner(show: Boolean) {
|
||||
val style = if (show) {
|
||||
"display:block;"
|
||||
} else {
|
||||
"display:none;"
|
||||
}
|
||||
document.getElementById("loader")?.setAttribute("style", style)
|
||||
}
|
||||
|
||||
private fun message(message: String?) {
|
||||
val element = document.getElementById("message")
|
||||
if (message == null) {
|
||||
element?.setAttribute("style", "display:none;")
|
||||
} else {
|
||||
element?.textContent = message
|
||||
element?.setAttribute("style", "display:block;")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
@ -59,13 +80,22 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
||||
|
||||
val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page")
|
||||
canvas.clear()
|
||||
|
||||
val action: suspend (String) -> Unit = {
|
||||
val action: suspend CoroutineScope.(String) -> Unit = {
|
||||
canvas.clear()
|
||||
val output = three.output(canvas)
|
||||
launch { spinner(true) }
|
||||
launch { message("Loading GDML") }
|
||||
val gdml = GDML.format.parse(GDML.serializer(), it)
|
||||
val visual = gdml.toVisual()
|
||||
launch { message("Converting GDML into DF-VIS format") }
|
||||
val visual = gdml.toVisual(LUnit.CM)
|
||||
launch { message("Rendering") }
|
||||
val output = three.output(canvas)
|
||||
output.render(visual)
|
||||
launch {
|
||||
message(null)
|
||||
spinner(false)
|
||||
}
|
||||
}
|
||||
|
||||
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
|
||||
|
@ -6,6 +6,7 @@
|
||||
<title>Three js demo for particle physics</title>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="main.css">
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
</head>
|
||||
<body class="testApp">
|
||||
@ -18,6 +19,8 @@
|
||||
<div class="container">
|
||||
<h1>GDML demo</h1>
|
||||
</div>
|
||||
<div class="container loader" id="loader" style="display:none;"></div>
|
||||
<div class="container animate-bottom" id="message" style="display:none;"></div>
|
||||
<div class="container" id="canvas"></div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||
|
13
dataforge-vis-spatial-gdml/src/jsMain/web/main.css
Normal file
13
dataforge-vis-spatial-gdml/src/jsMain/web/main.css
Normal file
@ -0,0 +1,13 @@
|
||||
.loader {
|
||||
border: 16px solid #f3f3f3; /* Light grey */
|
||||
border-top: 16px solid #3498db; /* Blue */
|
||||
border-radius: 50%;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
@ -4,6 +4,7 @@ import hep.dataforge.meta.*
|
||||
import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import info.laht.threekt.materials.Material
|
||||
import info.laht.threekt.materials.MeshBasicMaterial
|
||||
import info.laht.threekt.materials.MeshPhongMaterial
|
||||
import info.laht.threekt.math.Color
|
||||
|
||||
@ -39,19 +40,20 @@ fun MetaItem<*>.color(): Color {
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer FX material based on meta item
|
||||
* Infer Three material based on meta item
|
||||
*/
|
||||
fun MetaItem<*>?.material(): Material {
|
||||
return when (this) {
|
||||
null -> Materials.DEFAULT
|
||||
is MetaItem.ValueItem -> MeshPhongMaterial().apply {
|
||||
is MetaItem.ValueItem -> MeshBasicMaterial().apply {
|
||||
color = this@material.color()
|
||||
}
|
||||
is MetaItem.NodeItem -> MeshPhongMaterial().apply {
|
||||
is MetaItem.NodeItem -> MeshBasicMaterial().apply {
|
||||
(node["color"] ?: this@material).let { color = it.color() }
|
||||
opacity = node["opacity"]?.double ?: 1.0
|
||||
transparent = node["transparent"].boolean ?: (opacity < 1.0)
|
||||
node["specularColor"]?.let { specular = it.color() }
|
||||
//node["specularColor"]?.let { specular = it.color() }
|
||||
side = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,19 @@ import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.vis.spatial.GeometryBuilder
|
||||
import hep.dataforge.vis.spatial.Point2D
|
||||
import hep.dataforge.vis.spatial.Point3D
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Face3
|
||||
import info.laht.threekt.core.Geometry
|
||||
import info.laht.threekt.math.Vector2
|
||||
import info.laht.threekt.math.Vector3
|
||||
|
||||
// TODO use unsafe cast instead
|
||||
fun Point3D.asVector(): Vector3 = Vector3(this.x, this.y, this.z)
|
||||
|
||||
fun Point2D.asVector(): Vector2 = Vector2(this.x, this.y)
|
||||
|
||||
class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
|
||||
|
||||
private val vertices = ArrayList<Point3D>()
|
||||
@ -38,6 +42,7 @@ class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
|
||||
faces.add(face)
|
||||
}
|
||||
|
||||
|
||||
override fun build(): BufferGeometry {
|
||||
return Geometry().apply {
|
||||
vertices = this@ThreeGeometryBuilder.vertices.map { it.asVector() }.toTypedArray()
|
||||
|
@ -41,11 +41,12 @@ class ThreePlugin : AbstractPlugin() {
|
||||
is VisualGroup -> Group(obj.mapNotNull {
|
||||
try {
|
||||
buildObject3D(it)
|
||||
} catch (ex: Throwable){
|
||||
logger.error(ex){"Failed to render $it"}
|
||||
} catch (ex: Throwable) {
|
||||
console.error(ex)
|
||||
logger.error(ex) { "Failed to render $it" }
|
||||
null
|
||||
}
|
||||
}).apply {
|
||||
}).apply {
|
||||
updatePosition(obj)
|
||||
}
|
||||
is Composite -> compositeFactory(obj)
|
||||
|
@ -2,10 +2,11 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number, meta: Array<out Meta>) :
|
||||
VisualObject3D(parent, meta), Shape {
|
||||
VisualLeaf(parent, meta), Shape {
|
||||
|
||||
//TODO add helper for color configuration
|
||||
|
||||
|
@ -4,6 +4,7 @@ import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.seal
|
||||
import hep.dataforge.meta.update
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
enum class CompositeType {
|
||||
@ -18,7 +19,7 @@ open class Composite(
|
||||
val second: VisualObject,
|
||||
val type: CompositeType = CompositeType.UNION,
|
||||
meta: Array<out Meta>
|
||||
) : VisualObject3D(parent, meta)
|
||||
) : VisualLeaf(parent, meta)
|
||||
|
||||
fun VisualGroup.composite(
|
||||
type: CompositeType,
|
||||
|
@ -45,10 +45,23 @@ class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent
|
||||
val layers: MutableList<Layer> = ArrayList()
|
||||
|
||||
fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) {
|
||||
layers.add(Layer(x,y,z,scale))
|
||||
layers.add(Layer(x, y, z, scale))
|
||||
//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
|
||||
|
||||
@ -91,6 +104,8 @@ class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent
|
||||
)
|
||||
lowerLayer = upperLayer
|
||||
}
|
||||
geometryBuilder.cap(layers.first().reversed())
|
||||
geometryBuilder.cap(layers.last())
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -4,23 +4,9 @@ import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.output.Output
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.asName
|
||||
|
||||
/**
|
||||
* Performance optimized version of visual object
|
||||
*/
|
||||
open class VisualObject3D(parent: VisualObject?, tagRefs: Array<out Meta>) : VisualLeaf(parent, tagRefs) {
|
||||
var x: Number? = null; get() = field ?: (this as VisualLeaf).x
|
||||
var y: Number? = null; get() = field ?: (this as VisualLeaf).y
|
||||
var z: Number? = null; get() = field ?: (this as VisualLeaf).z
|
||||
|
||||
var rotationX: Number? = null; get() = field ?: (this as VisualLeaf).rotationX
|
||||
var rotationY: Number? = null; get() = field ?: (this as VisualLeaf).rotationY
|
||||
var rotationZ: Number? = null; get() = field ?: (this as VisualLeaf).rotationZ
|
||||
}
|
||||
|
||||
fun VisualGroup.group(key: String? = null, vararg meta: Meta, action: VisualGroup.() -> Unit = {}): VisualGroup =
|
||||
VisualGroup(this, meta).apply(action).also { set(key, it) }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user