0.2.0 #71
@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.project")
|
||||
// kotlin("multiplatform") version "1.5.30-RC" apply false
|
||||
// kotlin("js") version "1.5.30-RC" apply false
|
||||
kotlin("multiplatform") version "1.5.30" apply false
|
||||
kotlin("js") version "1.5.30" apply false
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.5.1")
|
||||
@ -16,7 +16,7 @@ allprojects {
|
||||
}
|
||||
|
||||
group = "space.kscience"
|
||||
version = "0.2.0-dev-23"
|
||||
version = "0.2.0-dev-24"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
@ -36,7 +36,8 @@ apiValidation {
|
||||
ignoredPackages.add("info.laht.threekt")
|
||||
}
|
||||
|
||||
|
||||
//workaround for https://youtrack.jetbrains.com/issue/KT-48273
|
||||
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() {
|
||||
private val context = Context {
|
||||
plugin(FX3DPlugin)
|
||||
plugin(VisionManager)
|
||||
}
|
||||
|
||||
private val fx3d = context.fetch(FX3DPlugin)
|
||||
|
@ -6,6 +6,7 @@ import react.RProps
|
||||
import react.child
|
||||
import react.functionalComponent
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.plotly.layout
|
||||
import space.kscience.plotly.models.Trace
|
||||
import space.kscience.visionforge.markup.VisionOfMarkup
|
||||
import space.kscience.visionforge.react.flexRow
|
||||
@ -91,7 +92,10 @@ val GravityDemo = functionalComponent<DemoProps> { props ->
|
||||
height = 50.vh - 50.pt
|
||||
}
|
||||
plotly {
|
||||
traces(velocityTrace)
|
||||
traces(velocityTrace,energyTrace)
|
||||
layout {
|
||||
xaxis.title = "time"
|
||||
}
|
||||
}
|
||||
Markup {
|
||||
attrs {
|
||||
|
@ -32,7 +32,7 @@ val Markup = functionalComponent<MarkupProps>("Markup") { props ->
|
||||
//TODO add new formats via plugins
|
||||
else -> error("Format ${vision.format} not recognized")
|
||||
}
|
||||
vision.useProperty(VisionOfMarkup::content) { content ->
|
||||
vision.useProperty(VisionOfMarkup::content) { content: String? ->
|
||||
element.clear()
|
||||
element.append {
|
||||
markdown(flavour) { content ?: "" }
|
||||
|
@ -6,7 +6,6 @@ import kotlinx.html.script
|
||||
import kotlinx.html.stream.createHTML
|
||||
import kotlinx.html.unsafe
|
||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary
|
||||
import org.jetbrains.kotlinx.jupyter.api.libraries.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
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.visionManager
|
||||
|
||||
@JupyterLibrary
|
||||
@DFExperimental
|
||||
public class VisionForgePlayGroundForJupyter : JupyterIntegration() {
|
||||
|
||||
|
@ -65,12 +65,6 @@ application {
|
||||
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 {
|
||||
// main {
|
||||
// contents {
|
||||
|
@ -55,6 +55,7 @@ kotlin {
|
||||
api(project(":visionforge-gdml"))
|
||||
api(project(":visionforge-plotly"))
|
||||
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.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.mpp.stability.nowarn=true
|
||||
kotlin.native.enableDependencyPropagation=false
|
||||
kapt.use.worker.api=false
|
||||
kapt.incremental.apt=false
|
||||
|
||||
kotlin.jupyter.add.scanner=false
|
||||
|
||||
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G
|
||||
org.gradle.parallel=true
|
@ -57,3 +57,7 @@ kscience {
|
||||
readme {
|
||||
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 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.resources
|
||||
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.visionManager
|
||||
|
||||
@JupyterLibrary
|
||||
@DFExperimental
|
||||
internal class GdmlForJupyter : JupyterIntegration() {
|
||||
|
||||
|
@ -32,6 +32,7 @@ include(
|
||||
":visionforge-threejs",
|
||||
":visionforge-threejs:visionforge-threejs-server",
|
||||
":visionforge-gdml",
|
||||
":cern-root-loader",
|
||||
":visionforge-server",
|
||||
":visionforge-plotly",
|
||||
":visionforge-markdown",
|
||||
|
@ -83,7 +83,7 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
|
||||
}
|
||||
obj.children.entries
|
||||
.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) ->
|
||||
styledDiv {
|
||||
css {
|
||||
|
@ -13,9 +13,7 @@ import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.isEmpty
|
||||
import space.kscience.dataforge.names.length
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionGroup
|
||||
import space.kscience.visionforge.computeProperties
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.react.ThreeCanvasComponent
|
||||
import space.kscience.visionforge.react.flexColumn
|
||||
import space.kscience.visionforge.react.flexRow
|
||||
@ -85,7 +83,9 @@ public val ThreeCanvasWithControls: FunctionComponent<ThreeCanvasWithControlsPro
|
||||
|
||||
useEffect {
|
||||
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 val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty()
|
||||
public fun VisionGroup.isEmpty(): Boolean = this.children.isEmpty()
|
||||
|
||||
public interface VisionContainerBuilder<in V : Vision> {
|
||||
//TODO add documentation
|
||||
|
@ -76,7 +76,7 @@ public open class VisionGroupBase(
|
||||
* Set parent for given child and attach it
|
||||
*/
|
||||
private fun attachChild(token: NameToken, child: Vision?) {
|
||||
val before = children[token]
|
||||
val before = childrenInternal[token]
|
||||
when {
|
||||
child == null -> {
|
||||
childrenInternal.remove(token)
|
||||
|
@ -67,8 +67,8 @@ internal fun checkOrStoreFile(htmlPath: Path, filePath: Path, resource: String):
|
||||
if (!skip) {
|
||||
logger.debug("File $fullPath does not exist or wrong checksum. Writing file")
|
||||
Files.createDirectories(fullPath.parent)
|
||||
Files.write(fullPath, bytes, StandardOpenOption.CREATE, StandardOpenOption.WRITE)
|
||||
Files.write(md5File, checksum.encodeToByteArray(), StandardOpenOption.CREATE, StandardOpenOption.WRITE)
|
||||
Files.write(fullPath, bytes, StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)
|
||||
Files.write(md5File, checksum.encodeToByteArray(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)
|
||||
}
|
||||
|
||||
return if (htmlPath.isAbsolute && fullPath.startsWith(htmlPath.parent)) {
|
||||
|
@ -27,6 +27,8 @@ import kotlin.reflect.KClass
|
||||
public class FX3DPlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
public val solids: Solids by require(Solids)
|
||||
|
||||
private val objectFactories = HashMap<KClass<out Solid>, FX3DFactory<*>>()
|
||||
private val compositeFactory = FXCompositeFactory(this)
|
||||
private val referenceFactory = FXReferenceFactory(this)
|
||||
@ -50,6 +52,7 @@ public class FX3DPlugin : AbstractPlugin() {
|
||||
is SolidGroup -> {
|
||||
Group(obj.children.mapNotNull { (token, obj) ->
|
||||
(obj as? Solid)?.let {
|
||||
logger.info { token.toString() }
|
||||
buildNode(it).apply {
|
||||
properties["name"] = token.toString()
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class FXCompositeFactory(public val plugin: FX3DPlugin) : FX3DFactory<Com
|
||||
val firstCSG = first.toCSG()
|
||||
val secondCSG = second.toCSG()
|
||||
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.SUBTRACT -> firstCSG.difference(secondCSG)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
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
|
||||
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
@ -11,10 +9,8 @@ import space.kscience.gdml.*
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.html.VisionOutput
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
private val solidsName = "solids".asName()
|
||||
private val volumesName = "volumes".asName()
|
||||
@ -25,91 +21,7 @@ private inline operator fun Number.times(d: Double) = toDouble() * d
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline operator fun Number.times(f: Float) = toFloat() * f
|
||||
|
||||
public class GdmlTransformer {
|
||||
|
||||
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 class GdmlLoader(val settings: GdmlLoaderOptions) {
|
||||
//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 second: GdmlSolid = solid.second.resolve(root) ?: error("")
|
||||
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 GdmlIntersection -> CompositeType.INTERSECT
|
||||
}
|
||||
|
||||
return composite(type, name) {
|
||||
return smartComposite(type, name) {
|
||||
addSolid(root, first).withPosition(
|
||||
solid.resolveFirstPosition(root),
|
||||
solid.resolveFirstRotation(root),
|
||||
@ -356,13 +268,13 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
||||
): Solid? {
|
||||
require(name != "") { "Can't use empty solid name. Use null instead." }
|
||||
return when (settings.solidAction(solid)) {
|
||||
GdmlTransformer.Action.ADD -> {
|
||||
GdmlLoaderOptions.Action.ADD -> {
|
||||
addSolid(root, solid, name)
|
||||
}
|
||||
GdmlTransformer.Action.PROTOTYPE -> {
|
||||
GdmlLoaderOptions.Action.PROTOTYPE -> {
|
||||
proxySolid(root, this, solid, name ?: solid.name)
|
||||
}
|
||||
GdmlTransformer.Action.REJECT -> {
|
||||
GdmlLoaderOptions.Action.REJECT -> {
|
||||
//ignore
|
||||
null
|
||||
}
|
||||
@ -388,14 +300,14 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
||||
}
|
||||
|
||||
when (settings.volumeAction(volume)) {
|
||||
GdmlTransformer.Action.ADD -> {
|
||||
GdmlLoaderOptions.Action.ADD -> {
|
||||
val group: SolidGroup = volume(root, volume)
|
||||
this[physVolume.name] = group.withPosition(root, physVolume)
|
||||
}
|
||||
GdmlTransformer.Action.PROTOTYPE -> {
|
||||
GdmlLoaderOptions.Action.PROTOTYPE -> {
|
||||
proxyVolume(root, this, physVolume, volume)
|
||||
}
|
||||
GdmlTransformer.Action.REJECT -> {
|
||||
GdmlLoaderOptions.Action.REJECT -> {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
@ -460,16 +372,16 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
||||
}
|
||||
|
||||
|
||||
public fun Gdml.toVision(block: GdmlTransformer.() -> Unit = {}): SolidGroup {
|
||||
val settings = GdmlTransformer().apply(block)
|
||||
val context = GdmlTransformerEnv(settings)
|
||||
public fun Gdml.toVision(block: GdmlLoaderOptions.() -> Unit = {}): SolidGroup {
|
||||
val settings = GdmlLoaderOptions().apply(block)
|
||||
val context = GdmlLoader(settings)
|
||||
return context.transform(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
|
||||
set(key, visual)
|
@ -9,7 +9,7 @@ public fun SolidGroup.gdml(
|
||||
file: Path,
|
||||
key: String = "",
|
||||
usePreprocessor: Boolean = false,
|
||||
transformer: GdmlTransformer.() -> Unit = {},
|
||||
transformer: GdmlLoaderOptions.() -> Unit = {},
|
||||
) {
|
||||
val gdml = Gdml.decodeFromFile(file, usePreprocessor)
|
||||
gdml(gdml, key, transformer)
|
||||
|
@ -2,13 +2,15 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.isEmpty
|
||||
import space.kscience.dataforge.meta.update
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.VisionContainerBuilder
|
||||
import space.kscience.visionforge.VisionPropertyContainer
|
||||
import space.kscience.visionforge.set
|
||||
|
||||
public enum class CompositeType {
|
||||
SUM, // Dumb sum of meshes
|
||||
GROUP, // Dumb sum of meshes
|
||||
UNION, //CSG union
|
||||
INTERSECT,
|
||||
SUBTRACT
|
||||
@ -20,7 +22,7 @@ public class Composite(
|
||||
public val compositeType: CompositeType,
|
||||
public val first: Solid,
|
||||
public val second: Solid,
|
||||
) : SolidBase(), Solid
|
||||
) : SolidBase(), VisionPropertyContainer<Composite>
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun VisionContainerBuilder<Solid>.composite(
|
||||
@ -30,7 +32,9 @@ public inline fun VisionContainerBuilder<Solid>.composite(
|
||||
): Composite {
|
||||
val group = SolidGroup().apply(builder)
|
||||
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])
|
||||
|
||||
res.meta.update(group.meta)
|
||||
@ -49,6 +53,31 @@ public inline fun VisionContainerBuilder<Solid>.composite(
|
||||
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
|
||||
public inline fun VisionContainerBuilder<Solid>.union(
|
||||
name: String? = null,
|
||||
|
@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.VisionContainerBuilder
|
||||
import space.kscience.visionforge.VisionPropertyContainer
|
||||
import space.kscience.visionforge.set
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
@ -23,7 +24,7 @@ public class ConeSurface(
|
||||
public val topInnerRadius: Float,
|
||||
public val startAngle: Float = 0f,
|
||||
public val angle: Float = PI2,
|
||||
) : SolidBase(), GeometrySolid {
|
||||
) : SolidBase(), GeometrySolid, VisionPropertyContainer<ConeSurface> {
|
||||
|
||||
init {
|
||||
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.Serializable
|
||||
import space.kscience.visionforge.VisionContainerBuilder
|
||||
import space.kscience.visionforge.VisionPropertyContainer
|
||||
import space.kscience.visionforge.set
|
||||
|
||||
@Serializable
|
||||
@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 =
|
||||
ConvexBuilder().apply(action).build().also { set(name, it) }
|
||||
|
@ -57,8 +57,8 @@ public inline fun VisionContainerBuilder<Solid>.box(
|
||||
ySize: Number,
|
||||
zSize: Number,
|
||||
name: String? = null,
|
||||
action: Box.() -> Unit = {},
|
||||
): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(action).also { set(name, it) }
|
||||
block: Box.() -> Unit = {},
|
||||
): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(block).also { set(name, it) }
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.hexagon")
|
||||
|
@ -5,14 +5,11 @@ import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.VisionContainerBuilder
|
||||
import space.kscience.visionforge.numberProperty
|
||||
import space.kscience.visionforge.set
|
||||
import space.kscience.visionforge.*
|
||||
|
||||
@Serializable
|
||||
@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()
|
||||
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
|
||||
public fun VisionContainerBuilder<Vision>.group(
|
||||
name: Name? = null,
|
||||
action: SolidGroup.() -> Unit = {},
|
||||
): SolidGroup = SolidGroup().apply(action).also { set(name, it) }
|
||||
builder: SolidGroup.() -> Unit = {},
|
||||
): SolidGroup = SolidGroup().apply(builder).also { set(name, 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 space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.VisionContainerBuilder
|
||||
import space.kscience.visionforge.VisionPropertyContainer
|
||||
import space.kscience.visionforge.set
|
||||
|
||||
@Serializable
|
||||
@ -12,7 +13,7 @@ public class SolidLabel(
|
||||
public val text: String,
|
||||
public val fontSize: Double,
|
||||
public val fontFamily: String,
|
||||
) : SolidBase(), Solid
|
||||
) : SolidBase(), VisionPropertyContainer<SolidLabel>
|
||||
|
||||
@VisionBuilder
|
||||
public fun VisionContainerBuilder<Solid>.label(
|
||||
|
@ -158,21 +158,28 @@ public fun SolidGroup.ref(
|
||||
name: String? = null,
|
||||
): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) }
|
||||
|
||||
/**
|
||||
* Add new [SolidReferenceGroup] wrapping given object and automatically adding it to the prototypes
|
||||
*/
|
||||
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,
|
||||
templateName: Name = Name.parse(name),
|
||||
prototypeHolder: PrototypeHolder = this,
|
||||
templateName: Name = Name.parse(name ?: obj.toString()),
|
||||
): SolidReferenceGroup {
|
||||
val existing = getPrototype(templateName)
|
||||
if (existing == null) {
|
||||
prototypes {
|
||||
this[templateName] = obj
|
||||
prototypeHolder.prototypes {
|
||||
set(templateName, obj)
|
||||
}
|
||||
} else if (existing != obj) {
|
||||
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) {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
override val visionSerializersModule: SerializersModule get() = serializersModuleForSolids
|
||||
|
||||
public companion object : PluginFactory<Solids> {
|
||||
|
@ -5,7 +5,10 @@ import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
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.Y_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 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) {
|
||||
val impl: Point3DImpl = (value as? Point3DImpl) ?: Point3DImpl(value.x, value.y, value.z)
|
||||
|
@ -16,7 +16,7 @@ fun SolidGroup.refGroup(
|
||||
block: MutableVisionGroup.() -> Unit
|
||||
): SolidReferenceGroup {
|
||||
val group = SolidGroup().apply(block)
|
||||
return ref(name, group, templateName)
|
||||
return newRef(name, group, templateName = templateName)
|
||||
}
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ class SerializationTest {
|
||||
z = -100
|
||||
}
|
||||
val group = SolidGroup {
|
||||
ref("cube", cube)
|
||||
newRef("cube", cube)
|
||||
refGroup("pg", Name.parse("pg.content")) {
|
||||
sphere(50) {
|
||||
x = -100
|
||||
|
@ -14,7 +14,7 @@ class SolidReferenceTest {
|
||||
val theStyle by style {
|
||||
SolidMaterial.MATERIAL_COLOR_KEY put "red"
|
||||
}
|
||||
ref("test", Box(100f,100f,100f).apply {
|
||||
newRef("test", Box(100f,100f,100f).apply {
|
||||
color("blue")
|
||||
useStyle(theStyle)
|
||||
})
|
||||
|
@ -5,5 +5,5 @@ plugins {
|
||||
dependencies {
|
||||
api(project(":visionforge-solid"))
|
||||
implementation(npm("three", "0.130.1"))
|
||||
implementation(npm("three-csg-ts", "3.1.6"))
|
||||
implementation(npm("three-csg-ts", "3.1.9"))
|
||||
}
|
||||
|
@ -273,18 +273,14 @@ public class ThreeCanvas(
|
||||
return
|
||||
}
|
||||
if (this is Mesh) {
|
||||
if (highlight) {
|
||||
val edges = LineSegments(
|
||||
val edges = getObjectByName(edgesName) ?: LineSegments(
|
||||
EdgesGeometry(geometry),
|
||||
material
|
||||
).apply {
|
||||
name = edgesName
|
||||
}
|
||||
add(edges)
|
||||
} else {
|
||||
val highlightEdges = children.find { it.name == edgesName }
|
||||
highlightEdges?.let { remove(it) }
|
||||
).also {
|
||||
it.name = edgesName
|
||||
add(it)
|
||||
}
|
||||
edges.visible = highlight
|
||||
} else {
|
||||
children.filter { it.name != edgesName }.forEach {
|
||||
it.toggleHighlight(highlight, edgesName, material)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.visionforge.solid.three
|
||||
|
||||
import CSG
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import space.kscience.dataforge.names.startsWith
|
||||
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 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 second = three.buildObject3D(obj.second) as? Mesh ?: error("Second part of composite is not a mesh")
|
||||
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.SUBTRACT -> CSG.subtract(first, second)
|
||||
}.apply {
|
||||
|
Loading…
Reference in New Issue
Block a user