feature/root #69
@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.project")
|
id("ru.mipt.npm.gradle.project")
|
||||||
// kotlin("multiplatform") version "1.5.30-RC" apply false
|
kotlin("multiplatform") version "1.5.30" apply false
|
||||||
// kotlin("js") version "1.5.30-RC" apply false
|
kotlin("js") version "1.5.30" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.5.1")
|
val dataforgeVersion by extra("0.5.1")
|
||||||
@ -16,7 +16,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.2.0-dev-23"
|
version = "0.2.0-dev-24"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
@ -36,7 +36,8 @@ apiValidation {
|
|||||||
ignoredPackages.add("info.laht.threekt")
|
ignoredPackages.add("info.laht.threekt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//workaround for https://youtrack.jetbrains.com/issue/KT-48273
|
//workaround for https://youtrack.jetbrains.com/issue/KT-48273
|
||||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>().versions.webpackDevServer.version = "4.0.0-rc.0"
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>().versions.webpackDevServer.version = "4.0.0"
|
||||||
}
|
}
|
19
cern-root-loader/build.gradle.kts
Normal file
19
cern-root-loader/build.gradle.kts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
plugins {
|
||||||
|
id("ru.mipt.npm.gradle.mpp")
|
||||||
|
}
|
||||||
|
|
||||||
|
kscience{
|
||||||
|
useSerialization {
|
||||||
|
json()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
api(project(":visionforge-solid"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
package ru.mipt.npm.root
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.misc.Named
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.asName
|
||||||
|
import space.kscience.dataforge.values.doubleArray
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
|
||||||
|
public fun MetaProvider.doubleArray(
|
||||||
|
vararg default: Double,
|
||||||
|
key: Name? = null,
|
||||||
|
): ReadOnlyProperty<Any?, DoubleArray> = value(key) {
|
||||||
|
it?.doubleArray ?: doubleArrayOf(*default)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DObjectCache(private val cache: List<Meta>, public val refStack: List<Int> = emptyList()) {
|
||||||
|
public operator fun get(index: Int): Meta = cache[index]
|
||||||
|
|
||||||
|
public fun stack(ref: Int): DObjectCache = DObjectCache(cache, refStack + ref)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val empty: DObjectCache = DObjectCache(emptyList(), emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class DObject(public val meta: Meta, private val refCache: DObjectCache) {
|
||||||
|
|
||||||
|
public val typename: String by meta.string(key = "_typename".asName()) {
|
||||||
|
error("Type is not defined")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : DObject> resolve(builder: (Meta, DObjectCache) -> T, meta: Meta): T? {
|
||||||
|
meta["\$ref"]?.int?.let { refId ->
|
||||||
|
if (refCache.refStack.contains(refId)) {
|
||||||
|
println("Circular reference $refId in stack ${refCache.refStack}")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return builder(refCache[refId], refCache.stack(refId))
|
||||||
|
}
|
||||||
|
return builder(meta, refCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun <T : DObject> tObjectArray(
|
||||||
|
builder: (Meta, DObjectCache) -> T
|
||||||
|
): ReadOnlyProperty<Any?, List<T>> = ReadOnlyProperty { _, property ->
|
||||||
|
meta.getIndexed(Name.of(property.name, "arr")).values.mapNotNull {
|
||||||
|
resolve(builder, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun <T : DObject> dObject(
|
||||||
|
builder: (Meta, DObjectCache) -> T,
|
||||||
|
key: Name? = null
|
||||||
|
): ReadOnlyProperty<Any?, T?> = ReadOnlyProperty { _, property ->
|
||||||
|
meta[key ?: property.name.asName()]?.let { resolve(builder, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class DNamed(meta: Meta, refCache: DObjectCache) : DObject(meta, refCache) {
|
||||||
|
public val fName: String by meta.string("")
|
||||||
|
public val fTitle: String by meta.string("")
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DGeoMaterial(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DGeoMedium(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||||
|
public val fMaterial: DGeoMaterial? by dObject(::DGeoMaterial)
|
||||||
|
public val fParams: DoubleArray by meta.doubleArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DGeoShape(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||||
|
public val fDX: Double by meta.double(0.0)
|
||||||
|
public val fDY: Double by meta.double(0.0)
|
||||||
|
public val fDZ: Double by meta.double(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DGeoVolume(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache), Named {
|
||||||
|
public val fNodes: List<DGeoNode> by tObjectArray(::DGeoNode)
|
||||||
|
public val fShape: DGeoShape? by dObject(::DGeoShape)
|
||||||
|
public val fMedium: DGeoMedium? by dObject(::DGeoMedium)
|
||||||
|
|
||||||
|
public val fFillColor: Int? by meta.int()
|
||||||
|
|
||||||
|
override val name: Name by lazy { Name.parse(fName.ifEmpty { "volume[${meta.hashCode().toUInt()}]" }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DGeoNode(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||||
|
public val fVolume: DGeoVolume? by dObject(::DGeoVolume)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DGeoMatrix(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class DGeoBoolNode(meta: Meta, refCache: DObjectCache) : DObject(meta, refCache) {
|
||||||
|
public val fLeft: DGeoShape? by dObject(::DGeoShape)
|
||||||
|
public val fLeftMat: DGeoMatrix? by dObject(::DGeoMatrix)
|
||||||
|
|
||||||
|
public val fRight: DGeoShape? by dObject(::DGeoShape)
|
||||||
|
public val fRightMat: DGeoMatrix? by dObject(::DGeoMatrix)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class DGeoManager(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||||
|
public val fMatrices: List<DGeoMatrix> by tObjectArray(::DGeoMatrix)
|
||||||
|
|
||||||
|
public val fShapes: List<DGeoShape> by tObjectArray(::DGeoShape)
|
||||||
|
|
||||||
|
public val fVolumes: List<DGeoVolume> by tObjectArray(::DGeoVolume)
|
||||||
|
|
||||||
|
public val fNodes: List<DGeoNode> by tObjectArray(::DGeoNode)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
|
||||||
|
public fun parse(string: String): DGeoManager {
|
||||||
|
val meta = Json.decodeFromString(MetaSerializer, string)
|
||||||
|
val res = ArrayList<Meta>(4096)
|
||||||
|
|
||||||
|
fun fillCache(element: Meta) {
|
||||||
|
if (element["\$ref"] == null) {
|
||||||
|
res.add(element)
|
||||||
|
element.items.values.forEach {
|
||||||
|
if (!it.isLeaf) {
|
||||||
|
fillCache(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fillCache(meta)
|
||||||
|
|
||||||
|
val refCache = DObjectCache(res)
|
||||||
|
return DGeoManager(meta, refCache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,331 @@
|
|||||||
|
package ru.mipt.npm.root
|
||||||
|
|
||||||
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.plus
|
||||||
|
import space.kscience.dataforge.values.doubleArray
|
||||||
|
import space.kscience.visionforge.isEmpty
|
||||||
|
import space.kscience.visionforge.solid.*
|
||||||
|
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
||||||
|
import kotlin.math.*
|
||||||
|
|
||||||
|
private val volumesName = Name.EMPTY //"volumes".asName()
|
||||||
|
|
||||||
|
private operator fun Number.times(d: Double) = toDouble() * d
|
||||||
|
|
||||||
|
private operator fun Number.times(f: Float) = toFloat() * f
|
||||||
|
|
||||||
|
private fun degToRad(d: Double) = d * PI / 180.0
|
||||||
|
|
||||||
|
private class RootToSolidContext(val prototypeHolder: PrototypeHolder, val maxLayer: Int = 3) {
|
||||||
|
val layers: MutableList<Int> = mutableListOf(0)
|
||||||
|
|
||||||
|
val layerLimits = listOf(10_000, 25_000, 50_000, 100_000, 200_000, 400_000, 600_000)
|
||||||
|
|
||||||
|
val bottomLayer: Int get() = layers.size - 1
|
||||||
|
fun addLayer() {
|
||||||
|
layers.add(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// converting to XYZ to Tait–Bryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
|
||||||
|
private fun Solid.rotate(rot: DoubleArray) {
|
||||||
|
val xAngle = atan2(-rot[5], rot[8])
|
||||||
|
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
|
||||||
|
val zAngle = atan2(-rot[1], rot[0])
|
||||||
|
rotation = Point3D(xAngle, yAngle, zAngle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Solid.translate(trans: DoubleArray) {
|
||||||
|
val (x, y, z) = trans
|
||||||
|
position = Point3D(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Solid.useMatrix(matrix: DGeoMatrix?) {
|
||||||
|
if (matrix == null) return
|
||||||
|
when (matrix.typename) {
|
||||||
|
"TGeoIdentity" -> {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
"TGeoTranslation" -> {
|
||||||
|
val fTranslation by matrix.meta.doubleArray()
|
||||||
|
translate(fTranslation)
|
||||||
|
}
|
||||||
|
"TGeoRotation" -> {
|
||||||
|
val fRotationMatrix by matrix.meta.doubleArray()
|
||||||
|
rotate(fRotationMatrix)
|
||||||
|
}
|
||||||
|
"TGeoCombiTrans" -> {
|
||||||
|
val fTranslation by matrix.meta.doubleArray()
|
||||||
|
|
||||||
|
translate(fTranslation)
|
||||||
|
matrix.meta["fRotation.fRotationMatrix"]?.value?.let {
|
||||||
|
rotate(it.doubleArray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"TGeoHMatrix" -> {
|
||||||
|
val fTranslation by matrix.meta.doubleArray()
|
||||||
|
val fRotationMatrix by matrix.meta.doubleArray()
|
||||||
|
val fScale by matrix.meta.doubleArray()
|
||||||
|
translate(fTranslation)
|
||||||
|
rotate(fRotationMatrix)
|
||||||
|
scale = Point3D(fScale[0], fScale[1], fScale[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SolidGroup.addShape(
|
||||||
|
shape: DGeoShape,
|
||||||
|
context: RootToSolidContext,
|
||||||
|
name: String? = shape.fName.ifEmpty { null },
|
||||||
|
block: Solid.() -> Unit = {}
|
||||||
|
) {
|
||||||
|
when (shape.typename) {
|
||||||
|
"TGeoCompositeShape" -> {
|
||||||
|
val fNode: DGeoBoolNode? by shape.dObject(::DGeoBoolNode)
|
||||||
|
val node = fNode ?: error("Composite shape node not resolved")
|
||||||
|
val compositeType = when (node.typename) {
|
||||||
|
"TGeoIntersection" -> CompositeType.INTERSECT
|
||||||
|
"TGeoSubtraction" -> CompositeType.SUBTRACT
|
||||||
|
"TGeoUnion" -> CompositeType.GROUP
|
||||||
|
else -> error("Unknown bool node type ${node.typename}")
|
||||||
|
}
|
||||||
|
smartComposite(compositeType, name = name) {
|
||||||
|
addShape(node.fLeft!!, context, null) {
|
||||||
|
this.useMatrix(node.fLeftMat)
|
||||||
|
}
|
||||||
|
addShape(node.fRight!!, context, null) {
|
||||||
|
this.useMatrix(node.fRightMat)
|
||||||
|
}
|
||||||
|
}.apply(block)
|
||||||
|
}
|
||||||
|
"TGeoXtru" -> {
|
||||||
|
val fNvert by shape.meta.int(0)
|
||||||
|
val fX by shape.meta.doubleArray()
|
||||||
|
val fY by shape.meta.doubleArray()
|
||||||
|
val fNz by shape.meta.int(0)
|
||||||
|
val fZ by shape.meta.doubleArray()
|
||||||
|
val fX0 by shape.meta.doubleArray()
|
||||||
|
val fY0 by shape.meta.doubleArray()
|
||||||
|
val fScale by shape.meta.doubleArray()
|
||||||
|
|
||||||
|
extruded(name = name) {
|
||||||
|
(0 until fNvert).forEach { index ->
|
||||||
|
shape {
|
||||||
|
point(fX[index], fY[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(0 until fNz).forEach { index ->
|
||||||
|
layer(
|
||||||
|
fZ[index],
|
||||||
|
fX0[index],
|
||||||
|
fY0[index],
|
||||||
|
fScale[index]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.apply(block)
|
||||||
|
}
|
||||||
|
"TGeoTube" -> {
|
||||||
|
val fRmax by shape.meta.double(0.0)
|
||||||
|
val fDz by shape.meta.double(0.0)
|
||||||
|
val fRmin by shape.meta.double(0.0)
|
||||||
|
|
||||||
|
tube(
|
||||||
|
radius = fRmax,
|
||||||
|
height = fDz * 2,
|
||||||
|
innerRadius = fRmin,
|
||||||
|
name = name,
|
||||||
|
block = block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"TGeoTubeSeg" -> {
|
||||||
|
val fRmax by shape.meta.double(0.0)
|
||||||
|
val fDz by shape.meta.double(0.0)
|
||||||
|
val fRmin by shape.meta.double(0.0)
|
||||||
|
val fPhi1 by shape.meta.double(0.0)
|
||||||
|
val fPhi2 by shape.meta.double(0.0)
|
||||||
|
|
||||||
|
tube(
|
||||||
|
radius = fRmax,
|
||||||
|
height = fDz * 2,
|
||||||
|
innerRadius = fRmin,
|
||||||
|
startAngle = degToRad(fPhi1),
|
||||||
|
angle = degToRad(fPhi2 - fPhi1),
|
||||||
|
name = name,
|
||||||
|
block = block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"TGeoPcon" -> {
|
||||||
|
val fDphi by shape.meta.double(0.0)
|
||||||
|
val fNz by shape.meta.int(2)
|
||||||
|
val fPhi1 by shape.meta.double(360.0)
|
||||||
|
val fRmax by shape.meta.doubleArray()
|
||||||
|
val fRmin by shape.meta.doubleArray()
|
||||||
|
val fZ by shape.meta.doubleArray()
|
||||||
|
if (fNz == 2) {
|
||||||
|
coneSurface(
|
||||||
|
bottomOuterRadius = fRmax[0],
|
||||||
|
bottomInnerRadius = fRmin[0],
|
||||||
|
height = fZ[1] - fZ[0],
|
||||||
|
topOuterRadius = fRmax[1],
|
||||||
|
topInnerRadius = fRmin[1],
|
||||||
|
startAngle = degToRad(fPhi1),
|
||||||
|
angle = degToRad(fDphi),
|
||||||
|
name = name,
|
||||||
|
) {
|
||||||
|
z = (fZ[1] + fZ[0]) / 2
|
||||||
|
|
||||||
|
}.apply(block)
|
||||||
|
} else {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"TGeoPgon" -> {
|
||||||
|
val fDphi by shape.meta.double(0.0)
|
||||||
|
val fNz by shape.meta.int(2)
|
||||||
|
val fPhi1 by shape.meta.double(360.0)
|
||||||
|
val fRmax by shape.meta.doubleArray()
|
||||||
|
val fRmin by shape.meta.doubleArray()
|
||||||
|
val fZ by shape.meta.doubleArray()
|
||||||
|
|
||||||
|
val fNedges by shape.meta.int(1)
|
||||||
|
|
||||||
|
val startphi = degToRad(fPhi1)
|
||||||
|
val deltaphi = degToRad(fDphi)
|
||||||
|
|
||||||
|
extruded(name) {
|
||||||
|
//getting the radius of first
|
||||||
|
require(fNz > 1) { "The polyhedron geometry requires at least two planes" }
|
||||||
|
val baseRadius = fRmax[0]
|
||||||
|
shape {
|
||||||
|
(0..fNedges).forEach {
|
||||||
|
val phi = deltaphi * fNedges * it + startphi
|
||||||
|
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(0 until fNz).forEach { index ->
|
||||||
|
//scaling all radii relative to first layer radius
|
||||||
|
layer(fZ[index], scale = fRmax[index] / baseRadius)
|
||||||
|
}
|
||||||
|
}.apply(block)
|
||||||
|
}
|
||||||
|
"TGeoShapeAssembly" -> {
|
||||||
|
val fVolume by shape.dObject(::DGeoVolume)
|
||||||
|
fVolume?.let { volume ->
|
||||||
|
addRootVolume(volume, context, block = block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"TGeoBBox" -> {
|
||||||
|
box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = name, block = block)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
TODO("A shape with type ${shape.typename} not implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SolidGroup.addRootNode(obj: DGeoNode, context: RootToSolidContext) {
|
||||||
|
val volume = obj.fVolume ?: return
|
||||||
|
addRootVolume(volume, context, obj.fName) {
|
||||||
|
if (context.bottomLayer > 0) {
|
||||||
|
this.layer = context.bottomLayer
|
||||||
|
}
|
||||||
|
when (obj.typename) {
|
||||||
|
"TGeoNodeMatrix" -> {
|
||||||
|
val fMatrix by obj.dObject(::DGeoMatrix)
|
||||||
|
this.useMatrix(fMatrix)
|
||||||
|
}
|
||||||
|
"TGeoNodeOffset" -> {
|
||||||
|
val fOffset by obj.meta.double(0.0)
|
||||||
|
x = fOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid? {
|
||||||
|
val group = SolidGroup {
|
||||||
|
val nodesNum = volume.fNodes.size
|
||||||
|
if (nodesNum == 0) {
|
||||||
|
//TODO add smart filter
|
||||||
|
volume.fShape?.let { shape ->
|
||||||
|
addShape(shape, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val expectedLayerSize = context.layers.last() + nodesNum
|
||||||
|
//If expected number exceeds layer limit, move everything else to the bottom layer.
|
||||||
|
if (expectedLayerSize >= context.layerLimits[context.bottomLayer]) {
|
||||||
|
context.addLayer()
|
||||||
|
println("Adding new layer. Sizes after add: ${context.layers}")
|
||||||
|
}
|
||||||
|
context.layers[context.bottomLayer] += nodesNum
|
||||||
|
volume.fNodes.forEach { node ->
|
||||||
|
addRootNode(node, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return if (group.isEmpty()) {
|
||||||
|
null
|
||||||
|
} else if (group.children.size == 1 && group.meta.isEmpty()) {
|
||||||
|
(group.children.values.first() as Solid).apply { parent = null }
|
||||||
|
} else {
|
||||||
|
group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//private val SolidGroup.rootPrototypes: SolidGroup get() = (parent as? SolidGroup)?.rootPrototypes ?: this
|
||||||
|
|
||||||
|
private fun SolidGroup.addRootVolume(
|
||||||
|
volume: DGeoVolume,
|
||||||
|
context: RootToSolidContext,
|
||||||
|
name: String? = null,
|
||||||
|
cache: Boolean = true,
|
||||||
|
block: Solid.() -> Unit = {}
|
||||||
|
) {
|
||||||
|
//skip if maximum layer number is reached
|
||||||
|
if (context.bottomLayer > context.maxLayer){
|
||||||
|
println("Maximum layer depth reached. Skipping ${volume.fName}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val combinedName = if (volume.fName.isEmpty()) {
|
||||||
|
name
|
||||||
|
} else if (name == null) {
|
||||||
|
volume.fName
|
||||||
|
} else {
|
||||||
|
"${name}_${volume.fName}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cache) {
|
||||||
|
val group = buildVolume(volume, context)?.apply {
|
||||||
|
volume.fFillColor?.let {
|
||||||
|
meta[MATERIAL_COLOR_KEY] = RootColors[it]
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
set(combinedName?.let { Name.parse(it) }, group)
|
||||||
|
} else {
|
||||||
|
val templateName = volumesName + volume.name
|
||||||
|
val existing = getPrototype(templateName)
|
||||||
|
if (existing == null) {
|
||||||
|
context.prototypeHolder.prototypes {
|
||||||
|
val group = buildVolume(volume, context)
|
||||||
|
set(templateName, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ref(templateName, name).apply {
|
||||||
|
volume.fFillColor?.let {
|
||||||
|
meta[MATERIAL_COLOR_KEY] = RootColors[it]
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun DGeoManager.toSolid(): SolidGroup = SolidGroup {
|
||||||
|
val context = RootToSolidContext(this)
|
||||||
|
fNodes.forEach { node ->
|
||||||
|
addRootNode(node, context)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package ru.mipt.npm.root
|
||||||
|
|
||||||
|
public object RootColors {
|
||||||
|
private val colorMap = Array<String>(924) { "white" }
|
||||||
|
|
||||||
|
//colorMap[110] = "white"
|
||||||
|
|
||||||
|
private val moreCol = listOf(
|
||||||
|
11 to "c1b7ad4d4d4d6666668080809a9a9ab3b3b3cdcdcde6e6e6f3f3f3cdc8accdc8acc3c0a9bbb6a4b3a697b8a49cae9a8d9c8f83886657b1cfc885c3a48aa9a1839f8daebdc87b8f9a768a926983976e7b857d9ad280809caca6c0d4cf88dfbb88bd9f83c89a7dc08378cf5f61ac8f94a6787b946971d45a549300ff7b00ff6300ff4b00ff3300ff1b00ff0300ff0014ff002cff0044ff005cff0074ff008cff00a4ff00bcff00d4ff00ecff00fffd00ffe500ffcd00ffb500ff9d00ff8500ff6d00ff5500ff3d00ff2600ff0e0aff0022ff003aff0052ff006aff0082ff009aff00b1ff00c9ff00e1ff00f9ff00ffef00ffd700ffbf00ffa700ff8f00ff7700ff6000ff4800ff3000ff1800ff0000",
|
||||||
|
201 to "5c5c5c7b7b7bb8b8b8d7d7d78a0f0fb81414ec4848f176760f8a0f14b81448ec4876f1760f0f8a1414b84848ec7676f18a8a0fb8b814ecec48f1f1768a0f8ab814b8ec48ecf176f10f8a8a14b8b848ecec76f1f1",
|
||||||
|
390 to "ffffcdffff9acdcd9affff66cdcd669a9a66ffff33cdcd339a9a33666633ffff00cdcd009a9a00666600333300",
|
||||||
|
406 to "cdffcd9aff9a9acd9a66ff6666cd66669a6633ff3333cd33339a3333663300ff0000cd00009a00006600003300",
|
||||||
|
422 to "cdffff9affff9acdcd66ffff66cdcd669a9a33ffff33cdcd339a9a33666600ffff00cdcd009a9a006666003333",
|
||||||
|
590 to "cdcdff9a9aff9a9acd6666ff6666cd66669a3333ff3333cd33339a3333660000ff0000cd00009a000066000033",
|
||||||
|
606 to "ffcdffff9affcd9acdff66ffcd66cd9a669aff33ffcd33cd9a339a663366ff00ffcd00cd9a009a660066330033",
|
||||||
|
622 to "ffcdcdff9a9acd9a9aff6666cd66669a6666ff3333cd33339a3333663333ff0000cd00009a0000660000330000",
|
||||||
|
791 to "ffcd9acd9a669a66339a6600cd9a33ffcd66ff9a00ffcd33cd9a00ffcd00ff9a33cd66006633009a3300cd6633ff9a66ff6600ff6633cd3300ff33009aff3366cd00336600339a0066cd339aff6666ff0066ff3333cd0033ff00cdff9a9acd66669a33669a009acd33cdff669aff00cdff339acd00cdff009affcd66cd9a339a66009a6633cd9a66ffcd00ff6633ffcd00cd9a00ffcd33ff9a00cd66006633009a3333cd6666ff9a00ff9a33ff6600cd3300ff339acdff669acd33669a00339a3366cd669aff0066ff3366ff0033cd0033ff339aff0066cd00336600669a339acd66cdff009aff33cdff009acd00cdffcd9aff9a66cd66339a66009a9a33cdcd66ff9a00ffcd33ff9a00cdcd00ff9a33ff6600cd33006633009a6633cd9a66ff6600ff6633ff3300cd3300ffff339acd00666600339a0033cd3366ff669aff0066ff3366cd0033ff0033ff9acdcd669a9a33669a0066cd339aff66cdff009acd009aff33cdff009a",
|
||||||
|
920 to "cdcdcd9a9a9a666666333333"
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
colorMap[0] = "white"
|
||||||
|
colorMap[1] = "black"
|
||||||
|
colorMap[2] = "red"
|
||||||
|
colorMap[3] = "green"
|
||||||
|
colorMap[4] = "blue"
|
||||||
|
colorMap[5] = "yellow"
|
||||||
|
colorMap[6] = "magenta"
|
||||||
|
colorMap[7] = "cyan"
|
||||||
|
colorMap[8] = "rgb(89,212,84)"
|
||||||
|
colorMap[9] = "rgb(89,84,217)"
|
||||||
|
colorMap[10] = "white"
|
||||||
|
|
||||||
|
moreCol.forEach { (n, s) ->
|
||||||
|
for (i in 0 until (s.length / 6)) {
|
||||||
|
colorMap[n + i] = "#" + s.substring(i * 6, (i + 1) * 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun get(index: Int): String = colorMap[index]
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoManager")
|
||||||
|
public class TGeoManager : TNamed() {
|
||||||
|
|
||||||
|
public val fMatrices: TObjArray<TGeoMatrix> = TObjArray.getEmpty()
|
||||||
|
|
||||||
|
public val fShapes: TObjArray<TGeoShape> = TObjArray.getEmpty()
|
||||||
|
|
||||||
|
public val fVolumes: TObjArray<TGeoVolume> = TObjArray.getEmpty()
|
||||||
|
|
||||||
|
public val fNodes: TObjArray<TGeoNode> = TObjArray.getEmpty()
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoMaterial")
|
||||||
|
public open class TGeoMaterial: TNamed()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoMixture")
|
||||||
|
public class TGeoMixture: TGeoMaterial()
|
@ -0,0 +1,42 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoMatrix")
|
||||||
|
public sealed class TGeoMatrix : TNamed()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoIdentity")
|
||||||
|
public class TGeoIdentity : TGeoMatrix()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoHMatrix")
|
||||||
|
public class TGeoHMatrix(
|
||||||
|
public val fTranslation: DoubleArray,
|
||||||
|
public val fRotationMatrix: DoubleArray,
|
||||||
|
public val fScale: DoubleArray
|
||||||
|
) : TGeoMatrix()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoTranslation")
|
||||||
|
public class TGeoTranslation(
|
||||||
|
public val fTranslation: DoubleArray
|
||||||
|
) : TGeoMatrix()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoRotation")
|
||||||
|
public class TGeoRotation(
|
||||||
|
public val fRotationMatrix: DoubleArray
|
||||||
|
) : TGeoMatrix()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoCombiTrans")
|
||||||
|
public class TGeoCombiTrans(
|
||||||
|
public val fTranslation: DoubleArray,
|
||||||
|
@Contextual
|
||||||
|
public val fRotation: TGeoRotation? = null,
|
||||||
|
) : TGeoMatrix()
|
@ -0,0 +1,14 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoMedium")
|
||||||
|
public class TGeoMedium(
|
||||||
|
public val fId: Int,
|
||||||
|
@Contextual
|
||||||
|
public val fMaterial: TGeoMaterial,
|
||||||
|
public val fParams: DoubleArray
|
||||||
|
) : TNamed()
|
@ -0,0 +1,34 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoNode")
|
||||||
|
public open class TGeoNode : TNamed() {
|
||||||
|
public val fGeoAtt: UInt = 0u
|
||||||
|
|
||||||
|
@Contextual
|
||||||
|
public val fVolume: TGeoVolume? = null
|
||||||
|
|
||||||
|
// @Contextual
|
||||||
|
// public val fMother: TGeoVolume? = null
|
||||||
|
|
||||||
|
public val fNumber: Int = 0
|
||||||
|
public val fNovlp: Int = 0
|
||||||
|
public val fOverlaps: IntArray = intArrayOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoNodeMatrix")
|
||||||
|
public class TGeoNodeMatrix : TGeoNode() {
|
||||||
|
@Contextual
|
||||||
|
public val fMatrix: TGeoMatrix? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoNodeOffset")
|
||||||
|
public class TGeoNodeOffset : TGeoNode() {
|
||||||
|
public val fOffset: Double = 0.0
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.math.PI
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoShape")
|
||||||
|
public sealed class TGeoShape : TNamed() {
|
||||||
|
public val fShapeBits: UInt = 0u
|
||||||
|
public val fShapeId: Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoBBox")
|
||||||
|
public open class TGeoBBox : TGeoShape() {
|
||||||
|
public val fDX: Double = 0.0
|
||||||
|
public val fDY: Double = 0.0
|
||||||
|
public val fDZ: Double = 0.0
|
||||||
|
public val fOrigin: DoubleArray = doubleArrayOf(0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoBoolNode")
|
||||||
|
public sealed class TGeoBoolNode : TObject() {
|
||||||
|
@Contextual
|
||||||
|
public abstract val fLeft: TGeoShape
|
||||||
|
|
||||||
|
@Contextual
|
||||||
|
public val fLeftMat: TGeoMatrix? = null
|
||||||
|
|
||||||
|
@Contextual
|
||||||
|
public abstract val fRight: TGeoShape
|
||||||
|
|
||||||
|
@Contextual
|
||||||
|
public val fRightMat: TGeoMatrix? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoUnion")
|
||||||
|
public class TGeoUnion(
|
||||||
|
@Contextual
|
||||||
|
override val fLeft: TGeoShape,
|
||||||
|
@Contextual
|
||||||
|
override val fRight: TGeoShape,
|
||||||
|
) : TGeoBoolNode()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoSubtraction")
|
||||||
|
public class TGeoSubtraction(
|
||||||
|
@Contextual
|
||||||
|
override val fLeft: TGeoShape,
|
||||||
|
@Contextual
|
||||||
|
override val fRight: TGeoShape,
|
||||||
|
) : TGeoBoolNode()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoIntersection")
|
||||||
|
public class TGeoIntersection(
|
||||||
|
@Contextual
|
||||||
|
override val fLeft: TGeoShape,
|
||||||
|
@Contextual
|
||||||
|
override val fRight: TGeoShape,
|
||||||
|
) : TGeoBoolNode()
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoCompositeShape")
|
||||||
|
public class TGeoCompositeShape(public val fNode: TGeoBoolNode) : TGeoBBox()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoXtru")
|
||||||
|
public class TGeoXtru(
|
||||||
|
public val fNvert: Int,
|
||||||
|
public val fNz: Int,
|
||||||
|
public val fZcurrent: Double,
|
||||||
|
public val fX: DoubleArray,
|
||||||
|
public val fY: DoubleArray,
|
||||||
|
public val fZ: DoubleArray,
|
||||||
|
public val fScale: DoubleArray,
|
||||||
|
public val fX0: DoubleArray,
|
||||||
|
public val fY0: DoubleArray
|
||||||
|
) : TGeoBBox()
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoTube")
|
||||||
|
public open class TGeoTube : TGeoBBox() {
|
||||||
|
public val fRmin: Double = 0.0
|
||||||
|
public val fRmax: Double = 0.0
|
||||||
|
public val fDz: Double = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoTubeSeg")
|
||||||
|
public class TGeoTubeSeg(
|
||||||
|
public val fPhi1: Double,
|
||||||
|
public val fPhi2: Double,
|
||||||
|
public val fS1: Double,
|
||||||
|
public val fC1: Double,
|
||||||
|
public val fS2: Double,
|
||||||
|
public val fC2: Double,
|
||||||
|
public val fSm: Double,
|
||||||
|
public val fCm: Double,
|
||||||
|
public val fCdfi: Double,
|
||||||
|
) : TGeoTube()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoPcon")
|
||||||
|
public open class TGeoPcon : TGeoBBox() {
|
||||||
|
public val fNz: Int = 0 // number of z planes (at least two)
|
||||||
|
public val fPhi1: Double = 0.0 // lower phi limit (converted to [0,2*pi)
|
||||||
|
public val fDphi: Double = PI * 2 // phi range
|
||||||
|
public val fRmin: DoubleArray = doubleArrayOf() //[fNz] pointer to array of inner radii
|
||||||
|
public val fRmax: DoubleArray = doubleArrayOf() //[fNz] pointer to array of outer radii
|
||||||
|
public val fZ: DoubleArray = doubleArrayOf() //[fNz] pointer to array of Z planes positions
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoPgon")
|
||||||
|
public open class TGeoPgon : TGeoPcon() {
|
||||||
|
public val fNedges: Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoShapeAssembly")
|
||||||
|
public class TGeoShapeAssembly(
|
||||||
|
@Contextual
|
||||||
|
public val fVolume: TGeoVolumeAssembly,
|
||||||
|
public val fBBoxOK: Boolean = true
|
||||||
|
) : TGeoBBox()
|
||||||
|
|
||||||
|
public class TGeoShapeRef(provider: () -> TGeoShape) : TGeoShape() {
|
||||||
|
public val value: TGeoShape by lazy(provider)
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoVolume")
|
||||||
|
public open class TGeoVolume : TNamed() {
|
||||||
|
public val fGeoAtt: UInt = 0u
|
||||||
|
public val fLineColor: Int = 2
|
||||||
|
public val fLineStyle: Int? = null
|
||||||
|
public val fLineWidth: UInt = 1u
|
||||||
|
public val fFillColor: Int? = null
|
||||||
|
public val fFillStyle: Int? = null
|
||||||
|
|
||||||
|
@Contextual
|
||||||
|
public val fNodes: TObjArray<@Contextual TGeoNode>? = null
|
||||||
|
|
||||||
|
@Contextual
|
||||||
|
public val fShape: TGeoShape? = null
|
||||||
|
|
||||||
|
@Contextual
|
||||||
|
public val fMedium: TGeoMedium? = null
|
||||||
|
|
||||||
|
public val fNumber: Int = 1
|
||||||
|
public val fNtotal: Int = 1
|
||||||
|
public val fRefCount: Int = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TGeoVolumeRef(provider: () -> TGeoVolume) : TGeoVolume() {
|
||||||
|
public val value: TGeoVolume by lazy(provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TGeoVolumeAssembly")
|
||||||
|
public open class TGeoVolumeAssembly : TGeoVolume()
|
||||||
|
|
||||||
|
public class TGeoVolumeAssemblyRef(provider: () -> TGeoVolumeAssembly) : TGeoVolumeAssembly() {
|
||||||
|
public val value: TGeoVolumeAssembly by lazy(provider)
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
public abstract class TObject {
|
||||||
|
public val fUniqueID: UInt = 0u
|
||||||
|
public val fBits: UInt = 0u
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
public open class TNamed : TObject() {
|
||||||
|
public val fName: String = ""
|
||||||
|
public val fTitle: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TObjArray")
|
||||||
|
public class TObjArray<T: TObject>(public val arr: List<@Contextual T>): TObject() {
|
||||||
|
public companion object{
|
||||||
|
public fun <T: TObject> getEmpty(): TObjArray<T> = TObjArray(emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("TList")
|
||||||
|
public class TList(public val arr: List<@Contextual TObject>): TObject()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("THashList")
|
||||||
|
public class THashList(public val arr: List<@Contextual TObject>): TObject()
|
@ -0,0 +1,235 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
|
import kotlinx.serialization.modules.contextual
|
||||||
|
import kotlinx.serialization.modules.polymorphic
|
||||||
|
import kotlinx.serialization.modules.subclass
|
||||||
|
|
||||||
|
|
||||||
|
private fun <T> jsonRootDeserializer(
|
||||||
|
tSerializer: KSerializer<T>,
|
||||||
|
builder: (JsonElement) -> T
|
||||||
|
): DeserializationStrategy<T> = object :
|
||||||
|
DeserializationStrategy<T> {
|
||||||
|
private val jsonElementSerializer = JsonElement.serializer()
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = jsonElementSerializer.descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): T {
|
||||||
|
val json = decoder.decodeSerializableValue(jsonElementSerializer)
|
||||||
|
return builder(json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load Json encoded TObject
|
||||||
|
*/
|
||||||
|
public fun <T : TObject> TObject.decodeFromJson(serializer: KSerializer<T>, jsonElement: JsonElement): T =
|
||||||
|
RootDecoder.decode(serializer, jsonElement)
|
||||||
|
|
||||||
|
public fun <T : TObject> TObject.decodeFromString(serializer: KSerializer<T>, string: String): T {
|
||||||
|
val json = Json.parseToJsonElement(string)
|
||||||
|
return RootDecoder.decode(serializer, json)
|
||||||
|
}
|
||||||
|
|
||||||
|
private object RootDecoder {
|
||||||
|
|
||||||
|
private class RootUnrefSerializer<T>(
|
||||||
|
private val tSerializer: KSerializer<T>,
|
||||||
|
private val refCache: List<RefEntry>,
|
||||||
|
) : KSerializer<T> by tSerializer {
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): T {
|
||||||
|
val input = decoder as JsonDecoder
|
||||||
|
val element = input.decodeJsonElement()
|
||||||
|
val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int
|
||||||
|
val ref = if (refId != null) {
|
||||||
|
println("Substituting ${tSerializer.descriptor.serialName} ref $refId")
|
||||||
|
//Forward ref for shapes
|
||||||
|
when (tSerializer.descriptor.serialName) {
|
||||||
|
"TGeoShape" -> return TGeoShapeRef {
|
||||||
|
refCache[refId].getOrPutValue {
|
||||||
|
input.json.decodeFromJsonElement(tSerializer, it) as TGeoShape
|
||||||
|
}
|
||||||
|
} as T
|
||||||
|
|
||||||
|
"TGeoVolumeAssembly" -> return TGeoVolumeAssemblyRef {
|
||||||
|
refCache[refId].getOrPutValue {
|
||||||
|
input.json.decodeFromJsonElement(tSerializer, it) as TGeoVolumeAssembly
|
||||||
|
}
|
||||||
|
} as T
|
||||||
|
|
||||||
|
"TGeoVolume" -> return TGeoVolumeRef {
|
||||||
|
refCache[refId].getOrPutValue {
|
||||||
|
input.json.decodeFromJsonElement(tSerializer, it) as TGeoVolume
|
||||||
|
}
|
||||||
|
} as T
|
||||||
|
|
||||||
|
//Do unref
|
||||||
|
else -> refCache[refId]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
refCache.find { it.element == element } ?: error("Element '$element' not found in the cache")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref.getOrPutValue {
|
||||||
|
// println("Decoding $it")
|
||||||
|
val actualTypeName = it.jsonObject["_typename"]?.jsonPrimitive?.content
|
||||||
|
input.json.decodeFromJsonElement(tSerializer, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> KSerializer<T>.unref(refCache: List<RefEntry>): KSerializer<T> = RootUnrefSerializer(this, refCache)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
fun unrefSerializersModule(
|
||||||
|
refCache: List<RefEntry>
|
||||||
|
): SerializersModule = SerializersModule {
|
||||||
|
|
||||||
|
contextual(TObjArray::class) {
|
||||||
|
TObjArray.serializer(it[0]).unref(refCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
contextual(TGeoMedium.serializer().unref(refCache))
|
||||||
|
|
||||||
|
polymorphic(TGeoBoolNode::class) {
|
||||||
|
subclass(TGeoIntersection.serializer().unref(refCache))
|
||||||
|
subclass(TGeoUnion.serializer().unref(refCache))
|
||||||
|
subclass(TGeoSubtraction.serializer().unref(refCache))
|
||||||
|
}
|
||||||
|
|
||||||
|
polymorphic(TGeoShape::class) {
|
||||||
|
subclass(TGeoBBox.serializer())
|
||||||
|
subclass(TGeoXtru.serializer())
|
||||||
|
subclass(TGeoTube.serializer())
|
||||||
|
subclass(TGeoTubeSeg.serializer())
|
||||||
|
subclass(TGeoPcon.serializer())
|
||||||
|
subclass(TGeoPgon.serializer())
|
||||||
|
|
||||||
|
subclass(TGeoCompositeShape.serializer().unref(refCache))
|
||||||
|
subclass(TGeoShapeAssembly.serializer().unref(refCache))
|
||||||
|
|
||||||
|
default {
|
||||||
|
if (it == null) {
|
||||||
|
TGeoShape.serializer().unref(refCache)
|
||||||
|
} else {
|
||||||
|
error("Unrecognized shape $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
polymorphic(TGeoMatrix::class) {
|
||||||
|
subclass(TGeoIdentity.serializer())
|
||||||
|
subclass(TGeoHMatrix.serializer().unref(refCache))
|
||||||
|
subclass(TGeoTranslation.serializer())
|
||||||
|
subclass(TGeoRotation.serializer())
|
||||||
|
subclass(TGeoCombiTrans.serializer().unref(refCache))
|
||||||
|
|
||||||
|
|
||||||
|
val unrefed = TGeoMatrix.serializer().unref(refCache)
|
||||||
|
default {
|
||||||
|
if (it == null) {
|
||||||
|
unrefed
|
||||||
|
} else {
|
||||||
|
error("Unrecognized matrix $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
polymorphic(TGeoVolume::class, TGeoVolume.serializer().unref(refCache)) {
|
||||||
|
subclass(TGeoVolumeAssembly.serializer().unref(refCache))
|
||||||
|
|
||||||
|
val unrefed = TGeoVolume.serializer().unref(refCache)
|
||||||
|
default {
|
||||||
|
if (it == null) {
|
||||||
|
unrefed
|
||||||
|
} else {
|
||||||
|
error("Unrecognized volume $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
polymorphic(TGeoNode::class, TGeoNode.serializer().unref(refCache)) {
|
||||||
|
subclass(TGeoNodeMatrix.serializer().unref(refCache))
|
||||||
|
subclass(TGeoNodeOffset.serializer().unref(refCache))
|
||||||
|
|
||||||
|
val unrefed = TGeoNode.serializer().unref(refCache)
|
||||||
|
default {
|
||||||
|
if (it == null) {
|
||||||
|
unrefed
|
||||||
|
} else {
|
||||||
|
error("Unrecognized node $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of Json with unfolding Root references. This instance could not be reused because of the cache.
|
||||||
|
*/
|
||||||
|
private fun unrefJson(refCache: MutableList<RefEntry>): Json = Json {
|
||||||
|
encodeDefaults = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
classDiscriminator = "_typename"
|
||||||
|
serializersModule = unrefSerializersModule(refCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun <T : TObject> decode(sourceDeserializer: KSerializer<T>, source: JsonElement): T {
|
||||||
|
val refCache = ArrayList<RefEntry>()
|
||||||
|
|
||||||
|
fun fillCache(element: JsonElement) {
|
||||||
|
when (element) {
|
||||||
|
is JsonObject -> {
|
||||||
|
if (element["_typename"] != null) {
|
||||||
|
refCache.add(RefEntry(element))
|
||||||
|
}
|
||||||
|
element.values.forEach {
|
||||||
|
fillCache(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is JsonArray -> {
|
||||||
|
element.forEach {
|
||||||
|
fillCache(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
//ignore primitives
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fillCache(source)
|
||||||
|
|
||||||
|
return unrefJson(refCache).decodeFromJsonElement(sourceDeserializer.unref(refCache), source)
|
||||||
|
}
|
||||||
|
|
||||||
|
class RefEntry(val element: JsonElement) {
|
||||||
|
|
||||||
|
var value: Any? = null
|
||||||
|
|
||||||
|
fun <T> getOrPutValue(builder: (JsonElement) -> T): T {
|
||||||
|
if (value == null) {
|
||||||
|
value = builder(element)
|
||||||
|
}
|
||||||
|
return value as T
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = element.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// val json = Json {
|
||||||
|
// encodeDefaults = true
|
||||||
|
// ignoreUnknownKeys = true
|
||||||
|
// classDiscriminator = "_typename"
|
||||||
|
// serializersModule = this@RootDecoder.serializersModule
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,187 @@
|
|||||||
|
package ru.mipt.npm.root.serialization
|
||||||
|
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.asName
|
||||||
|
import space.kscience.dataforge.names.plus
|
||||||
|
import space.kscience.visionforge.solid.*
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.atan2
|
||||||
|
import kotlin.math.pow
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
|
||||||
|
private val solidsName = "solids".asName()
|
||||||
|
private val volumesName = "volumes".asName()
|
||||||
|
|
||||||
|
private operator fun Number.times(d: Double) = toDouble() * d
|
||||||
|
|
||||||
|
private operator fun Number.times(f: Float) = toFloat() * f
|
||||||
|
|
||||||
|
private fun degToRad(d: Double) = d * PI / 180.0
|
||||||
|
|
||||||
|
// converting to XYZ to Tait–Bryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
|
||||||
|
private fun Solid.rotate(rot: DoubleArray) {
|
||||||
|
val xAngle = atan2(-rot[5], rot[8])
|
||||||
|
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
|
||||||
|
val zAngle = atan2(-rot[1], rot[0])
|
||||||
|
rotation = Point3D(xAngle, yAngle, zAngle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Solid.translate(trans: DoubleArray) {
|
||||||
|
val (x, y, z) = trans
|
||||||
|
position = Point3D(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Solid.useMatrix(matrix: TGeoMatrix?) {
|
||||||
|
when (matrix) {
|
||||||
|
null, is TGeoIdentity -> {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
is TGeoTranslation -> {
|
||||||
|
translate(matrix.fTranslation)
|
||||||
|
}
|
||||||
|
is TGeoRotation -> {
|
||||||
|
rotate(matrix.fRotationMatrix)
|
||||||
|
}
|
||||||
|
is TGeoCombiTrans -> {
|
||||||
|
translate(matrix.fTranslation)
|
||||||
|
matrix.fRotation?.let { rotate(it.fRotationMatrix) }
|
||||||
|
}
|
||||||
|
is TGeoHMatrix -> {
|
||||||
|
translate(matrix.fTranslation)
|
||||||
|
rotate(matrix.fRotationMatrix)
|
||||||
|
val (xScale, yScale, zScale) = matrix.fScale
|
||||||
|
scale = Point3D(xScale, yScale, zScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SolidGroup.addShape(shape: TGeoShape) {
|
||||||
|
when (shape) {
|
||||||
|
is TGeoShapeRef -> addShape(shape.value)
|
||||||
|
is TGeoCompositeShape -> {
|
||||||
|
val bool: TGeoBoolNode = shape.fNode
|
||||||
|
val compositeType = when (bool) {
|
||||||
|
is TGeoIntersection -> CompositeType.INTERSECT
|
||||||
|
is TGeoSubtraction -> CompositeType.SUBTRACT
|
||||||
|
is TGeoUnion -> CompositeType.UNION
|
||||||
|
}
|
||||||
|
composite(compositeType, name = shape.fName) {
|
||||||
|
addShape(bool.fLeft).apply {
|
||||||
|
useMatrix(bool.fLeftMat)
|
||||||
|
}
|
||||||
|
addShape(bool.fRight).apply {
|
||||||
|
useMatrix(bool.fRightMat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is TGeoXtru -> extruded(name = shape.fName) {
|
||||||
|
|
||||||
|
(0 until shape.fNvert).forEach { index ->
|
||||||
|
shape {
|
||||||
|
point(shape.fX[index], shape.fY[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(0 until shape.fNz).forEach { index ->
|
||||||
|
layer(
|
||||||
|
shape.fZ[index],
|
||||||
|
shape.fX0[index],
|
||||||
|
shape.fY0[index],
|
||||||
|
shape.fScale[index]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is TGeoTube -> tube(
|
||||||
|
radius = shape.fRmax,
|
||||||
|
height = shape.fDz * 2,
|
||||||
|
innerRadius = shape.fRmin,
|
||||||
|
name = shape.fName
|
||||||
|
)
|
||||||
|
is TGeoTubeSeg -> tube(
|
||||||
|
radius = shape.fRmax,
|
||||||
|
height = shape.fDz * 2,
|
||||||
|
innerRadius = shape.fRmin,
|
||||||
|
startAngle = degToRad(shape.fPhi1),
|
||||||
|
angle = degToRad(shape.fPhi2 - shape.fPhi1),
|
||||||
|
name = shape.fName
|
||||||
|
)
|
||||||
|
is TGeoPcon -> TODO()
|
||||||
|
is TGeoPgon -> TODO()
|
||||||
|
is TGeoShapeAssembly -> volume(shape.fVolume)
|
||||||
|
is TGeoBBox -> box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = shape.fName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SolidGroup.node(obj: TGeoNode) {
|
||||||
|
if (obj.fVolume != null) {
|
||||||
|
volume(obj.fVolume, obj.fName).apply {
|
||||||
|
when (obj) {
|
||||||
|
is TGeoNodeMatrix -> {
|
||||||
|
useMatrix(obj.fMatrix)
|
||||||
|
}
|
||||||
|
is TGeoNodeOffset -> {
|
||||||
|
x = obj.fOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildGroup(volume: TGeoVolume): SolidGroup {
|
||||||
|
return if (volume is TGeoVolumeAssemblyRef) {
|
||||||
|
buildGroup(volume.value)
|
||||||
|
} else {
|
||||||
|
SolidGroup {
|
||||||
|
volume.fShape?.let { addShape(it) }
|
||||||
|
volume.fNodes?.let {
|
||||||
|
it.arr.forEach { obj ->
|
||||||
|
node(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val SolidGroup.rootPrototypes: SolidGroup get() = (parent as? SolidGroup)?.rootPrototypes ?: this
|
||||||
|
|
||||||
|
private fun SolidGroup.volume(volume: TGeoVolume, name: String? = null, cache: Boolean = true): Solid {
|
||||||
|
val group = buildGroup(volume)
|
||||||
|
val combinedName = if (volume.fName.isEmpty()) {
|
||||||
|
name
|
||||||
|
} else if (name == null) {
|
||||||
|
volume.fName
|
||||||
|
} else {
|
||||||
|
"${name}_${volume.fName}"
|
||||||
|
}
|
||||||
|
return if (!cache) {
|
||||||
|
group
|
||||||
|
} else newRef(
|
||||||
|
name = combinedName,
|
||||||
|
obj = group,
|
||||||
|
prototypeHolder = rootPrototypes,
|
||||||
|
templateName = volumesName + Name.parse(combinedName ?: "volume[${group.hashCode()}]")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// private fun load(geo: TGeoManager): SolidGroup {
|
||||||
|
//// /**
|
||||||
|
//// * A special group for local templates
|
||||||
|
//// */
|
||||||
|
//// val proto = SolidGroup()
|
||||||
|
////
|
||||||
|
//// val solids = proto.group(solidsName) {
|
||||||
|
//// setPropertyNode("edges.enabled", false)
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// val volumes = proto.group(volumesName)
|
||||||
|
////
|
||||||
|
//// val referenceStore = HashMap<Name, MutableList<SolidReferenceGroup>>()
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public fun TGeoManager.toSolid(): SolidGroup = SolidGroup {
|
||||||
|
fNodes.arr.forEach {
|
||||||
|
node(it)
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,6 @@ class GDMLDemoApp : App(GDMLView::class)
|
|||||||
class GDMLView : View() {
|
class GDMLView : View() {
|
||||||
private val context = Context {
|
private val context = Context {
|
||||||
plugin(FX3DPlugin)
|
plugin(FX3DPlugin)
|
||||||
plugin(VisionManager)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val fx3d = context.fetch(FX3DPlugin)
|
private val fx3d = context.fetch(FX3DPlugin)
|
||||||
|
@ -6,6 +6,7 @@ import react.RProps
|
|||||||
import react.child
|
import react.child
|
||||||
import react.functionalComponent
|
import react.functionalComponent
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.plotly.layout
|
||||||
import space.kscience.plotly.models.Trace
|
import space.kscience.plotly.models.Trace
|
||||||
import space.kscience.visionforge.markup.VisionOfMarkup
|
import space.kscience.visionforge.markup.VisionOfMarkup
|
||||||
import space.kscience.visionforge.react.flexRow
|
import space.kscience.visionforge.react.flexRow
|
||||||
@ -91,7 +92,10 @@ val GravityDemo = functionalComponent<DemoProps> { props ->
|
|||||||
height = 50.vh - 50.pt
|
height = 50.vh - 50.pt
|
||||||
}
|
}
|
||||||
plotly {
|
plotly {
|
||||||
traces(velocityTrace)
|
traces(velocityTrace,energyTrace)
|
||||||
|
layout {
|
||||||
|
xaxis.title = "time"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Markup {
|
Markup {
|
||||||
attrs {
|
attrs {
|
||||||
|
@ -32,7 +32,7 @@ val Markup = functionalComponent<MarkupProps>("Markup") { props ->
|
|||||||
//TODO add new formats via plugins
|
//TODO add new formats via plugins
|
||||||
else -> error("Format ${vision.format} not recognized")
|
else -> error("Format ${vision.format} not recognized")
|
||||||
}
|
}
|
||||||
vision.useProperty(VisionOfMarkup::content) { content ->
|
vision.useProperty(VisionOfMarkup::content) { content: String? ->
|
||||||
element.clear()
|
element.clear()
|
||||||
element.append {
|
element.append {
|
||||||
markdown(flavour) { content ?: "" }
|
markdown(flavour) { content ?: "" }
|
||||||
|
@ -6,7 +6,6 @@ import kotlinx.html.script
|
|||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import kotlinx.html.unsafe
|
import kotlinx.html.unsafe
|
||||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||||
import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.libraries.*
|
import org.jetbrains.kotlinx.jupyter.api.libraries.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
@ -22,7 +21,6 @@ import space.kscience.visionforge.plotly.asVision
|
|||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.visionManager
|
import space.kscience.visionforge.visionManager
|
||||||
|
|
||||||
@JupyterLibrary
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public class VisionForgePlayGroundForJupyter : JupyterIntegration() {
|
public class VisionForgePlayGroundForJupyter : JupyterIntegration() {
|
||||||
|
|
||||||
|
@ -65,12 +65,6 @@ application {
|
|||||||
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
|
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile>() {
|
|
||||||
kotlinOptions {
|
|
||||||
freeCompilerArgs = freeCompilerArgs + "-Xir-property-lazy-initialization"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//distributions {
|
//distributions {
|
||||||
// main {
|
// main {
|
||||||
// contents {
|
// contents {
|
||||||
|
@ -55,6 +55,7 @@ kotlin {
|
|||||||
api(project(":visionforge-gdml"))
|
api(project(":visionforge-gdml"))
|
||||||
api(project(":visionforge-plotly"))
|
api(project(":visionforge-plotly"))
|
||||||
api(projects.visionforge.visionforgeMarkdown)
|
api(projects.visionforge.visionforgeMarkdown)
|
||||||
|
api(projects.visionforge.cernRootLoader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
109
demo/playground/src/jvmMain/kotlin/rootParser.kt
Normal file
109
demo/playground/src/jvmMain/kotlin/rootParser.kt
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
|
import ru.mipt.npm.root.DGeoManager
|
||||||
|
import ru.mipt.npm.root.serialization.TGeoManager
|
||||||
|
import ru.mipt.npm.root.toSolid
|
||||||
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.isLeaf
|
||||||
|
import space.kscience.dataforge.values.string
|
||||||
|
import space.kscience.visionforge.solid.Solids
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
|
||||||
|
|
||||||
|
private fun Meta.countTypes(): Sequence<String> = sequence {
|
||||||
|
if (!isLeaf) {
|
||||||
|
get("_typename")?.value?.let { yield(it.string) }
|
||||||
|
items.forEach { yieldAll(it.value.countTypes()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val context = Context {
|
||||||
|
plugin(Solids)
|
||||||
|
}
|
||||||
|
|
||||||
|
val string = TGeoManager::class.java.getResourceAsStream("/root/BM@N.root.json")!!
|
||||||
|
.readAllBytes().decodeToString()
|
||||||
|
|
||||||
|
val geo = DGeoManager.parse(string)
|
||||||
|
|
||||||
|
|
||||||
|
val sizes = geo.meta.countTypes().groupBy { it }.mapValues { it.value.size }
|
||||||
|
sizes.forEach {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val solid = geo.toSolid()
|
||||||
|
|
||||||
|
Paths.get("BM@N.vf.json").writeText(Solids.encodeToString(solid))
|
||||||
|
//println(Solids.encodeToString(solid))
|
||||||
|
|
||||||
|
context.makeVisionFile {
|
||||||
|
vision("canvas") {
|
||||||
|
solid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* SolidGroup {
|
||||||
|
set(
|
||||||
|
"Coil",
|
||||||
|
solid.getPrototype("Coil".asName())!!.apply {
|
||||||
|
parent = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
*//* group("Shade") {
|
||||||
|
y = 200
|
||||||
|
color("red")
|
||||||
|
coneSurface(
|
||||||
|
bottomOuterRadius = 135,
|
||||||
|
bottomInnerRadius = 25,
|
||||||
|
height = 50,
|
||||||
|
topOuterRadius = 135,
|
||||||
|
topInnerRadius = 25,
|
||||||
|
angle = 1.5707964
|
||||||
|
) {
|
||||||
|
position = Point3D(79.6, 0, -122.1)
|
||||||
|
rotation = Point3D(-1.5707964, 0, 0)
|
||||||
|
}
|
||||||
|
coneSurface(
|
||||||
|
bottomOuterRadius = 135,
|
||||||
|
bottomInnerRadius = 25,
|
||||||
|
height = 50,
|
||||||
|
topOuterRadius = 135,
|
||||||
|
topInnerRadius = 25,
|
||||||
|
angle = 1.5707964
|
||||||
|
) {
|
||||||
|
position = Point3D(-79.6, 0, -122.1)
|
||||||
|
rotation = Point3D(1.5707964, 0, -3.1415927)
|
||||||
|
}
|
||||||
|
coneSurface(
|
||||||
|
bottomOuterRadius = 135,
|
||||||
|
bottomInnerRadius = 25,
|
||||||
|
height = 50,
|
||||||
|
topOuterRadius = 135,
|
||||||
|
topInnerRadius = 25,
|
||||||
|
angle = 1.5707964
|
||||||
|
) {
|
||||||
|
position = Point3D(79.6, 0, 122.1)
|
||||||
|
rotation = Point3D(1.5707964, 0, 0)
|
||||||
|
}
|
||||||
|
coneSurface(
|
||||||
|
bottomOuterRadius = 135,
|
||||||
|
bottomInnerRadius = 25,
|
||||||
|
height = 50,
|
||||||
|
topOuterRadius = 135,
|
||||||
|
topInnerRadius = 25,
|
||||||
|
angle = 1.5707964
|
||||||
|
) {
|
||||||
|
position = Point3D(-79.6, 0, 122.1)
|
||||||
|
rotation = Point3D(-1.5707964, 0, -3.1415927)
|
||||||
|
}
|
||||||
|
}*//*
|
||||||
|
}*/
|
26920
demo/playground/src/jvmMain/resources/root/BM@N.root.json
Normal file
26920
demo/playground/src/jvmMain/resources/root/BM@N.root.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,7 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
|
||||||
kotlin.mpp.stability.nowarn=true
|
kotlin.mpp.stability.nowarn=true
|
||||||
kotlin.native.enableDependencyPropagation=false
|
|
||||||
kapt.use.worker.api=false
|
kotlin.jupyter.add.scanner=false
|
||||||
kapt.incremental.apt=false
|
|
||||||
|
|
||||||
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G
|
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
@ -57,3 +57,7 @@ kscience {
|
|||||||
readme {
|
readme {
|
||||||
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
|
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.named<org.jetbrains.kotlinx.jupyter.api.plugin.tasks.JupyterApiResourcesTask>("processJupyterApiResources") {
|
||||||
|
libraryProducers = listOf("space.kscience.visionforge.gdml.jupyter.GdmlForJupyter")
|
||||||
|
}
|
||||||
|
@ -2,7 +2,6 @@ package space.kscience.visionforge.gdml.jupyter
|
|||||||
|
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||||
import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
||||||
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
@ -16,7 +15,6 @@ import space.kscience.visionforge.html.embedAndRenderVisionFragment
|
|||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.visionManager
|
import space.kscience.visionforge.visionManager
|
||||||
|
|
||||||
@JupyterLibrary
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
internal class GdmlForJupyter : JupyterIntegration() {
|
internal class GdmlForJupyter : JupyterIntegration() {
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ include(
|
|||||||
":visionforge-threejs",
|
":visionforge-threejs",
|
||||||
":visionforge-threejs:visionforge-threejs-server",
|
":visionforge-threejs:visionforge-threejs-server",
|
||||||
":visionforge-gdml",
|
":visionforge-gdml",
|
||||||
|
":cern-root-loader",
|
||||||
":visionforge-server",
|
":visionforge-server",
|
||||||
":visionforge-plotly",
|
":visionforge-plotly",
|
||||||
":visionforge-markdown",
|
":visionforge-markdown",
|
||||||
|
@ -83,7 +83,7 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
|
|||||||
}
|
}
|
||||||
obj.children.entries
|
obj.children.entries
|
||||||
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
||||||
.sortedBy { (it.value as? VisionGroup)?.isEmpty ?: true } // ignore empty groups
|
.sortedBy { (it.value as? VisionGroup)?.isEmpty() ?: true } // ignore empty groups
|
||||||
.forEach { (childToken, child) ->
|
.forEach { (childToken, child) ->
|
||||||
styledDiv {
|
styledDiv {
|
||||||
css {
|
css {
|
||||||
|
@ -13,9 +13,7 @@ import space.kscience.dataforge.names.Name
|
|||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.names.isEmpty
|
import space.kscience.dataforge.names.isEmpty
|
||||||
import space.kscience.dataforge.names.length
|
import space.kscience.dataforge.names.length
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.*
|
||||||
import space.kscience.visionforge.VisionGroup
|
|
||||||
import space.kscience.visionforge.computeProperties
|
|
||||||
import space.kscience.visionforge.react.ThreeCanvasComponent
|
import space.kscience.visionforge.react.ThreeCanvasComponent
|
||||||
import space.kscience.visionforge.react.flexColumn
|
import space.kscience.visionforge.react.flexColumn
|
||||||
import space.kscience.visionforge.react.flexRow
|
import space.kscience.visionforge.react.flexRow
|
||||||
@ -85,7 +83,9 @@ public val ThreeCanvasWithControls: FunctionComponent<ThreeCanvasWithControlsPro
|
|||||||
|
|
||||||
useEffect {
|
useEffect {
|
||||||
props.context.launch {
|
props.context.launch {
|
||||||
solid = props.builderOfSolid.await()
|
solid = props.builderOfSolid.await().also {
|
||||||
|
it?.root(props.context.visionManager)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public interface VisionGroup : Provider, Vision, VisionContainer<Vision> {
|
|||||||
*/
|
*/
|
||||||
public operator fun VisionGroup.iterator(): Iterator<Vision> = children.values.iterator()
|
public operator fun VisionGroup.iterator(): Iterator<Vision> = children.values.iterator()
|
||||||
|
|
||||||
public val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty()
|
public fun VisionGroup.isEmpty(): Boolean = this.children.isEmpty()
|
||||||
|
|
||||||
public interface VisionContainerBuilder<in V : Vision> {
|
public interface VisionContainerBuilder<in V : Vision> {
|
||||||
//TODO add documentation
|
//TODO add documentation
|
||||||
|
@ -76,7 +76,7 @@ public open class VisionGroupBase(
|
|||||||
* Set parent for given child and attach it
|
* Set parent for given child and attach it
|
||||||
*/
|
*/
|
||||||
private fun attachChild(token: NameToken, child: Vision?) {
|
private fun attachChild(token: NameToken, child: Vision?) {
|
||||||
val before = children[token]
|
val before = childrenInternal[token]
|
||||||
when {
|
when {
|
||||||
child == null -> {
|
child == null -> {
|
||||||
childrenInternal.remove(token)
|
childrenInternal.remove(token)
|
||||||
|
@ -67,8 +67,8 @@ internal fun checkOrStoreFile(htmlPath: Path, filePath: Path, resource: String):
|
|||||||
if (!skip) {
|
if (!skip) {
|
||||||
logger.debug("File $fullPath does not exist or wrong checksum. Writing file")
|
logger.debug("File $fullPath does not exist or wrong checksum. Writing file")
|
||||||
Files.createDirectories(fullPath.parent)
|
Files.createDirectories(fullPath.parent)
|
||||||
Files.write(fullPath, bytes, StandardOpenOption.CREATE, StandardOpenOption.WRITE)
|
Files.write(fullPath, bytes, StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)
|
||||||
Files.write(md5File, checksum.encodeToByteArray(), StandardOpenOption.CREATE, StandardOpenOption.WRITE)
|
Files.write(md5File, checksum.encodeToByteArray(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (htmlPath.isAbsolute && fullPath.startsWith(htmlPath.parent)) {
|
return if (htmlPath.isAbsolute && fullPath.startsWith(htmlPath.parent)) {
|
||||||
|
@ -27,6 +27,8 @@ import kotlin.reflect.KClass
|
|||||||
public class FX3DPlugin : AbstractPlugin() {
|
public class FX3DPlugin : AbstractPlugin() {
|
||||||
override val tag: PluginTag get() = Companion.tag
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
|
public val solids: Solids by require(Solids)
|
||||||
|
|
||||||
private val objectFactories = HashMap<KClass<out Solid>, FX3DFactory<*>>()
|
private val objectFactories = HashMap<KClass<out Solid>, FX3DFactory<*>>()
|
||||||
private val compositeFactory = FXCompositeFactory(this)
|
private val compositeFactory = FXCompositeFactory(this)
|
||||||
private val referenceFactory = FXReferenceFactory(this)
|
private val referenceFactory = FXReferenceFactory(this)
|
||||||
@ -50,6 +52,7 @@ public class FX3DPlugin : AbstractPlugin() {
|
|||||||
is SolidGroup -> {
|
is SolidGroup -> {
|
||||||
Group(obj.children.mapNotNull { (token, obj) ->
|
Group(obj.children.mapNotNull { (token, obj) ->
|
||||||
(obj as? Solid)?.let {
|
(obj as? Solid)?.let {
|
||||||
|
logger.info { token.toString() }
|
||||||
buildNode(it).apply {
|
buildNode(it).apply {
|
||||||
properties["name"] = token.toString()
|
properties["name"] = token.toString()
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public class FXCompositeFactory(public val plugin: FX3DPlugin) : FX3DFactory<Com
|
|||||||
val firstCSG = first.toCSG()
|
val firstCSG = first.toCSG()
|
||||||
val secondCSG = second.toCSG()
|
val secondCSG = second.toCSG()
|
||||||
val resultCSG = when (obj.compositeType) {
|
val resultCSG = when (obj.compositeType) {
|
||||||
CompositeType.SUM, CompositeType.UNION -> firstCSG.union(secondCSG)
|
CompositeType.GROUP, CompositeType.UNION -> firstCSG.union(secondCSG)
|
||||||
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
||||||
CompositeType.SUBTRACT -> firstCSG.difference(secondCSG)
|
CompositeType.SUBTRACT -> firstCSG.difference(secondCSG)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform")
|
|
||||||
id("ru.mipt.npm.gradle.mpp")
|
id("ru.mipt.npm.gradle.mpp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
package space.kscience.visionforge.gdml
|
||||||
|
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.MutableMeta
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.gdml.*
|
||||||
|
import space.kscience.visionforge.solid.Solid
|
||||||
|
import space.kscience.visionforge.solid.SolidMaterial
|
||||||
|
import space.kscience.visionforge.solid.invoke
|
||||||
|
import space.kscience.visionforge.useStyle
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
public class GdmlLoaderOptions {
|
||||||
|
|
||||||
|
public enum class Action {
|
||||||
|
ADD,
|
||||||
|
REJECT,
|
||||||
|
PROTOTYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
public var lUnit: LUnit = LUnit.MM
|
||||||
|
public var aUnit: AUnit = AUnit.RADIAN
|
||||||
|
|
||||||
|
public var solidAction: (GdmlSolid) -> Action = { Action.PROTOTYPE }
|
||||||
|
public var volumeAction: (GdmlGroup) -> Action = { Action.PROTOTYPE }
|
||||||
|
|
||||||
|
internal val styleCache = HashMap<Name, Meta>()
|
||||||
|
|
||||||
|
public fun Solid.registerAndUseStyle(name: String, builder: MutableMeta.() -> Unit) {
|
||||||
|
styleCache.getOrPut(Name.parse(name)) {
|
||||||
|
Meta(builder)
|
||||||
|
}
|
||||||
|
useStyle(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun Solid.transparent() {
|
||||||
|
registerAndUseStyle("transparent") {
|
||||||
|
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
|
||||||
|
"edges.enabled" put true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure paint for given solid with given [GdmlMaterial]
|
||||||
|
*/
|
||||||
|
public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit =
|
||||||
|
{ material, _ -> color(randomColor(material)) }
|
||||||
|
private set
|
||||||
|
|
||||||
|
public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) {
|
||||||
|
configurePaint = block
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure given solid
|
||||||
|
*/
|
||||||
|
public var configureSolid: Solid.(parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial) -> Unit =
|
||||||
|
{ parent, solid, material ->
|
||||||
|
val styleName = "materials.${material.name}"
|
||||||
|
|
||||||
|
if (parent.physVolumes.isNotEmpty()) transparent()
|
||||||
|
|
||||||
|
registerAndUseStyle(styleName) {
|
||||||
|
val vfMaterial = SolidMaterial().apply {
|
||||||
|
configurePaint(material, solid)
|
||||||
|
}
|
||||||
|
SolidMaterial.MATERIAL_KEY put vfMaterial.toMeta()
|
||||||
|
"Gdml.material" put material.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
|
||||||
|
public fun configure(block: Solid.(parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial) -> Unit) {
|
||||||
|
val oldConfigure = configureSolid
|
||||||
|
configureSolid = { parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial ->
|
||||||
|
oldConfigure(parent, solid, material)
|
||||||
|
block(parent, solid, material)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
private val random: Random = Random(222)
|
||||||
|
|
||||||
|
private val colorCache = HashMap<GdmlMaterial, Int>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use random color and cache it based on the material. Meaning that colors are random, but always the same for the
|
||||||
|
* same material.
|
||||||
|
*/
|
||||||
|
public fun randomColor(material: GdmlMaterial): Int {
|
||||||
|
return colorCache.getOrPut(material) { random.nextInt(16777216) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
package space.kscience.visionforge.gdml
|
package space.kscience.visionforge.gdml
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
@ -11,10 +9,8 @@ import space.kscience.gdml.*
|
|||||||
import space.kscience.visionforge.*
|
import space.kscience.visionforge.*
|
||||||
import space.kscience.visionforge.html.VisionOutput
|
import space.kscience.visionforge.html.VisionOutput
|
||||||
import space.kscience.visionforge.solid.*
|
import space.kscience.visionforge.solid.*
|
||||||
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY
|
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
private val solidsName = "solids".asName()
|
private val solidsName = "solids".asName()
|
||||||
private val volumesName = "volumes".asName()
|
private val volumesName = "volumes".asName()
|
||||||
@ -25,91 +21,7 @@ private inline operator fun Number.times(d: Double) = toDouble() * d
|
|||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline operator fun Number.times(f: Float) = toFloat() * f
|
private inline operator fun Number.times(f: Float) = toFloat() * f
|
||||||
|
|
||||||
public class GdmlTransformer {
|
private class GdmlLoader(val settings: GdmlLoaderOptions) {
|
||||||
|
|
||||||
public enum class Action {
|
|
||||||
ADD,
|
|
||||||
REJECT,
|
|
||||||
PROTOTYPE
|
|
||||||
}
|
|
||||||
|
|
||||||
public var lUnit: LUnit = LUnit.MM
|
|
||||||
public var aUnit: AUnit = AUnit.RADIAN
|
|
||||||
|
|
||||||
public var solidAction: (GdmlSolid) -> Action = { Action.PROTOTYPE }
|
|
||||||
public var volumeAction: (GdmlGroup) -> Action = { Action.PROTOTYPE }
|
|
||||||
|
|
||||||
internal val styleCache = HashMap<Name, Meta>()
|
|
||||||
|
|
||||||
public fun Solid.registerAndUseStyle(name: String, builder: MutableMeta.() -> Unit) {
|
|
||||||
styleCache.getOrPut(Name.parse(name)) {
|
|
||||||
Meta(builder)
|
|
||||||
}
|
|
||||||
useStyle(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun Solid.transparent() {
|
|
||||||
registerAndUseStyle("transparent") {
|
|
||||||
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
|
|
||||||
"edges.enabled" put true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure paint for given solid with given [GdmlMaterial]
|
|
||||||
*/
|
|
||||||
public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit =
|
|
||||||
{ material, _ -> color(randomColor(material)) }
|
|
||||||
private set
|
|
||||||
|
|
||||||
public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) {
|
|
||||||
configurePaint = block
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure given solid
|
|
||||||
*/
|
|
||||||
public var configureSolid: Solid.(parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial) -> Unit =
|
|
||||||
{ parent, solid, material ->
|
|
||||||
val styleName = "materials.${material.name}"
|
|
||||||
|
|
||||||
if (parent.physVolumes.isNotEmpty()) transparent()
|
|
||||||
|
|
||||||
registerAndUseStyle(styleName) {
|
|
||||||
val vfMaterial = SolidMaterial().apply {
|
|
||||||
configurePaint(material, solid)
|
|
||||||
}
|
|
||||||
MATERIAL_KEY put vfMaterial.toMeta()
|
|
||||||
"Gdml.material" put material.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private set
|
|
||||||
|
|
||||||
public fun configure(block: Solid.(parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial) -> Unit) {
|
|
||||||
val oldConfigure = configureSolid
|
|
||||||
configureSolid = { parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial ->
|
|
||||||
oldConfigure(parent, solid, material)
|
|
||||||
block(parent, solid, material)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
private val random: Random = Random(222)
|
|
||||||
|
|
||||||
private val colorCache = HashMap<GdmlMaterial, Int>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use random color and cache it based on the material. Meaning that colors are random, but always the same for the
|
|
||||||
* same material.
|
|
||||||
*/
|
|
||||||
public fun randomColor(material: GdmlMaterial): Int {
|
|
||||||
return colorCache.getOrPut(material) { random.nextInt(16777216) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|
||||||
//private val materialCache = HashMap<GdmlMaterial, Meta>()
|
//private val materialCache = HashMap<GdmlMaterial, Meta>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -303,12 +215,12 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
val first: GdmlSolid = solid.first.resolve(root) ?: error("")
|
val first: GdmlSolid = solid.first.resolve(root) ?: error("")
|
||||||
val second: GdmlSolid = solid.second.resolve(root) ?: error("")
|
val second: GdmlSolid = solid.second.resolve(root) ?: error("")
|
||||||
val type: CompositeType = when (solid) {
|
val type: CompositeType = when (solid) {
|
||||||
is GdmlUnion -> CompositeType.SUM // dumb sum for better performance
|
is GdmlUnion -> CompositeType.UNION // dumb sum for better performance
|
||||||
is GdmlSubtraction -> CompositeType.SUBTRACT
|
is GdmlSubtraction -> CompositeType.SUBTRACT
|
||||||
is GdmlIntersection -> CompositeType.INTERSECT
|
is GdmlIntersection -> CompositeType.INTERSECT
|
||||||
}
|
}
|
||||||
|
|
||||||
return composite(type, name) {
|
return smartComposite(type, name) {
|
||||||
addSolid(root, first).withPosition(
|
addSolid(root, first).withPosition(
|
||||||
solid.resolveFirstPosition(root),
|
solid.resolveFirstPosition(root),
|
||||||
solid.resolveFirstRotation(root),
|
solid.resolveFirstRotation(root),
|
||||||
@ -356,13 +268,13 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
): Solid? {
|
): Solid? {
|
||||||
require(name != "") { "Can't use empty solid name. Use null instead." }
|
require(name != "") { "Can't use empty solid name. Use null instead." }
|
||||||
return when (settings.solidAction(solid)) {
|
return when (settings.solidAction(solid)) {
|
||||||
GdmlTransformer.Action.ADD -> {
|
GdmlLoaderOptions.Action.ADD -> {
|
||||||
addSolid(root, solid, name)
|
addSolid(root, solid, name)
|
||||||
}
|
}
|
||||||
GdmlTransformer.Action.PROTOTYPE -> {
|
GdmlLoaderOptions.Action.PROTOTYPE -> {
|
||||||
proxySolid(root, this, solid, name ?: solid.name)
|
proxySolid(root, this, solid, name ?: solid.name)
|
||||||
}
|
}
|
||||||
GdmlTransformer.Action.REJECT -> {
|
GdmlLoaderOptions.Action.REJECT -> {
|
||||||
//ignore
|
//ignore
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -388,14 +300,14 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
when (settings.volumeAction(volume)) {
|
when (settings.volumeAction(volume)) {
|
||||||
GdmlTransformer.Action.ADD -> {
|
GdmlLoaderOptions.Action.ADD -> {
|
||||||
val group: SolidGroup = volume(root, volume)
|
val group: SolidGroup = volume(root, volume)
|
||||||
this[physVolume.name] = group.withPosition(root, physVolume)
|
this[physVolume.name] = group.withPosition(root, physVolume)
|
||||||
}
|
}
|
||||||
GdmlTransformer.Action.PROTOTYPE -> {
|
GdmlLoaderOptions.Action.PROTOTYPE -> {
|
||||||
proxyVolume(root, this, physVolume, volume)
|
proxyVolume(root, this, physVolume, volume)
|
||||||
}
|
}
|
||||||
GdmlTransformer.Action.REJECT -> {
|
GdmlLoaderOptions.Action.REJECT -> {
|
||||||
//ignore
|
//ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -460,16 +372,16 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public fun Gdml.toVision(block: GdmlTransformer.() -> Unit = {}): SolidGroup {
|
public fun Gdml.toVision(block: GdmlLoaderOptions.() -> Unit = {}): SolidGroup {
|
||||||
val settings = GdmlTransformer().apply(block)
|
val settings = GdmlLoaderOptions().apply(block)
|
||||||
val context = GdmlTransformerEnv(settings)
|
val context = GdmlLoader(settings)
|
||||||
return context.transform(this)
|
return context.transform(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append Gdml node to the group
|
* Append Gdml node to the group
|
||||||
*/
|
*/
|
||||||
public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlTransformer.() -> Unit = {}) {
|
public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlLoaderOptions.() -> Unit = {}) {
|
||||||
val visual = gdml.toVision(transformer)
|
val visual = gdml.toVision(transformer)
|
||||||
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
|
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
|
||||||
set(key, visual)
|
set(key, visual)
|
@ -9,7 +9,7 @@ public fun SolidGroup.gdml(
|
|||||||
file: Path,
|
file: Path,
|
||||||
key: String = "",
|
key: String = "",
|
||||||
usePreprocessor: Boolean = false,
|
usePreprocessor: Boolean = false,
|
||||||
transformer: GdmlTransformer.() -> Unit = {},
|
transformer: GdmlLoaderOptions.() -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val gdml = Gdml.decodeFromFile(file, usePreprocessor)
|
val gdml = Gdml.decodeFromFile(file, usePreprocessor)
|
||||||
gdml(gdml, key, transformer)
|
gdml(gdml, key, transformer)
|
||||||
|
@ -2,13 +2,15 @@ package space.kscience.visionforge.solid
|
|||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import space.kscience.dataforge.meta.isEmpty
|
||||||
import space.kscience.dataforge.meta.update
|
import space.kscience.dataforge.meta.update
|
||||||
import space.kscience.visionforge.VisionBuilder
|
import space.kscience.visionforge.VisionBuilder
|
||||||
import space.kscience.visionforge.VisionContainerBuilder
|
import space.kscience.visionforge.VisionContainerBuilder
|
||||||
|
import space.kscience.visionforge.VisionPropertyContainer
|
||||||
import space.kscience.visionforge.set
|
import space.kscience.visionforge.set
|
||||||
|
|
||||||
public enum class CompositeType {
|
public enum class CompositeType {
|
||||||
SUM, // Dumb sum of meshes
|
GROUP, // Dumb sum of meshes
|
||||||
UNION, //CSG union
|
UNION, //CSG union
|
||||||
INTERSECT,
|
INTERSECT,
|
||||||
SUBTRACT
|
SUBTRACT
|
||||||
@ -20,7 +22,7 @@ public class Composite(
|
|||||||
public val compositeType: CompositeType,
|
public val compositeType: CompositeType,
|
||||||
public val first: Solid,
|
public val first: Solid,
|
||||||
public val second: Solid,
|
public val second: Solid,
|
||||||
) : SolidBase(), Solid
|
) : SolidBase(), VisionPropertyContainer<Composite>
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public inline fun VisionContainerBuilder<Solid>.composite(
|
public inline fun VisionContainerBuilder<Solid>.composite(
|
||||||
@ -30,7 +32,9 @@ public inline fun VisionContainerBuilder<Solid>.composite(
|
|||||||
): Composite {
|
): Composite {
|
||||||
val group = SolidGroup().apply(builder)
|
val group = SolidGroup().apply(builder)
|
||||||
val children = group.children.values.filterIsInstance<Solid>()
|
val children = group.children.values.filterIsInstance<Solid>()
|
||||||
if (children.size != 2) error("Composite requires exactly two children")
|
if (children.size != 2){
|
||||||
|
error("Composite requires exactly two children, but found ${children.size}")
|
||||||
|
}
|
||||||
val res = Composite(type, children[0], children[1])
|
val res = Composite(type, children[0], children[1])
|
||||||
|
|
||||||
res.meta.update(group.meta)
|
res.meta.update(group.meta)
|
||||||
@ -49,6 +53,31 @@ public inline fun VisionContainerBuilder<Solid>.composite(
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A smart form of [Composite] that in case of [CompositeType.GROUP] creates a static group instead
|
||||||
|
*/
|
||||||
|
@VisionBuilder
|
||||||
|
public fun SolidGroup.smartComposite(
|
||||||
|
type: CompositeType,
|
||||||
|
name: String? = null,
|
||||||
|
builder: SolidGroup.() -> Unit,
|
||||||
|
): Solid = if (type == CompositeType.GROUP) {
|
||||||
|
val group = SolidGroup(builder)
|
||||||
|
if (name == null && group.meta.isEmpty()) {
|
||||||
|
//append directly to group if no properties are defined
|
||||||
|
group.children.forEach { (key, value) ->
|
||||||
|
value.parent = null
|
||||||
|
set(null, value)
|
||||||
|
}
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
set(name, group)
|
||||||
|
group
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
composite(type, name, builder)
|
||||||
|
}
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public inline fun VisionContainerBuilder<Solid>.union(
|
public inline fun VisionContainerBuilder<Solid>.union(
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
|
@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.visionforge.VisionBuilder
|
import space.kscience.visionforge.VisionBuilder
|
||||||
import space.kscience.visionforge.VisionContainerBuilder
|
import space.kscience.visionforge.VisionContainerBuilder
|
||||||
|
import space.kscience.visionforge.VisionPropertyContainer
|
||||||
import space.kscience.visionforge.set
|
import space.kscience.visionforge.set
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
@ -23,7 +24,7 @@ public class ConeSurface(
|
|||||||
public val topInnerRadius: Float,
|
public val topInnerRadius: Float,
|
||||||
public val startAngle: Float = 0f,
|
public val startAngle: Float = 0f,
|
||||||
public val angle: Float = PI2,
|
public val angle: Float = PI2,
|
||||||
) : SolidBase(), GeometrySolid {
|
) : SolidBase(), GeometrySolid, VisionPropertyContainer<ConeSurface> {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(bottomRadius > 0) { "Cone surface bottom radius must be positive" }
|
require(bottomRadius > 0) { "Cone surface bottom radius must be positive" }
|
||||||
|
@ -3,11 +3,12 @@ package space.kscience.visionforge.solid
|
|||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.visionforge.VisionContainerBuilder
|
import space.kscience.visionforge.VisionContainerBuilder
|
||||||
|
import space.kscience.visionforge.VisionPropertyContainer
|
||||||
import space.kscience.visionforge.set
|
import space.kscience.visionforge.set
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.convex")
|
@SerialName("solid.convex")
|
||||||
public class Convex(public val points: List<Point3D>) : SolidBase(), Solid
|
public class Convex(public val points: List<Point3D>) : SolidBase(), VisionPropertyContainer<Convex>
|
||||||
|
|
||||||
public inline fun VisionContainerBuilder<Solid>.convex(name: String? = null, action: ConvexBuilder.() -> Unit = {}): Convex =
|
public inline fun VisionContainerBuilder<Solid>.convex(name: String? = null, action: ConvexBuilder.() -> Unit = {}): Convex =
|
||||||
ConvexBuilder().apply(action).build().also { set(name, it) }
|
ConvexBuilder().apply(action).build().also { set(name, it) }
|
||||||
|
@ -57,8 +57,8 @@ public inline fun VisionContainerBuilder<Solid>.box(
|
|||||||
ySize: Number,
|
ySize: Number,
|
||||||
zSize: Number,
|
zSize: Number,
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
action: Box.() -> Unit = {},
|
block: Box.() -> Unit = {},
|
||||||
): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(action).also { set(name, it) }
|
): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(block).also { set(name, it) }
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.hexagon")
|
@SerialName("solid.hexagon")
|
||||||
|
@ -5,14 +5,11 @@ import kotlinx.serialization.Serializable
|
|||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.dataforge.names.plus
|
import space.kscience.dataforge.names.plus
|
||||||
import space.kscience.visionforge.VisionBuilder
|
import space.kscience.visionforge.*
|
||||||
import space.kscience.visionforge.VisionContainerBuilder
|
|
||||||
import space.kscience.visionforge.numberProperty
|
|
||||||
import space.kscience.visionforge.set
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.line")
|
@SerialName("solid.line")
|
||||||
public class PolyLine(public val points: List<Point3D>) : SolidBase(), Solid {
|
public class PolyLine(public val points: List<Point3D>) : SolidBase(), VisionPropertyContainer<PolyLine> {
|
||||||
|
|
||||||
//var lineType by string()
|
//var lineType by string()
|
||||||
public var thickness: Number by numberProperty(name = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) { 1.0 }
|
public var thickness: Number by numberProperty(name = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) { 1.0 }
|
||||||
|
@ -82,8 +82,8 @@ public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup {
|
|||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun VisionContainerBuilder<Vision>.group(
|
public fun VisionContainerBuilder<Vision>.group(
|
||||||
name: Name? = null,
|
name: Name? = null,
|
||||||
action: SolidGroup.() -> Unit = {},
|
builder: SolidGroup.() -> Unit = {},
|
||||||
): SolidGroup = SolidGroup().apply(action).also { set(name, it) }
|
): SolidGroup = SolidGroup().apply(builder).also { set(name, it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a group with given [name], attach it to this parent and return it.
|
* Define a group with given [name], attach it to this parent and return it.
|
||||||
|
@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.visionforge.VisionBuilder
|
import space.kscience.visionforge.VisionBuilder
|
||||||
import space.kscience.visionforge.VisionContainerBuilder
|
import space.kscience.visionforge.VisionContainerBuilder
|
||||||
|
import space.kscience.visionforge.VisionPropertyContainer
|
||||||
import space.kscience.visionforge.set
|
import space.kscience.visionforge.set
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -12,7 +13,7 @@ public class SolidLabel(
|
|||||||
public val text: String,
|
public val text: String,
|
||||||
public val fontSize: Double,
|
public val fontSize: Double,
|
||||||
public val fontFamily: String,
|
public val fontFamily: String,
|
||||||
) : SolidBase(), Solid
|
) : SolidBase(), VisionPropertyContainer<SolidLabel>
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun VisionContainerBuilder<Solid>.label(
|
public fun VisionContainerBuilder<Solid>.label(
|
||||||
|
@ -158,21 +158,28 @@ public fun SolidGroup.ref(
|
|||||||
name: String? = null,
|
name: String? = null,
|
||||||
): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) }
|
): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) }
|
||||||
|
|
||||||
/**
|
|
||||||
* Add new [SolidReferenceGroup] wrapping given object and automatically adding it to the prototypes
|
|
||||||
*/
|
|
||||||
public fun SolidGroup.ref(
|
public fun SolidGroup.ref(
|
||||||
name: String,
|
templateName: String,
|
||||||
|
name: String? = null,
|
||||||
|
): SolidReferenceGroup = ref(Name.parse(templateName), name)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add new [SolidReferenceGroup] wrapping given object and automatically adding it to the prototypes.
|
||||||
|
* One must ensure that [prototypeHolder] is a parent of this group.
|
||||||
|
*/
|
||||||
|
public fun SolidGroup.newRef(
|
||||||
|
name: String?,
|
||||||
obj: Solid,
|
obj: Solid,
|
||||||
templateName: Name = Name.parse(name),
|
prototypeHolder: PrototypeHolder = this,
|
||||||
|
templateName: Name = Name.parse(name ?: obj.toString()),
|
||||||
): SolidReferenceGroup {
|
): SolidReferenceGroup {
|
||||||
val existing = getPrototype(templateName)
|
val existing = getPrototype(templateName)
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
prototypes {
|
prototypeHolder.prototypes {
|
||||||
this[templateName] = obj
|
set(templateName, obj)
|
||||||
}
|
}
|
||||||
} else if (existing != obj) {
|
} else if (existing != obj) {
|
||||||
error("Can't add different prototype on top of existing one")
|
error("Can't add different prototype on top of existing one")
|
||||||
}
|
}
|
||||||
return this@ref.ref(templateName, name)
|
return ref(templateName, name)
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import kotlin.reflect.KClass
|
|||||||
|
|
||||||
public class Solids(meta: Meta) : VisionPlugin(meta) {
|
public class Solids(meta: Meta) : VisionPlugin(meta) {
|
||||||
override val tag: PluginTag get() = Companion.tag
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
override val visionSerializersModule: SerializersModule get() = serializersModuleForSolids
|
override val visionSerializersModule: SerializersModule get() = serializersModuleForSolids
|
||||||
|
|
||||||
public companion object : PluginFactory<Solids> {
|
public companion object : PluginFactory<Solids> {
|
||||||
|
@ -5,7 +5,10 @@ import kotlinx.serialization.Serializable
|
|||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
import kotlinx.serialization.encoding.Decoder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.MetaProvider
|
||||||
|
import space.kscience.dataforge.meta.float
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.visionforge.solid.Solid.Companion.X_KEY
|
import space.kscience.visionforge.solid.Solid.Companion.X_KEY
|
||||||
import space.kscience.visionforge.solid.Solid.Companion.Y_KEY
|
import space.kscience.visionforge.solid.Solid.Companion.Y_KEY
|
||||||
import space.kscience.visionforge.solid.Solid.Companion.Z_KEY
|
import space.kscience.visionforge.solid.Solid.Companion.Z_KEY
|
||||||
@ -54,7 +57,7 @@ internal object Point3DSerializer : KSerializer<Point3D> {
|
|||||||
override val descriptor: SerialDescriptor = Point3DImpl.serializer().descriptor
|
override val descriptor: SerialDescriptor = Point3DImpl.serializer().descriptor
|
||||||
|
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): Point3D = decoder.decodeSerializableValue(Point3DImpl.serializer())
|
override fun deserialize(decoder: Decoder): MutablePoint3D = decoder.decodeSerializableValue(Point3DImpl.serializer())
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: Point3D) {
|
override fun serialize(encoder: Encoder, value: Point3D) {
|
||||||
val impl: Point3DImpl = (value as? Point3DImpl) ?: Point3DImpl(value.x, value.y, value.z)
|
val impl: Point3DImpl = (value as? Point3DImpl) ?: Point3DImpl(value.x, value.y, value.z)
|
||||||
|
@ -16,7 +16,7 @@ fun SolidGroup.refGroup(
|
|||||||
block: MutableVisionGroup.() -> Unit
|
block: MutableVisionGroup.() -> Unit
|
||||||
): SolidReferenceGroup {
|
): SolidReferenceGroup {
|
||||||
val group = SolidGroup().apply(block)
|
val group = SolidGroup().apply(block)
|
||||||
return ref(name, group, templateName)
|
return newRef(name, group, templateName = templateName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class SerializationTest {
|
|||||||
z = -100
|
z = -100
|
||||||
}
|
}
|
||||||
val group = SolidGroup {
|
val group = SolidGroup {
|
||||||
ref("cube", cube)
|
newRef("cube", cube)
|
||||||
refGroup("pg", Name.parse("pg.content")) {
|
refGroup("pg", Name.parse("pg.content")) {
|
||||||
sphere(50) {
|
sphere(50) {
|
||||||
x = -100
|
x = -100
|
||||||
|
@ -14,7 +14,7 @@ class SolidReferenceTest {
|
|||||||
val theStyle by style {
|
val theStyle by style {
|
||||||
SolidMaterial.MATERIAL_COLOR_KEY put "red"
|
SolidMaterial.MATERIAL_COLOR_KEY put "red"
|
||||||
}
|
}
|
||||||
ref("test", Box(100f,100f,100f).apply {
|
newRef("test", Box(100f,100f,100f).apply {
|
||||||
color("blue")
|
color("blue")
|
||||||
useStyle(theStyle)
|
useStyle(theStyle)
|
||||||
})
|
})
|
||||||
|
@ -5,5 +5,5 @@ plugins {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(project(":visionforge-solid"))
|
api(project(":visionforge-solid"))
|
||||||
implementation(npm("three", "0.130.1"))
|
implementation(npm("three", "0.130.1"))
|
||||||
implementation(npm("three-csg-ts", "3.1.6"))
|
implementation(npm("three-csg-ts", "3.1.9"))
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ public class ThreeCanvas(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Clipping planes
|
//Clipping planes
|
||||||
options.useProperty(Canvas3DOptions::clipping){clipping ->
|
options.useProperty(Canvas3DOptions::clipping) { clipping ->
|
||||||
if (!clipping.meta.isEmpty()) {
|
if (!clipping.meta.isEmpty()) {
|
||||||
renderer.localClippingEnabled = true
|
renderer.localClippingEnabled = true
|
||||||
boundingBox?.let { boundingBox ->
|
boundingBox?.let { boundingBox ->
|
||||||
@ -192,7 +192,7 @@ public class ThreeCanvas(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options.useProperty(Canvas3DOptions::size){
|
options.useProperty(Canvas3DOptions::size) {
|
||||||
canvas.style.apply {
|
canvas.style.apply {
|
||||||
minWidth = "${options.size.minWith.toInt()}px"
|
minWidth = "${options.size.minWith.toInt()}px"
|
||||||
maxWidth = "${options.size.maxWith.toInt()}px"
|
maxWidth = "${options.size.maxWith.toInt()}px"
|
||||||
@ -273,18 +273,14 @@ public class ThreeCanvas(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this is Mesh) {
|
if (this is Mesh) {
|
||||||
if (highlight) {
|
val edges = getObjectByName(edgesName) ?: LineSegments(
|
||||||
val edges = LineSegments(
|
|
||||||
EdgesGeometry(geometry),
|
EdgesGeometry(geometry),
|
||||||
material
|
material
|
||||||
).apply {
|
).also {
|
||||||
name = edgesName
|
it.name = edgesName
|
||||||
}
|
add(it)
|
||||||
add(edges)
|
|
||||||
} else {
|
|
||||||
val highlightEdges = children.find { it.name == edgesName }
|
|
||||||
highlightEdges?.let { remove(it) }
|
|
||||||
}
|
}
|
||||||
|
edges.visible = highlight
|
||||||
} else {
|
} else {
|
||||||
children.filter { it.name != edgesName }.forEach {
|
children.filter { it.name != edgesName }.forEach {
|
||||||
it.toggleHighlight(highlight, edgesName, material)
|
it.toggleHighlight(highlight, edgesName, material)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package space.kscience.visionforge.solid.three
|
package space.kscience.visionforge.solid.three
|
||||||
|
|
||||||
import CSG
|
import CSG
|
||||||
import info.laht.threekt.core.Object3D
|
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
import space.kscience.dataforge.names.startsWith
|
import space.kscience.dataforge.names.startsWith
|
||||||
import space.kscience.visionforge.onPropertyChange
|
import space.kscience.visionforge.onPropertyChange
|
||||||
@ -38,11 +37,11 @@ public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory
|
|||||||
|
|
||||||
override val type: KClass<in Composite> get() = Composite::class
|
override val type: KClass<in Composite> get() = Composite::class
|
||||||
|
|
||||||
override fun invoke(three: ThreePlugin, obj: Composite): Object3D {
|
override fun invoke(three: ThreePlugin, obj: Composite): Mesh {
|
||||||
val first = three.buildObject3D(obj.first) as? Mesh ?: error("First part of composite is not a mesh")
|
val first = three.buildObject3D(obj.first) as? Mesh ?: error("First part of composite is not a mesh")
|
||||||
val second = three.buildObject3D(obj.second) as? Mesh ?: error("Second part of composite is not a mesh")
|
val second = three.buildObject3D(obj.second) as? Mesh ?: error("Second part of composite is not a mesh")
|
||||||
return when (obj.compositeType) {
|
return when (obj.compositeType) {
|
||||||
CompositeType.SUM, CompositeType.UNION -> CSG.union(first, second)
|
CompositeType.GROUP, CompositeType.UNION -> CSG.union(first, second)
|
||||||
CompositeType.INTERSECT -> CSG.intersect(first, second)
|
CompositeType.INTERSECT -> CSG.intersect(first, second)
|
||||||
CompositeType.SUBTRACT -> CSG.subtract(first, second)
|
CompositeType.SUBTRACT -> CSG.subtract(first, second)
|
||||||
}.apply {
|
}.apply {
|
||||||
|
Loading…
Reference in New Issue
Block a user