diff --git a/build.gradle.kts b/build.gradle.kts index 20d1a1bd..1984e3d3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,7 @@ allprojects { } group = "space.kscience" - version = "0.2.0-dev-23" + version = "0.2.0-dev-24" } subprojects { diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/DObject.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/DObject.kt index e0933614..712e1a4e 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/DObject.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/DObject.kt @@ -15,7 +15,6 @@ public fun MetaProvider.doubleArray( it?.doubleArray ?: doubleArrayOf(*default) } - public class DObjectCache(private val cache: List, public val refStack: List = emptyList()) { public operator fun get(index: Int): Meta = cache[index] @@ -25,28 +24,6 @@ public class DObjectCache(private val cache: List, public val refStack: Li public val empty: DObjectCache = DObjectCache(emptyList(), emptyList()) } } -// -//public interface ObjectRef { -// public fun resolve(): T? -//} - -//public class ChildObjectRef( -// public val builder: (Meta, DObjectCache) -> T, -// public val refCache: DObjectCache, -// public val metaProvider: () -> Meta? -//) : ObjectRef { -// override fun resolve(): T? { -// val meta = metaProvider() ?: return null -// 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) -// } -//} public open class DObject(public val meta: Meta, private val refCache: DObjectCache) { diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/dRootToSolid.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/dRootToSolid.kt index 0fa40340..ed0b76fd 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/dRootToSolid.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/dRootToSolid.kt @@ -4,6 +4,7 @@ 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.* @@ -16,7 +17,16 @@ private operator fun Number.times(f: Float) = toFloat() * f private fun degToRad(d: Double) = d * PI / 180.0 -private class RootToSolidContext(val prototypeHolder: PrototypeHolder) +private class RootToSolidContext(val prototypeHolder: PrototypeHolder, val maxLayer: Int = 3) { + val layers: MutableList = 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) { @@ -67,157 +77,164 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) { private fun SolidGroup.addShape( shape: DGeoShape, context: RootToSolidContext, - name: String? = shape.fName.ifEmpty { null } -): Solid? = 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).also { - if (it == null) TODO() - it.useMatrix(node.fLeftMat) + 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}") } - addShape(node.fRight!!, context, null).also { - if (it == null) TODO() - it.useMatrix(node.fRightMat) - } - } - } - "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]) + smartComposite(compositeType, name = name) { + addShape(node.fLeft!!, context, null) { + this.useMatrix(node.fLeftMat) } - } - - (0 until fNz).forEach { index -> - layer( - fZ[index], - fX0[index], - fY0[index], - fScale[index] - ) - } + addShape(node.fRight!!, context, null) { + this.useMatrix(node.fRightMat) + } + }.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) + "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() - tube( - radius = fRmax, - height = fDz * 2, - innerRadius = fRmin, - name = name - ) - } - "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) + extruded(name = name) { + (0 until fNvert).forEach { index -> + shape { + point(fX[index], fY[index]) + } + } - tube( - radius = fRmax, - height = fDz * 2, - innerRadius = fRmin, - startAngle = degToRad(fPhi1), - angle = degToRad(fPhi2 - fPhi1), - name = name - ) - } - "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], + (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(fDphi), - name = name - ) { - z = (fZ[1] + fZ[0]) / 2 - } - } else { - TODO() + angle = degToRad(fPhi2 - fPhi1), + name = name, + block = block + ) } - } - "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() + "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 - val fNedges by shape.meta.int(1) + }.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 startphi = degToRad(fPhi1) - val deltaphi = degToRad(fDphi) + val fNedges by shape.meta.int(1) - 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)) + 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) + (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) } } - } - "TGeoShapeAssembly" -> { - val fVolume by shape.dObject(::DGeoVolume) - fVolume?.let { volume -> - addRootVolume(volume, context) + "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") } - } - "TGeoBBox" -> { - box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = name) - } - 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).apply { + addRootVolume(volume, context, obj.fName) { + if (context.bottomLayer > 0) { + this.layer = context.bottomLayer + } when (obj.typename) { "TGeoNodeMatrix" -> { val fMatrix by obj.dObject(::DGeoMatrix) - useMatrix(fMatrix) + this.useMatrix(fMatrix) } "TGeoNodeOffset" -> { val fOffset by obj.meta.double(0.0) @@ -227,19 +244,29 @@ private fun SolidGroup.addRootNode(obj: DGeoNode, context: RootToSolidContext) { } } -private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid { +private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid? { val group = SolidGroup { - if(volume.fNodes.isEmpty()) { + 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.children.size == 1 && group.meta.isEmpty()) { + 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 @@ -252,8 +279,15 @@ private fun SolidGroup.addRootVolume( volume: DGeoVolume, context: RootToSolidContext, name: String? = null, - cache: Boolean = true -): Solid { + 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) { @@ -262,23 +296,29 @@ private fun SolidGroup.addRootVolume( "${name}_${volume.fName}" } - return if (!cache) { - val group = buildVolume(volume, context) + 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) - group } else { val templateName = volumesName + volume.name val existing = getPrototype(templateName) if (existing == null) { context.prototypeHolder.prototypes { - set(templateName, buildVolume(volume, context)) + val group = buildVolume(volume, context) + set(templateName, group) } } - ref(templateName, name) - }.apply { - volume.fFillColor?.let { - meta[MATERIAL_COLOR_KEY] = RootColors[it] + ref(templateName, name).apply { + volume.fFillColor?.let { + meta[MATERIAL_COLOR_KEY] = RootColors[it] + } + block() } } } diff --git a/demo/muon-monitor/build.gradle.kts b/demo/muon-monitor/build.gradle.kts index 3818cada..a7bf2709 100644 --- a/demo/muon-monitor/build.gradle.kts +++ b/demo/muon-monitor/build.gradle.kts @@ -65,12 +65,6 @@ application { mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt") } -tasks.withType() { - kotlinOptions { - freeCompilerArgs = freeCompilerArgs + "-Xir-property-lazy-initialization" - } -} - //distributions { // main { // contents { diff --git a/demo/playground/src/jvmMain/kotlin/rootParser.kt b/demo/playground/src/jvmMain/kotlin/rootParser.kt index e8995009..16e1b3fe 100644 --- a/demo/playground/src/jvmMain/kotlin/rootParser.kt +++ b/demo/playground/src/jvmMain/kotlin/rootParser.kt @@ -9,6 +9,8 @@ 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 = sequence { @@ -37,12 +39,18 @@ fun main() { val solid = geo.toSolid() - //Paths.get("BM@N.vf.json").writeText(Solids.encodeToString(solid)) + Paths.get("BM@N.vf.json").writeText(Solids.encodeToString(solid)) //println(Solids.encodeToString(solid)) context.makeVisionFile { vision("canvas") { solid + } + } +} + + + /* SolidGroup { set( "Coil", @@ -99,6 +107,3 @@ fun main() { } }*//* }*/ - } - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 24f28924..a1def2dd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ kotlin.code.style=official kotlin.mpp.stability.nowarn=true #kotlin.jupyter.add.scanner=false -kotlin.incremental.js.klib=false +#kotlin.incremental.js.klib=false org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G org.gradle.parallel=true \ No newline at end of file diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt index 6be045d6..d746e14a 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt @@ -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 { diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt index 12fe243b..a8ad1dcd 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt @@ -63,7 +63,7 @@ public interface VisionGroup : Provider, Vision, VisionContainer { */ public operator fun VisionGroup.iterator(): Iterator = children.values.iterator() -public val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty() +public fun VisionGroup.isEmpty(): Boolean = this.children.isEmpty() public interface VisionContainerBuilder { //TODO add documentation diff --git a/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt b/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt index ffe35cc7..9837c60a 100644 --- a/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt +++ b/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt @@ -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)) { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Hexagon.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Hexagon.kt index baced452..7f42a3eb 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Hexagon.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Hexagon.kt @@ -57,8 +57,8 @@ public inline fun VisionContainerBuilder.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")