From 9356e34328a44a9e52d2191c4104c65c1dba62fd Mon Sep 17 00:00:00 2001 From: Igor Dunaev Date: Wed, 4 Oct 2023 13:43:38 +0300 Subject: [PATCH 1/3] Add CutTube class --- .../kscience/visionforge/solid/CutTube.kt | 154 ++++++++++++++++++ .../kscience/visionforge/solid/Solids.kt | 1 + 2 files changed, 155 insertions(+) create mode 100644 visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/CutTube.kt diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/CutTube.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/CutTube.kt new file mode 100644 index 00000000..e98f07bc --- /dev/null +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/CutTube.kt @@ -0,0 +1,154 @@ +package space.kscience.visionforge.solid + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import space.kscience.visionforge.MutableVisionContainer +import space.kscience.visionforge.VisionBuilder +import space.kscience.visionforge.setChild +import kotlin.math.abs +import kotlin.math.cos +import kotlin.math.sin + +/** + * A tube section cut with two planes. + * The centers of the two sections are positioned + * along the central axis of the tube, at the top + * and at the bottom. + * + * The default segment number is 32. + */ +@Serializable +@SerialName("solid.cutTube") +public class CutTube( + public val outerRadius: Float, + public val innerRadius: Float, + public val height: Float, + public val phiStart: Float = 0f, + public val phi: Float = PI2, + public val nTop: Float32Vector3D, + public val nBottom: Float32Vector3D, +) : SolidBase(), GeometrySolid { + + init { + require(innerRadius >= 0) { "Tube inner radius must be non-negative" } + require(outerRadius >= 0) { "Tube outer radius must not be less than its inner radius" } + require(height >= 0) { "Tube height must be non-negative" } + //TODO implement more "subtle" section checks + require(abs(nTop.z) > 1e-5) { "Top section is almost vertical" } + require(abs(nBottom.z) > 1e-5) { "Bottom section is almost vertical" } + //require(phiStart >= 0) + require(phi in (0f..(PI2))) + } + + override fun toGeometry(geometryBuilder: GeometryBuilder) { + val segments = detail ?: 32 + require(segments >= 4) { "The number of segments in the tube is too small" } + val angleStep = phi / (segments - 1) + + fun section(r: Float, z: Float, n: Float32Vector3D): List = (0 until segments).map { i -> + val x = r * cos(phiStart + angleStep * i) + val y = r * sin(phiStart + angleStep * i) + Float32Vector3D(x, y, (n.z * z - n.x * x - n.y * y) / n.z) + } + + geometryBuilder.apply { + //creating top and bottom sections of the tube: + //shapes in planes defined with (0, 0, +-z) points and normal vectors + //TODO check for an intersection of the sections + val topOuterPoints = section(outerRadius, height / 2, nTop) + val bottomOuterPoints = section(outerRadius, -height / 2, nBottom) + + + //outer face + for (it in 1 until segments) { + face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], topOuterPoints[it], topOuterPoints[it - 1]) + } + if (phi == PI2) { + face4(bottomOuterPoints.last(), bottomOuterPoints[0], topOuterPoints[0], topOuterPoints.last()) + } + + if (innerRadius == 0f) { + val zeroBottom = Float32Vector3D(0f, 0f, -height / 2) + val zeroTop = Float32Vector3D(0f, 0f, height / 2) + (1 until segments).forEach { + face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it]) + face(topOuterPoints[it - 1], topOuterPoints[it], zeroTop) + } + if (phi == PI2) { + face(bottomOuterPoints.last(), zeroBottom, bottomOuterPoints[0]) + face(topOuterPoints.last(), topOuterPoints[0], zeroTop) + } else { + face4(zeroTop, zeroBottom, bottomOuterPoints[0], topOuterPoints[0]) + face4(zeroTop, zeroBottom, bottomOuterPoints.last(), topOuterPoints.last()) + } + } else { + val topInnerPoints = section(innerRadius, height / 2, nTop) + val bottomInnerPoints = section(innerRadius, -height / 2, nBottom) + // inner face + (1 until segments).forEach { + // inner surface + face4( + bottomInnerPoints[it], + bottomInnerPoints[it - 1], + topInnerPoints[it - 1], + topInnerPoints[it] + ) + //bottom cup + face4( + bottomInnerPoints[it - 1], + bottomInnerPoints[it], + bottomOuterPoints[it], + bottomOuterPoints[it - 1] + ) + //upper cup + face4( + topInnerPoints[it], + topInnerPoints[it - 1], + topOuterPoints[it - 1], + topOuterPoints[it] + ) + } + if (phi == PI2) { + face4(bottomInnerPoints[0], bottomInnerPoints.last(), topInnerPoints.last(), topInnerPoints[0]) + face4( + bottomInnerPoints.last(), + bottomInnerPoints[0], + bottomOuterPoints[0], + bottomOuterPoints.last() + ) + face4(topInnerPoints[0], topInnerPoints.last(), topOuterPoints.last(), topOuterPoints[0]) + } else { + face4(bottomInnerPoints[0], bottomOuterPoints[0], topOuterPoints[0], topInnerPoints[0]) + face4( + bottomOuterPoints.last(), + bottomInnerPoints.last(), + topInnerPoints.last(), + topOuterPoints.last() + ) + } + } + } + } +} + + +@VisionBuilder +public inline fun MutableVisionContainer.cutTube( + outerRadius: Number, + innerRadius: Number, + height: Number, + startAngle: Number = 0f, + angle: Number = PI2, + topNormal: Float32Vector3D, + bottomNormal: Float32Vector3D, + name: String? = null, + block: CutTube.() -> Unit = {}, +): CutTube = CutTube( + outerRadius = outerRadius.toFloat(), + innerRadius = innerRadius.toFloat(), + height = height.toFloat(), + phiStart = startAngle.toFloat(), + phi = angle.toFloat(), + nTop = topNormal, + nBottom = bottomNormal +).apply(block).also { setChild(name, it) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt index 1477c2a0..0ad9bdeb 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt @@ -45,6 +45,7 @@ public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer Date: Sun, 8 Oct 2023 15:16:32 +0300 Subject: [PATCH 2/3] Add ROOT to Solid conversion for TGeoCone and TGeoCTub shapes --- .../kotlin/ru/mipt/npm/root/dRootToSolid.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) 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 c81ca577..e19b2786 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 @@ -269,6 +269,44 @@ private fun SolidGroup.addShape( } } + "TGeoCone" -> { + val fDz by shape.meta.double(0.0) + val fRmin1 by shape.meta.double(0.0) + val fRmax1 by shape.meta.double(0.0) + val fRmin2 by shape.meta.double(0.0) + val fRmax2 by shape.meta.double(0.0) + + coneSurface( + bottomOuterRadius = fRmax1, + bottomInnerRadius = fRmin1, + height = fDz * 2.0, + topOuterRadius = fRmax2, + topInnerRadius = fRmin2, + name = name, + ) + } + + "TGeoCtub" -> { + val fRmin by shape.meta.double(0.0) + val fRmax by shape.meta.double(0.0) + val fDz by shape.meta.double(0.0) + val fPhi1 by shape.meta.double(0.0) + val fPhi2 by shape.meta.double(PI2.toDouble()) + val fNlow by shape.meta.doubleArray() + val fNhigh by shape.meta.doubleArray() + + cutTube( + outerRadius = fRmax, + innerRadius = fRmin, + height = fDz * 2.0, + startAngle = degToRad(fPhi1), + angle = degToRad(fPhi2 - fPhi1), + topNormal = Float32Vector3D(fNhigh[0], fNhigh[1], fNhigh[2]), + bottomNormal = Float32Vector3D(fNlow[0], fNlow[1], fNlow[2]), + name = name, + ) + } + else -> { TODO("A shape with type ${shape.typename} not implemented") } From 15b7dbd057cd5e16931145739813b9369a712a21 Mon Sep 17 00:00:00 2001 From: Igor Dunaev Date: Sun, 8 Oct 2023 17:09:18 +0300 Subject: [PATCH 3/3] Add documentation for parameters --- .../space/kscience/visionforge/solid/CutTube.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/CutTube.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/CutTube.kt index e98f07bc..cc0ef614 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/CutTube.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/CutTube.kt @@ -132,6 +132,20 @@ public class CutTube( } +/** + * Create a cut tube - a hollow cylinder (or a segment) cut with two planes. The axis of the cylinder + * is the Z axis. Each section is define by a center and a normal vector. The centers of the two sections + * are the points (0, 0, height / 2) and (0, 0, -height / 2). + * + * @param outerRadius outer radius of the tube + * @param innerRadius inner radius if the tube + * @param height height (length) of the tube. Essentially this is the length of the Z axis segment, + * contained between the top and bottom sections of the tube + * @param startAngle start angle of the segment (if this is a segment) + * @param angle angular magnitude of the segment + * @param topNormal normal vector of the top section + * @param bottomNormal normal vector of the bottom section + */ @VisionBuilder public inline fun MutableVisionContainer.cutTube( outerRadius: Number,