Implement cone surface as a partial fix for #39

This commit is contained in:
Alexander Nozik 2021-05-07 16:41:07 +03:00
parent 3a87e43483
commit ff80629f24
8 changed files with 82 additions and 47 deletions

View File

@ -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()
}
}
}

View File

@ -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")))
}

View File

@ -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")

View File

@ -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) }

View File

@ -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())

View File

@ -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 = {

View File

@ -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,

View File

@ -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
}