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 package space.kscience.visionforge.examples
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.gdml.Gdml import space.kscience.gdml.GdmlShowCase
import space.kscience.gdml.LUnit
import space.kscience.gdml.decodeFromStream
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
@ -14,9 +12,7 @@ fun main() {
} }
context.makeVisionFile(resourceLocation = ResourceLocation.EMBED) { context.makeVisionFile(resourceLocation = ResourceLocation.EMBED) {
vision("canvas") { vision("canvas") {
Gdml.decodeFromStream(javaClass.getResourceAsStream("/gdml/babyIAXO.gdml")!!, true).toVision { GdmlShowCase.babyIaxo().toVision()
lUnit = LUnit.MM
}
} }
} }
} }

View File

@ -187,12 +187,29 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
solid.deltaphi * aScale, solid.deltaphi * aScale,
name name
) )
is GdmlCone -> cone(solid.rmax1, solid.z, solid.rmax2, name = name) { is GdmlCone -> if (solid.rmin1.toDouble() == 0.0 && solid.rmin2.toDouble() == 0.0) {
require(solid.rmin1 == 0.0) { "Empty cones are not supported" } cone(
require(solid.rmin2 == 0.0) { "Empty cones are not supported" } bottomRadius = solid.rmax1,
height = solid.z,
upperRadius = solid.rmax2,
name = name
) {
startAngle = solid.startphi.toFloat() startAngle = solid.startphi.toFloat()
angle = solid.deltaphi.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) { is GdmlXtru -> extrude(name) {
shape { shape {
solid.vertices.forEach { solid.vertices.forEach {
@ -396,7 +413,8 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
return final 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 import kotlin.math.sin
/** /**
* A cylinder or cut cone segment * A solid cylinder or cut cone segment
*/ */
@Serializable @Serializable
@SerialName("solid.cone") @SerialName("solid.cone")

View File

@ -10,22 +10,24 @@ import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
/** /**
* Straight tube segment * A conical or cylindrical surface
*/ */
@Serializable @Serializable
@SerialName("solid.tube") @SerialName("solid.coneSurface")
public class Tube( public class ConeSurface(
public var radius: Float, public var bottomRadius: Float,
public var bottomInnerRadius: Float,
public var height: 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 startAngle: Float = 0f,
public var angle: Float = PI2, public var angle: Float = PI2,
) : SolidBase(), GeometrySolid { ) : SolidBase(), GeometrySolid {
init { init {
require(radius > 0) require(bottomRadius > 0) { "Cone surface bottom radius must be positive" }
require(height > 0) require(height > 0) { "Cone surface height must be positive" }
require(innerRadius >= 0) require(bottomInnerRadius >= 0) { "Cone surface bottom inner radius must be non-negative" }
//require(startAngle >= 0) //require(startAngle >= 0)
require(angle in (0f..(PI2))) require(angle in (0f..(PI2)))
} }
@ -44,8 +46,8 @@ public class Tube(
geometryBuilder.apply { geometryBuilder.apply {
//creating shape in x-y plane with z = 0 //creating shape in x-y plane with z = 0
val bottomOuterPoints = shape(radius, -height / 2) val bottomOuterPoints = shape(bottomRadius, -height / 2)
val upperOuterPoints = shape(radius, height / 2) val upperOuterPoints = shape(topRadius, height / 2)
//outer face //outer face
(1 until segments).forEach { (1 until segments).forEach {
face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], upperOuterPoints[it], upperOuterPoints[it - 1]) face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], upperOuterPoints[it], upperOuterPoints[it - 1])
@ -54,7 +56,7 @@ public class Tube(
if (angle == PI2) { if (angle == PI2) {
face4(bottomOuterPoints.last(), bottomOuterPoints[0], upperOuterPoints[0], upperOuterPoints.last()) face4(bottomOuterPoints.last(), bottomOuterPoints[0], upperOuterPoints[0], upperOuterPoints.last())
} }
if (innerRadius == 0f) { if (bottomInnerRadius == 0f) {
val zeroBottom = Point3D(0f, 0f, 0f) val zeroBottom = Point3D(0f, 0f, 0f)
val zeroTop = Point3D(0f, 0f, height) val zeroTop = Point3D(0f, 0f, height)
(1 until segments).forEach { (1 until segments).forEach {
@ -69,8 +71,8 @@ public class Tube(
face4(zeroTop, zeroBottom, bottomOuterPoints.last(), upperOuterPoints.last()) face4(zeroTop, zeroBottom, bottomOuterPoints.last(), upperOuterPoints.last())
} }
} else { } else {
val bottomInnerPoints = shape(innerRadius, -height / 2) val bottomInnerPoints = shape(bottomInnerRadius, -height / 2)
val upperInnerPoints = shape(innerRadius, height / 2) val upperInnerPoints = shape(topInnerRadius, height / 2)
//outer face //outer face
(1 until segments).forEach { (1 until segments).forEach {
// inner surface // inner surface
@ -116,24 +118,41 @@ public class Tube(
} }
} }
} }
} }
@VisionBuilder @VisionBuilder
public inline fun VisionContainerBuilder<Solid>.tube( public inline fun VisionContainerBuilder<Solid>.tube(
r: Number, r: Number,
height: Number, height: Number,
innerRadius: Number = 0f, innerRadius: Number,
startAngle: Number = 0f, startAngle: Number = 0f,
angle: Number = 2 * PI, angle: Number = 2 * PI,
name: String? = null, name: String? = null,
block: Tube.() -> Unit = {}, block: ConeSurface.() -> Unit = {},
): Tube = Tube( ): ConeSurface = ConeSurface(
r.toFloat(), bottomRadius = r.toFloat(),
height.toFloat(), bottomInnerRadius = innerRadius.toFloat(),
innerRadius.toFloat(), height = height.toFloat(),
startAngle.toFloat(), topRadius = r.toFloat(),
angle.toFloat() topInnerRadius = innerRadius.toFloat(),
).apply( startAngle = startAngle.toFloat(),
block angle = angle.toFloat()
).also { set(name, it) } ).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(SolidGroup.serializer())
subclass(SolidReferenceGroup.serializer()) subclass(SolidReferenceGroup.serializer())
subclass(Composite.serializer()) subclass(Composite.serializer())
subclass(Tube.serializer())
subclass(Box.serializer()) subclass(Box.serializer())
subclass(ConeSegment.serializer()) subclass(ConeSegment.serializer())
subclass(ConeSurface.serializer())
subclass(Convex.serializer()) subclass(Convex.serializer())
subclass(Extruded.serializer()) subclass(Extruded.serializer())
subclass(PolyLine.serializer()) subclass(PolyLine.serializer())

View File

@ -89,6 +89,7 @@ public class ThreeCanvas(
width = "100%" width = "100%"
height = "100%" height = "100%"
display = "block" display = "block"
zIndex = "1000"
} }
} }
@ -126,6 +127,7 @@ public class ThreeCanvas(
mousePosition.x = ((event.clientX - rect.left) / canvas.clientWidth) * 2 - 1 mousePosition.x = ((event.clientX - rect.left) / canvas.clientWidth) * 2 - 1
mousePosition.y = -((event.clientY - rect.top) / canvas.clientHeight) * 2 + 1 mousePosition.y = -((event.clientY - rect.top) / canvas.clientHeight) * 2 + 1
} }
event.preventDefault()
}, false) }, false)
canvas.onresize = { canvas.onresize = {

View File

@ -2,18 +2,18 @@ package space.kscience.visionforge.solid.three
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.CylinderBufferGeometry 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 space.kscience.visionforge.solid.detail
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.pow import kotlin.math.pow
public object ThreeCylinderFactory : MeshThreeFactory<ConeSegment>(ConeSegment::class) { public object ThreeCylinderFactory : MeshThreeFactory<ConeSurface>(ConeSurface::class) {
override fun buildGeometry(obj: ConeSegment): BufferGeometry { override fun buildGeometry(obj: ConeSurface): BufferGeometry {
val cylinder = obj.detail?.let { val cylinder = obj.detail?.let {
val segments = it.toDouble().pow(0.5).toInt() val segments = it.toDouble().pow(0.5).toInt()
CylinderBufferGeometry( CylinderBufferGeometry(
radiusTop = obj.upperRadius, radiusTop = obj.topRadius,
radiusBottom = obj.radius, radiusBottom = obj.bottomRadius,
height = obj.height, height = obj.height,
radialSegments = segments, radialSegments = segments,
heightSegments = segments, heightSegments = segments,
@ -22,8 +22,8 @@ public object ThreeCylinderFactory : MeshThreeFactory<ConeSegment>(ConeSegment::
thetaLength = obj.angle thetaLength = obj.angle
) )
} ?: CylinderBufferGeometry( } ?: CylinderBufferGeometry(
radiusTop = obj.upperRadius, radiusTop = obj.topRadius,
radiusBottom = obj.radius, radiusBottom = obj.bottomRadius,
height = obj.height, height = obj.height,
openEnded = false, openEnded = false,
thetaStart = obj.startAngle, thetaStart = obj.startAngle,

View File

@ -35,7 +35,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
objectFactories[Box::class] = ThreeBoxFactory objectFactories[Box::class] = ThreeBoxFactory
objectFactories[Convex::class] = ThreeConvexFactory objectFactories[Convex::class] = ThreeConvexFactory
objectFactories[Sphere::class] = ThreeSphereFactory objectFactories[Sphere::class] = ThreeSphereFactory
objectFactories[ConeSegment::class] = ThreeCylinderFactory objectFactories[ConeSurface::class] = ThreeCylinderFactory
objectFactories[PolyLine::class] = ThreeLineFactory objectFactories[PolyLine::class] = ThreeLineFactory
objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory
} }