Implement cone surface as a partial fix for #39
This commit is contained in:
parent
3a87e43483
commit
ff80629f24
@ -1,9 +1,7 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.gdml.LUnit
|
||||
import space.kscience.gdml.decodeFromStream
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
@ -14,9 +12,7 @@ fun main() {
|
||||
}
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.EMBED) {
|
||||
vision("canvas") {
|
||||
Gdml.decodeFromStream(javaClass.getResourceAsStream("/gdml/babyIAXO.gdml")!!, true).toVision {
|
||||
lUnit = LUnit.MM
|
||||
}
|
||||
GdmlShowCase.babyIaxo().toVision()
|
||||
}
|
||||
}
|
||||
}
|
@ -187,11 +187,28 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
|
||||
solid.deltaphi * aScale,
|
||||
name
|
||||
)
|
||||
is GdmlCone -> cone(solid.rmax1, solid.z, solid.rmax2, name = name) {
|
||||
require(solid.rmin1 == 0.0) { "Empty cones are not supported" }
|
||||
require(solid.rmin2 == 0.0) { "Empty cones are not supported" }
|
||||
startAngle = solid.startphi.toFloat()
|
||||
angle = solid.deltaphi.toFloat()
|
||||
is GdmlCone -> if (solid.rmin1.toDouble() == 0.0 && solid.rmin2.toDouble() == 0.0) {
|
||||
cone(
|
||||
bottomRadius = solid.rmax1,
|
||||
height = solid.z,
|
||||
upperRadius = solid.rmax2,
|
||||
name = name
|
||||
) {
|
||||
startAngle = solid.startphi.toFloat()
|
||||
angle = solid.deltaphi.toFloat()
|
||||
}
|
||||
} else {
|
||||
coneSurface(
|
||||
bottomOuterRadius = solid.rmax1,
|
||||
bottomInnerRadius = solid.rmin1,
|
||||
height = solid.z,
|
||||
topOuterRadius = solid.rmax2,
|
||||
topInnerRadius = solid.rmin2,
|
||||
name = name
|
||||
) {
|
||||
startAngle = solid.startphi.toFloat()
|
||||
angle = solid.deltaphi.toFloat()
|
||||
}
|
||||
}
|
||||
is GdmlXtru -> extrude(name) {
|
||||
shape {
|
||||
@ -396,7 +413,8 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
|
||||
return final
|
||||
}
|
||||
|
||||
fun transform(root: Gdml): SolidGroup = finalize(volume(root, root.world.resolve(root) ?: error("GDML root is not resolved")))
|
||||
fun transform(root: Gdml): SolidGroup =
|
||||
finalize(volume(root, root.world.resolve(root) ?: error("GDML root is not resolved")))
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* A cylinder or cut cone segment
|
||||
* A solid cylinder or cut cone segment
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("solid.cone")
|
||||
|
@ -10,22 +10,24 @@ import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* Straight tube segment
|
||||
* A conical or cylindrical surface
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("solid.tube")
|
||||
public class Tube(
|
||||
public var radius: Float,
|
||||
@SerialName("solid.coneSurface")
|
||||
public class ConeSurface(
|
||||
public var bottomRadius: Float,
|
||||
public var bottomInnerRadius: Float,
|
||||
public var height: Float,
|
||||
public var innerRadius: Float = 0f,
|
||||
public var topRadius: Float,
|
||||
public var topInnerRadius: Float,
|
||||
public var startAngle: Float = 0f,
|
||||
public var angle: Float = PI2,
|
||||
) : SolidBase(), GeometrySolid {
|
||||
|
||||
init {
|
||||
require(radius > 0)
|
||||
require(height > 0)
|
||||
require(innerRadius >= 0)
|
||||
require(bottomRadius > 0) { "Cone surface bottom radius must be positive" }
|
||||
require(height > 0) { "Cone surface height must be positive" }
|
||||
require(bottomInnerRadius >= 0) { "Cone surface bottom inner radius must be non-negative" }
|
||||
//require(startAngle >= 0)
|
||||
require(angle in (0f..(PI2)))
|
||||
}
|
||||
@ -44,8 +46,8 @@ public class Tube(
|
||||
geometryBuilder.apply {
|
||||
|
||||
//creating shape in x-y plane with z = 0
|
||||
val bottomOuterPoints = shape(radius, -height / 2)
|
||||
val upperOuterPoints = shape(radius, height / 2)
|
||||
val bottomOuterPoints = shape(bottomRadius, -height / 2)
|
||||
val upperOuterPoints = shape(topRadius, height / 2)
|
||||
//outer face
|
||||
(1 until segments).forEach {
|
||||
face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], upperOuterPoints[it], upperOuterPoints[it - 1])
|
||||
@ -54,7 +56,7 @@ public class Tube(
|
||||
if (angle == PI2) {
|
||||
face4(bottomOuterPoints.last(), bottomOuterPoints[0], upperOuterPoints[0], upperOuterPoints.last())
|
||||
}
|
||||
if (innerRadius == 0f) {
|
||||
if (bottomInnerRadius == 0f) {
|
||||
val zeroBottom = Point3D(0f, 0f, 0f)
|
||||
val zeroTop = Point3D(0f, 0f, height)
|
||||
(1 until segments).forEach {
|
||||
@ -69,8 +71,8 @@ public class Tube(
|
||||
face4(zeroTop, zeroBottom, bottomOuterPoints.last(), upperOuterPoints.last())
|
||||
}
|
||||
} else {
|
||||
val bottomInnerPoints = shape(innerRadius, -height / 2)
|
||||
val upperInnerPoints = shape(innerRadius, height / 2)
|
||||
val bottomInnerPoints = shape(bottomInnerRadius, -height / 2)
|
||||
val upperInnerPoints = shape(topInnerRadius, height / 2)
|
||||
//outer face
|
||||
(1 until segments).forEach {
|
||||
// inner surface
|
||||
@ -116,24 +118,41 @@ public class Tube(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun VisionContainerBuilder<Solid>.tube(
|
||||
r: Number,
|
||||
height: Number,
|
||||
innerRadius: Number = 0f,
|
||||
innerRadius: Number,
|
||||
startAngle: Number = 0f,
|
||||
angle: Number = 2 * PI,
|
||||
name: String? = null,
|
||||
block: Tube.() -> Unit = {},
|
||||
): Tube = Tube(
|
||||
r.toFloat(),
|
||||
height.toFloat(),
|
||||
innerRadius.toFloat(),
|
||||
startAngle.toFloat(),
|
||||
angle.toFloat()
|
||||
).apply(
|
||||
block
|
||||
).also { set(name, it) }
|
||||
block: ConeSurface.() -> Unit = {},
|
||||
): ConeSurface = ConeSurface(
|
||||
bottomRadius = r.toFloat(),
|
||||
bottomInnerRadius = innerRadius.toFloat(),
|
||||
height = height.toFloat(),
|
||||
topRadius = r.toFloat(),
|
||||
topInnerRadius = innerRadius.toFloat(),
|
||||
startAngle = startAngle.toFloat(),
|
||||
angle = angle.toFloat()
|
||||
).apply(block).also { set(name, it) }
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun VisionContainerBuilder<Solid>.coneSurface(
|
||||
bottomOuterRadius: Number,
|
||||
bottomInnerRadius: Number,
|
||||
height: Number,
|
||||
topOuterRadius: Number,
|
||||
topInnerRadius: Number,
|
||||
name: String? = null,
|
||||
block: ConeSurface.() -> Unit = {},
|
||||
): ConeSurface = ConeSurface(
|
||||
bottomRadius = bottomOuterRadius.toFloat(),
|
||||
bottomInnerRadius = bottomInnerRadius.toFloat(),
|
||||
height = height.toFloat(),
|
||||
topRadius = topOuterRadius.toFloat(),
|
||||
topInnerRadius = topInnerRadius.toFloat(),
|
||||
).apply(block).also { set(name, it) }
|
@ -29,9 +29,9 @@ public class Solids(meta: Meta) : VisionPlugin(meta) {
|
||||
subclass(SolidGroup.serializer())
|
||||
subclass(SolidReferenceGroup.serializer())
|
||||
subclass(Composite.serializer())
|
||||
subclass(Tube.serializer())
|
||||
subclass(Box.serializer())
|
||||
subclass(ConeSegment.serializer())
|
||||
subclass(ConeSurface.serializer())
|
||||
subclass(Convex.serializer())
|
||||
subclass(Extruded.serializer())
|
||||
subclass(PolyLine.serializer())
|
||||
|
@ -89,6 +89,7 @@ public class ThreeCanvas(
|
||||
width = "100%"
|
||||
height = "100%"
|
||||
display = "block"
|
||||
zIndex = "1000"
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +127,7 @@ public class ThreeCanvas(
|
||||
mousePosition.x = ((event.clientX - rect.left) / canvas.clientWidth) * 2 - 1
|
||||
mousePosition.y = -((event.clientY - rect.top) / canvas.clientHeight) * 2 + 1
|
||||
}
|
||||
event.preventDefault()
|
||||
}, false)
|
||||
|
||||
canvas.onresize = {
|
||||
|
@ -2,18 +2,18 @@ package space.kscience.visionforge.solid.three
|
||||
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.geometries.CylinderBufferGeometry
|
||||
import space.kscience.visionforge.solid.ConeSegment
|
||||
import space.kscience.visionforge.solid.ConeSurface
|
||||
import space.kscience.visionforge.solid.detail
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.pow
|
||||
|
||||
public object ThreeCylinderFactory : MeshThreeFactory<ConeSegment>(ConeSegment::class) {
|
||||
override fun buildGeometry(obj: ConeSegment): BufferGeometry {
|
||||
public object ThreeCylinderFactory : MeshThreeFactory<ConeSurface>(ConeSurface::class) {
|
||||
override fun buildGeometry(obj: ConeSurface): BufferGeometry {
|
||||
val cylinder = obj.detail?.let {
|
||||
val segments = it.toDouble().pow(0.5).toInt()
|
||||
CylinderBufferGeometry(
|
||||
radiusTop = obj.upperRadius,
|
||||
radiusBottom = obj.radius,
|
||||
radiusTop = obj.topRadius,
|
||||
radiusBottom = obj.bottomRadius,
|
||||
height = obj.height,
|
||||
radialSegments = segments,
|
||||
heightSegments = segments,
|
||||
@ -22,8 +22,8 @@ public object ThreeCylinderFactory : MeshThreeFactory<ConeSegment>(ConeSegment::
|
||||
thetaLength = obj.angle
|
||||
)
|
||||
} ?: CylinderBufferGeometry(
|
||||
radiusTop = obj.upperRadius,
|
||||
radiusBottom = obj.radius,
|
||||
radiusTop = obj.topRadius,
|
||||
radiusBottom = obj.bottomRadius,
|
||||
height = obj.height,
|
||||
openEnded = false,
|
||||
thetaStart = obj.startAngle,
|
||||
|
@ -35,7 +35,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
objectFactories[Box::class] = ThreeBoxFactory
|
||||
objectFactories[Convex::class] = ThreeConvexFactory
|
||||
objectFactories[Sphere::class] = ThreeSphereFactory
|
||||
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
||||
objectFactories[ConeSurface::class] = ThreeCylinderFactory
|
||||
objectFactories[PolyLine::class] = ThreeLineFactory
|
||||
objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user