Fix cone segmenta and cone surface representation

This commit is contained in:
Alexander Nozik 2023-05-30 13:56:14 +03:00
parent 33778801b6
commit f6f74b54f6
4 changed files with 40 additions and 47 deletions

View File

@ -1,8 +1,8 @@
package space.kscience.visionforge.examples package space.kscience.visionforge.examples
import kotlin.math.PI
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import kotlin.math.PI
fun main() = makeVisionFile { fun main() = makeVisionFile {
vision("canvas") { vision("canvas") {
@ -30,7 +30,7 @@ fun main() = makeVisionFile{
} }
tube(30,20, 20){ cylinder(30,20, name = "cylinder"){
detail = 31 detail = 31
y = -220 y = -220
} }

View File

@ -22,60 +22,53 @@ public class ConeSegment(
public val phi: Float = PI2, public val phi: Float = PI2,
) : SolidBase<ConeSegment>(), GeometrySolid { ) : SolidBase<ConeSegment>(), GeometrySolid {
init { init {
require(bottomRadius > 0) { "Bottom radius must be positive" } require(bottomRadius > 0) { "Cone segment bottom radius must be positive" }
require(topRadius > 0) { "Top radius must be positive" } require(height > 0) { "Cone segment height must be positive" }
require(topRadius >= 0) { "Cone segment top radius must be non-negative" }
//require(startAngle >= 0)
require(phi in (0f..(PI2)))
} }
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val segments = detail ?: 32
val segments: Int = detail ?: 32 require(segments >= 4) { "The number of segments in cone is too small" }
require(segments >= 4) { "The number of segments in cone segment is too small" }
val angleStep = phi / (segments - 1) val angleStep = phi / (segments - 1)
/**
* Top and bottom shape
*/
fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i -> fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i ->
Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z) Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
} }
with(geometryBuilder) { geometryBuilder.apply {
// top and bottom faces
val bottomOuterPoints: List<Point3D> = shape(topRadius, -height / 2)
val upperOuterPoints: List<Point3D> = shape(bottomRadius, height / 2)
//creating shape in x-y plane with z = 0
val bottomPoints = shape(bottomRadius, -height / 2)
val topPoints = shape(topRadius, height / 2)
//outer face //outer face
for (it in 1 until segments) { for (it in 1 until segments) {
face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], upperOuterPoints[it], upperOuterPoints[it - 1]) face4(bottomPoints[it - 1], bottomPoints[it], topPoints[it], topPoints[it - 1])
} }
//if the cone is closed
if (phi == PI2) { if (phi == PI2) {
face4(bottomOuterPoints.last(), bottomOuterPoints[0], upperOuterPoints[0], upperOuterPoints.last()) face4(bottomPoints.last(), bottomPoints[0], topPoints[0], topPoints.last())
} }
//top and bottom cups
val zeroBottom = Point3D(0f, 0f, -height / 2) val zeroBottom = Point3D(0f, 0f, -height / 2)
val zeroTop = Point3D(0f, 0f, height / 2) val zeroTop = Point3D(0f, 0f, +height / 2)
for (it in 1 until segments) { for (it in 1 until segments) {
face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it]) face(bottomPoints[it - 1], zeroBottom, bottomPoints[it])
face(upperOuterPoints[it - 1], upperOuterPoints[it], zeroTop) face(topPoints[it - 1], topPoints[it], zeroTop)
} }
// closed surface
if (phi == PI2) { if (phi == PI2) {
face(bottomOuterPoints.last(), zeroBottom, bottomOuterPoints[0]) face(bottomPoints.last(), zeroBottom, bottomPoints[0])
face(upperOuterPoints.last(), upperOuterPoints[0], zeroTop) face(topPoints.last(), topPoints[0], zeroTop)
} else { } else {
face4(zeroTop, zeroBottom, bottomOuterPoints[0], upperOuterPoints[0]) face4(zeroTop, zeroBottom, bottomPoints[0], topPoints[0])
face4(zeroTop, zeroBottom, bottomOuterPoints.last(), upperOuterPoints.last()) face4(zeroTop, zeroBottom, bottomPoints.last(), topPoints.last())
} }
} }
} }
} }
@VisionBuilder @VisionBuilder

View File

@ -21,8 +21,8 @@ public class ConeSurface(
public val height: Float, public val height: Float,
public val topRadius: Float, public val topRadius: Float,
public val topInnerRadius: Float, public val topInnerRadius: Float,
public val startAngle: Float = 0f, public val phiStart: Float = 0f,
public val angle: Float = PI2, public val phi: Float = PI2,
) : SolidBase<ConeSurface>(), GeometrySolid { ) : SolidBase<ConeSurface>(), GeometrySolid {
init { init {
@ -30,16 +30,16 @@ public class ConeSurface(
require(height > 0) { "Cone surface height 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(bottomInnerRadius >= 0) { "Cone surface bottom inner radius must be non-negative" }
//require(startAngle >= 0) //require(startAngle >= 0)
require(angle in (0f..(PI2))) require(phi in (0f..(PI2)))
} }
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val segments = detail ?: 32 val segments = detail ?: 32
require(segments >= 4) { "The number of segments in tube is too small" } require(segments >= 4) { "The number of segments in tube is too small" }
val angleStep = angle / (segments - 1) val angleStep = phi / (segments - 1)
fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i -> fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i ->
Point3D(r * cos(startAngle + angleStep * i), r * sin(startAngle + angleStep * i), z) Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
} }
geometryBuilder.apply { geometryBuilder.apply {
@ -52,17 +52,17 @@ public class ConeSurface(
face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], topOuterPoints[it], topOuterPoints[it - 1]) face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], topOuterPoints[it], topOuterPoints[it - 1])
} }
if (angle == PI2) { if (phi == PI2) {
face4(bottomOuterPoints.last(), bottomOuterPoints[0], topOuterPoints[0], topOuterPoints.last()) face4(bottomOuterPoints.last(), bottomOuterPoints[0], topOuterPoints[0], topOuterPoints.last())
} }
if (bottomInnerRadius == 0f) { if (bottomInnerRadius == 0f) {
val zeroBottom = Point3D(0f, 0f, 0f) val zeroBottom = Point3D(0f, 0f, -height / 2)
val zeroTop = Point3D(0f, 0f, height) val zeroTop = Point3D(0f, 0f, height / 2)
(1 until segments).forEach { (1 until segments).forEach {
face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it]) face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it])
face(topOuterPoints[it - 1], topOuterPoints[it], zeroTop) face(topOuterPoints[it - 1], topOuterPoints[it], zeroTop)
} }
if (angle == PI2) { if (phi == PI2) {
face(bottomOuterPoints.last(), zeroBottom, bottomOuterPoints[0]) face(bottomOuterPoints.last(), zeroBottom, bottomOuterPoints[0])
face(topOuterPoints.last(), topOuterPoints[0], zeroTop) face(topOuterPoints.last(), topOuterPoints[0], zeroTop)
} else { } else {
@ -96,7 +96,7 @@ public class ConeSurface(
topOuterPoints[it] topOuterPoints[it]
) )
} }
if (angle == PI2) { if (phi == PI2) {
face4(bottomInnerPoints[0], bottomInnerPoints.last(), topInnerPoints.last(), topInnerPoints[0]) face4(bottomInnerPoints[0], bottomInnerPoints.last(), topInnerPoints.last(), topInnerPoints[0])
face4( face4(
bottomInnerPoints.last(), bottomInnerPoints.last(),
@ -135,8 +135,8 @@ public inline fun MutableVisionContainer<Solid>.tube(
height = height.toFloat(), height = height.toFloat(),
topRadius = radius.toFloat(), topRadius = radius.toFloat(),
topInnerRadius = innerRadius.toFloat(), topInnerRadius = innerRadius.toFloat(),
startAngle = startAngle.toFloat(), phiStart = startAngle.toFloat(),
angle = angle.toFloat() phi = angle.toFloat()
).apply(block).also { setChild(name, it) } ).apply(block).also { setChild(name, it) }
@VisionBuilder @VisionBuilder
@ -156,6 +156,6 @@ public inline fun MutableVisionContainer<Solid>.coneSurface(
height = height.toFloat(), height = height.toFloat(),
topRadius = topOuterRadius.toFloat(), topRadius = topOuterRadius.toFloat(),
topInnerRadius = topInnerRadius.toFloat(), topInnerRadius = topInnerRadius.toFloat(),
startAngle = startAngle.toFloat(), phiStart = startAngle.toFloat(),
angle = angle.toFloat() phi = angle.toFloat()
).apply(block).also { setChild(name, it) } ).apply(block).also { setChild(name, it) }

View File

@ -34,7 +34,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] = ThreeConeFactory // objectFactories[ConeSegment::class] = ThreeConeFactory
objectFactories[PolyLine::class] = ThreeSmartLineFactory objectFactories[PolyLine::class] = ThreeSmartLineFactory
objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory
objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory