Merge branch 'dev' into beta/1.9.20

This commit is contained in:
Alexander Nozik 2023-10-04 08:53:55 +03:00
commit d44f25ad4c
14 changed files with 14978 additions and 76 deletions

View File

@ -0,0 +1,53 @@
package ru.mipt.npm.root
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import space.kscience.visionforge.solid.Float32Vector3D
@Serializable
public data class FairTrackParam(
val fX: Double,
val fY: Double,
val fZ: Double,
val fTx: Double,
val fTy: Double,
val fQp: Double,
)
public fun FairTrackParam.toVector(): Float32Vector3D = Float32Vector3D(fX,fY,fZ)
@Serializable
public data class CbmStsTrack(
val fParamFirst: FairTrackParam,
val fParamLast: FairTrackParam,
)
@Serializable
public data class BmnGlobalTrack(
val fParamFirst: FairTrackParam,
val fParamLast: FairTrackParam,
)
public class BmnEventContainer(
public val cbmTracks: List<CbmStsTrack>,
public val bmnGlobalTracks: List<BmnGlobalTrack>,
)
public object BMN {
public val json: Json = Json {
ignoreUnknownKeys = true
classDiscriminator = "_typename"
}
public fun readEventJson(string: String): BmnEventContainer {
val jsonArray = json.parseToJsonElement(string) as JsonArray
val cbmTracks: List<CbmStsTrack> =
json.decodeFromJsonElement(ListSerializer(CbmStsTrack.serializer()), jsonArray[0])
val bmnGlobalTracks: List<BmnGlobalTrack> =
json.decodeFromJsonElement(ListSerializer(BmnGlobalTrack.serializer()), jsonArray[1])
return BmnEventContainer(cbmTracks, bmnGlobalTracks)
}
}

View File

@ -42,7 +42,7 @@ public open class DObject(public val meta: Meta, public val refCache: DObjectCac
}
internal fun <T : DObject> tObjectArray(
builder: (Meta, DObjectCache) -> T
builder: (Meta, DObjectCache) -> T,
): ReadOnlyProperty<Any?, List<T>> = ReadOnlyProperty { _, property ->
meta.getIndexed(Name.of(property.name, "arr")).values.mapNotNull {
resolve(builder, it)
@ -51,9 +51,9 @@ public open class DObject(public val meta: Meta, public val refCache: DObjectCac
internal fun <T : DObject> dObject(
builder: (Meta, DObjectCache) -> T,
key: Name? = null
key: Name? = null,
): ReadOnlyProperty<Any?, T?> = ReadOnlyProperty { _, property ->
meta[key ?: property.name.asName()]?.let { resolve(builder, it) }
meta[key ?: property.name.asName()]?.takeIf { it.value != Null }?.let { resolve(builder, it) }
}
}
@ -62,8 +62,7 @@ public open class DNamed(meta: Meta, refCache: DObjectCache) : DObject(meta, ref
public val fTitle: String by meta.string("")
}
public class DGeoMaterial(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
}
public class DGeoMaterial(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache)
public class DGeoMedium(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
public val fMaterial: DGeoMaterial? by dObject(::DGeoMaterial)
@ -90,27 +89,69 @@ public class DGeoNode(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCach
public val fVolume: DGeoVolume? by dObject(::DGeoVolume)
}
public open class DGeoMatrix(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache)
public sealed class DGeoMatrix(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache)
public open class DGeoScale(meta: Meta, refCache: DObjectCache) : DGeoMatrix(meta, refCache) {
public class DGeoIdentity(meta: Meta, refCache: DObjectCache) : DGeoMatrix(meta, refCache)
public class DGeoScale(meta: Meta, refCache: DObjectCache) : DGeoMatrix(meta, refCache) {
public val fScale: DoubleArray by meta.doubleArray(1.0, 1.0, 1.0)
public val x: Double get() = fScale[0]
public val y: Double get() = fScale[1]
public val z: Double get() = fScale[2]
}
public class DGeoRotation(meta: Meta, refCache: DObjectCache) : DGeoMatrix(meta, refCache) {
public val fRotationMatrix: DoubleArray by meta.doubleArray()
}
public class DGeoTranslation(meta: Meta, refCache: DObjectCache) : DGeoMatrix(meta, refCache) {
public val fTranslation: DoubleArray by meta.doubleArray()
}
public open class DGeoCombiTrans(meta: Meta, refCache: DObjectCache) : DGeoMatrix(meta, refCache) {
public val fRotation: DGeoRotation? by dObject(::DGeoRotation)
public val fTranslation: DoubleArray by meta.doubleArray()
}
public class DGeoGenTrans(meta: Meta, refCache: DObjectCache) : DGeoCombiTrans(meta, refCache) {
public val fScale: DoubleArray by meta.doubleArray()
}
public class DGeoHMatrix(meta: Meta, refCache: DObjectCache) : DGeoMatrix(meta, refCache) {
public val fRotation: DGeoRotation? by dObject(::DGeoRotation)
public val fTranslation: DoubleArray by meta.doubleArray()
public val fScale: DoubleArray by meta.doubleArray()
}
/**
* Create a specialized version of [DGeoMatrix]
*/
internal fun dGeoMatrix(
meta: Meta,
refCache: DObjectCache,
): DGeoMatrix = when (val typename = meta["_typename"].string) {
null -> error("Type name is undefined")
"TGeoIdentity" -> DGeoIdentity(meta, refCache)
"TGeoScale" -> DGeoScale(meta, refCache)
"TGeoRotation" -> DGeoRotation(meta, refCache)
"TGeoTranslation" -> DGeoTranslation(meta, refCache)
"TGeoCombiTrans" -> DGeoCombiTrans(meta, refCache)
"TGeoGenTrans" -> DGeoGenTrans(meta, refCache)
"TGeoHMatrix" -> DGeoHMatrix(meta, refCache)
else -> error("$typename is not a member of TGeoMatrix")
}
public class DGeoBoolNode(meta: Meta, refCache: DObjectCache) : DObject(meta, refCache) {
public val fLeft: DGeoShape? by dObject(::DGeoShape)
public val fLeftMat: DGeoMatrix? by dObject(::DGeoMatrix)
public val fLeftMat: DGeoMatrix? by dObject(::dGeoMatrix)
public val fRight: DGeoShape? by dObject(::DGeoShape)
public val fRightMat: DGeoMatrix? by dObject(::DGeoMatrix)
public val fRightMat: DGeoMatrix? by dObject(::dGeoMatrix)
}
public class DGeoManager(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
public val fMatrices: List<DGeoMatrix> by tObjectArray(::DGeoMatrix)
public val fMatrices: List<DGeoMatrix> by tObjectArray(::dGeoMatrix)
public val fShapes: List<DGeoShape> by tObjectArray(::DGeoShape)

View File

@ -1,6 +1,8 @@
package ru.mipt.npm.root
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.names.plus
@ -44,39 +46,32 @@ private fun Solid.translate(trans: DoubleArray) {
position = Float32Vector3D(x, y, z)
}
private fun Solid.useMatrix(matrix: DGeoMatrix?) {
if (matrix == null) return
when (matrix.typename) {
"TGeoIdentity" -> {
//do nothing
private fun Solid.scale(s: DoubleArray) {
scale = Float32Vector3D(s[0], s[1], s[2])
}
private fun Solid.useMatrix(matrix: DGeoMatrix?): Unit {
when (matrix) {
null -> {}
is DGeoIdentity -> {}
is DGeoTranslation -> translate(matrix.fTranslation)
is DGeoRotation -> rotate(matrix.fRotationMatrix)
is DGeoScale -> scale(matrix.fScale)
is DGeoGenTrans -> {
translate(matrix.fTranslation)
matrix.fRotation?.fRotationMatrix?.let { rotate(it) }
scale(matrix.fScale)
}
"TGeoTranslation" -> {
val fTranslation by matrix.meta.doubleArray()
translate(fTranslation)
is DGeoCombiTrans -> {
translate(matrix.fTranslation)
matrix.fRotation?.fRotationMatrix?.let { rotate(it) }
}
"TGeoRotation" -> {
val fRotationMatrix by matrix.meta.doubleArray()
rotate(fRotationMatrix)
}
"TGeoCombiTrans" -> {
val fTranslation by matrix.meta.doubleArray()
translate(fTranslation)
matrix.meta["fRotation.fRotationMatrix"]?.value?.let {
rotate(it.doubleArray)
}
}
"TGeoHMatrix" -> {
val fTranslation by matrix.meta.doubleArray()
val fRotationMatrix by matrix.meta.doubleArray()
val fScale by matrix.meta.doubleArray()
translate(fTranslation)
rotate(fRotationMatrix)
scale = Float32Vector3D(fScale[0], fScale[1], fScale[2])
is DGeoHMatrix -> {
translate(matrix.fTranslation)
matrix.fRotation?.fRotationMatrix?.let { rotate(it) }
scale(matrix.fScale)
}
}
}
@ -99,10 +94,10 @@ private fun SolidGroup.addShape(
}
smartComposite(compositeType, name = name) {
addShape(node.fLeft!!, context, null) {
this.useMatrix(node.fLeftMat)
useMatrix(node.fLeftMat)
}
addShape(node.fRight!!, context, null) {
this.useMatrix(node.fRightMat)
useMatrix(node.fRightMat)
}
}.apply(block)
}
@ -118,8 +113,8 @@ private fun SolidGroup.addShape(
val fScale by shape.meta.doubleArray()
extruded(name = name) {
(0 until fNvert).forEach { index ->
shape {
shape {
(0 until fNvert).forEach { index ->
point(fX[index], fY[index])
}
}
@ -186,10 +181,9 @@ private fun SolidGroup.addShape(
name = name,
) {
z = (fZ[1] + fZ[0]) / 2
}.apply(block)
} else {
TODO()
TODO("Polycone is not implemented")
}
}
@ -286,7 +280,7 @@ private fun SolidGroup.addRootNode(obj: DGeoNode, context: RootToSolidContext) {
addRootVolume(volume, context, obj.fName) {
when (obj.typename) {
"TGeoNodeMatrix" -> {
val fMatrix by obj.dObject(::DGeoMatrix)
val fMatrix by obj.dObject(::dGeoMatrix)
this.useMatrix(fMatrix)
}

View File

@ -1,18 +1,17 @@
package space.kscience.visionforge.examples
import ru.mipt.npm.root.BMN
import ru.mipt.npm.root.DGeoManager
import ru.mipt.npm.root.rootGeo
import ru.mipt.npm.root.serialization.TGeoManager
import ru.mipt.npm.root.toVector
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.isLeaf
import space.kscience.dataforge.meta.string
import space.kscience.visionforge.Colors
import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.invoke
import space.kscience.visionforge.solid.solid
import space.kscience.visionforge.solid.*
import java.util.zip.ZipInputStream
import kotlin.io.path.Path
import kotlin.io.path.writeText
@ -39,6 +38,8 @@ fun main() {
println(it)
}
val events = BMN.readEventJson(TGeoManager::class.java.getResourceAsStream("/root/event_0.json")!!.bufferedReader().readText())
makeVisionFile(path = Path("data/output.html"), resourceLocation = ResourceLocation.EMBED) {
vision("canvas") {
requirePlugin(Solids)
@ -49,6 +50,30 @@ fun main() {
rootGeo(geo,"BM@N", ignoreRootColors = true).also {
Path("data/BM@N.vf.json").writeText(Solids.encodeToString(it))
}
solidGroup("cbmStsTracks") {
events.cbmTracks.forEach { track ->
polyline(
track.fParamFirst.toVector(),
track.fParamLast.toVector()
) {
thickness = 2.0
color(Colors.blue)
}
}
}
solidGroup("bmnGlobalTracks") {
events.bmnGlobalTracks.forEach { track ->
polyline(
track.fParamFirst.toVector(),
track.fParamLast.toVector()
) {
thickness = 2.0
color(Colors.red)
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,215 @@
[
[{
"_typename" : "CbmStsTrack",
"fUniqueID" : 0,
"fBits" : 0,
"ststrk" : 2112745000,
"fStsHits" : [216, 221, 5, 50, 63, 82, 133],
"fMvdHits" : [],
"fPidHypo" : 211,
"fParamFirst" : {
"_typename" : "FairTrackParam",
"fUniqueID" : 0,
"fBits" : 0,
"fX" : -4.79052305221558,
"fY" : 8.83165168762207,
"fZ" : 36.5065002441406,
"fTx" : -0.11185921728611,
"fTy" : 0.228091076016426,
"fQp" : 0.893607378005981,
"fCovMatrix" : [1.68777798535302e-4, -9.69577522482723e-4, -1.07744517663377e-5, 1.2614247680176e-5, 2.87587154161884e-5, 0.0403835587203503, 5.90448107686825e-5, -5.16064348630607e-4, -2.05383723368868e-4, 2.20405854634009e-6, -8.30146461794357e-7, -5.49759215573431e-6, 1.16227884063846e-5, 3.6013934732182e-6, 1.46196922287345e-4]
},
"fParamLast" : {
"_typename" : "FairTrackParam",
"fUniqueID" : 0,
"fBits" : 0,
"fX" : 4.67626953125,
"fY" : 43.7959594726562,
"fZ" : 190.759826660156,
"fTx" : 0.240885242819786,
"fTy" : 0.230351597070694,
"fQp" : 0.896391570568085,
"fCovMatrix" : [0.00152156152762473, 0.00325645040720701, 4.73157851956785e-5, 4.51812447863631e-5, 1.32256536744535e-4, 0.0311740729957819, 1.29416745039634e-4, 3.94528120523319e-4, 3.28425812767819e-4, 3.75195077140233e-6, 1.75166894678114e-6, 9.62230569712119e-6, 9.89728778222343e-6, 4.38717188444571e-6, 1.46559861605056e-4]
},
"fFlag" : 0,
"fChi2" : 3.59655165672302,
"fNDF" : 9,
"fB" : 0,
"fnEv" : 0,
"fHitsArr" : {
"_typename" : "TClonesArray",
"name" : "CbmStsHits",
"arr" : []
}
}, {
"_typename" : "CbmStsTrack",
"fUniqueID" : 0,
"fBits" : 0,
"ststrk" : 0,
"fStsHits" : [79, 108, 136, 160],
"fMvdHits" : [],
"fPidHypo" : 211,
"fParamFirst" : {
"_typename" : "FairTrackParam",
"fUniqueID" : 0,
"fBits" : 0,
"fX" : 7.56474113464355,
"fY" : -6.31765508651733,
"fZ" : 123.474571228027,
"fTx" : 0.198165953159332,
"fTy" : -0.0599287338554859,
"fQp" : 0.826108694076538,
"fCovMatrix" : [0.00192060391418636, -0.00543995574116707, -7.47249359847046e-5, 9.40304598771036e-5, 4.32362634455785e-4, 0.0452092550694942, 2.69763870164752e-4, -7.19496863894165e-4, -0.00178805936593562, 5.47440777154407e-6, -4.97666542287334e-6, -3.32396593876183e-5, 1.67156704264926e-5, 3.69572808267549e-5, 3.62657097866759e-4]
},
"fParamLast" : {
"_typename" : "FairTrackParam",
"fUniqueID" : 0,
"fBits" : 0,
"fX" : 38.4175720214844,
"fY" : -12.5351896286011,
"fZ" : 222.266052246094,
"fTx" : 0.428264498710632,
"fTy" : -0.0652719661593437,
"fQp" : 0.827384233474731,
"fCovMatrix" : [0.00191446917597204, 0.00539331184700131, 7.50113686081022e-5, 9.10265880520456e-5, 4.29905543569475e-4, 0.0449054278433323, 2.68147414317355e-4, 7.0479983696714e-4, 0.00177141127642244, 5.60895068701939e-6, 4.76898094348144e-6, 3.39166836056393e-5, 1.61108710017288e-5, 3.51894050254487e-5, 3.57192009687424e-4]
},
"fFlag" : 0,
"fChi2" : 0.0889237225055695,
"fNDF" : 3,
"fB" : 0,
"fnEv" : 0,
"fHitsArr" : {
"_typename" : "TClonesArray",
"name" : "CbmStsHits",
"arr" : []
}
}]
,
[{
"_typename" : "BmnGlobalTrack",
"fUniqueID" : 0,
"fBits" : 0,
"fHits" : [],
"fParamFirst" : {
"_typename" : "FairTrackParam",
"fUniqueID" : 0,
"fBits" : 0,
"fX" : 0.514997541904449,
"fY" : 0.614071667194366,
"fZ" : 0.716018855571747,
"fTx" : -0.182437181472778,
"fTy" : 0.231170654296875,
"fQp" : 0.890645802021027,
"fCovMatrix" : [0.00640107085928321, -0.00549036636948586, -2.40077963098884e-4, 5.9050194977317e-5, 4.4034980237484e-4, 0.0939982086420059, 1.37060953420587e-4, -0.0010447750100866, -3.45796346664429e-4, 1.4494138667942e-5, -1.88720639471285e-6, -1.71545361808967e-5, 2.24065261136275e-5, 4.27886925535859e-6, 1.46344274980947e-4]
},
"fParamLast" : {
"_typename" : "FairTrackParam",
"fUniqueID" : 0,
"fBits" : 0,
"fX" : 4.67626953125,
"fY" : 43.7959594726562,
"fZ" : 190.759826660156,
"fTx" : 0.240885242819786,
"fTy" : 0.230351597070694,
"fQp" : 0.896391570568085,
"fCovMatrix" : [0.00152156152762473, 0.00325645040720701, 4.73157851956785e-5, 4.51812447863631e-5, 1.32256536744535e-4, 0.0311740729957819, 1.29416745039634e-4, 3.94528120523319e-4, 3.28425812767819e-4, 3.75195077140233e-6, 1.75166894678114e-6, 9.62230569712119e-6, 9.89728778222343e-6, 4.38717188444571e-6, 1.46559861605056e-4]
},
"fFlag" : -1,
"fChi2" : 3.596552,
"fNDF" : 9,
"fB" : 0,
"fLength" : 196.4224,
"fNhits" : 7,
"fUsing" : false,
"fGemTrack" : 0,
"fSsdTrack" : -1,
"fSilTrack" : -1,
"fTof1Hit" : -1,
"fTof2Hit" : -1,
"fDch1Track" : -1,
"fDch2Track" : -1,
"fDchTrack" : -1,
"fMwpc1Track" : -1,
"fMwpc2Track" : -1,
"fUpstreamTrack" : -1,
"fScWallCellId" : -1,
"fCscHit" : [-1, -1, -1, -1],
"fScWallSignal" : -1000,
"fBeta400" : -1000,
"fBeta700" : -1000,
"fdQdNUpper" : 0,
"fdQdNLower" : 0,
"fA" : -1,
"fZ" : 0,
"fPDG" : 0,
"fChi2InVertex" : 2.34478793822074e-310,
"fDCAInVertex" : 0.169359803199768,
"fPidTof400" : [],
"fPidTof700" : [],
"fIsPrimary" : true,
"fRefIndex" : 0
}, {
"_typename" : "BmnGlobalTrack",
"fUniqueID" : 0,
"fBits" : 0,
"fHits" : [],
"fParamFirst" : {
"_typename" : "FairTrackParam",
"fUniqueID" : 0,
"fBits" : 0,
"fX" : -1.30332088470459,
"fY" : 0.709633886814117,
"fZ" : 0.716018855571747,
"fTx" : -0.0394387505948544,
"fTy" : -0.0554707236588001,
"fQp" : 0.794627845287323,
"fCovMatrix" : [1.0362377166748, -0.242409482598305, -0.0120293609797955, 0.00132000644225627, 0.0113498065620661, 1.10034370422363, 0.0026963478885591, -0.0100266374647617, -0.00638964772224426, 1.71858730027452e-4, -1.46718830364989e-5, -1.39145020511933e-4, 1.31993641844019e-4, 3.72123940906022e-5, 3.64089268259704e-4]
},
"fParamLast" : {
"_typename" : "FairTrackParam",
"fUniqueID" : 0,
"fBits" : 0,
"fX" : 153.443832397461,
"fY" : -27.7408638000488,
"fZ" : 422.371002197266,
"fTx" : 0.623147189617157,
"fTy" : -0.0757252722978592,
"fQp" : 0.879441320896149,
"fCovMatrix" : [0.210621446371078, 0.00677151698619127, 0.00126052019186318, -1.89192069228739e-5, 0.00233594398014247, 0.117487587034702, 1.90632126759738e-5, 5.61361608561128e-4, 3.66047053830698e-4, 5.4321233619703e-5, -1.38209452416049e-6, 1.40239317261148e-5, 3.86252722819336e-5, -2.07852266953523e-7, 1.6525064711459e-4]
},
"fFlag" : -1,
"fChi2" : 28.54445,
"fNDF" : 3,
"fB" : 0,
"fLength" : 456.7299,
"fNhits" : 5,
"fUsing" : false,
"fGemTrack" : 1,
"fSsdTrack" : -1,
"fSilTrack" : -1,
"fTof1Hit" : 0,
"fTof2Hit" : -1,
"fDch1Track" : -1,
"fDch2Track" : -1,
"fDchTrack" : -1,
"fMwpc1Track" : -1,
"fMwpc2Track" : -1,
"fUpstreamTrack" : -1,
"fScWallCellId" : -1,
"fCscHit" : [-1, -1, -1, -1],
"fScWallSignal" : -1000,
"fBeta400" : 0.813491527513362,
"fBeta700" : -1000,
"fdQdNUpper" : 0,
"fdQdNLower" : 0,
"fA" : -1,
"fZ" : 0,
"fPDG" : 0,
"fChi2InVertex" : 2.34478793822074e-310,
"fDCAInVertex" : 1.82100653648376,
"fPidTof400" : [0.698706510566553, 0.0456706934329348, 0.070160747316434, 0.044178479600919, 0.0321317585690916, 0.0203602877603981, 0.0563498151154308, 0.0324417076382387],
"fPidTof700" : [],
"fIsPrimary" : true,
"fRefIndex" : 0
}]
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -34,20 +34,27 @@ public fun Shape2DBuilder.polygon(vertices: Int, radius: Number) {
}
}
/**
* A layer for extruded shape
*/
@Serializable
public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Float)
/**
* An extruded shape with the same number of points on each layer.
*/
@Serializable
@SerialName("solid.extrude")
public class Extruded(
public val shape: List<Float32Vector2D>,
public val shape: Shape2D,
public val layers: List<Layer>,
) : SolidBase<Extruded>(), GeometrySolid {
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val shape: Shape2D = shape
init {
require(shape.size > 2) { "Extruded shape requires more than 2 points per layer" }
}
if (shape.size < 3) error("Extruded shape requires more than 2 points per layer")
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
/**
* Expand the shape for specific layers
@ -90,31 +97,31 @@ public class Extruded(
geometryBuilder.cap(layers.last())
}
public class Builder(
public var shape: List<Float32Vector2D> = emptyList(),
public var layers: MutableList<Layer> = ArrayList(),
public val properties: MutableMeta = MutableMeta(),
) {
public fun shape(block: Shape2DBuilder.() -> Unit) {
this.shape = Shape2DBuilder().apply(block).build()
}
public fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) {
layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat()))
}
internal fun build(): Extruded = Extruded(shape, layers).apply {
this.properties.setProperty(Name.EMPTY, this@Builder.properties)
}
}
public companion object {
public const val TYPE: String = "solid.extruded"
}
}
public class ExtrudeBuilder(
public var shape: List<Float32Vector2D> = emptyList(),
public var layers: MutableList<Layer> = ArrayList(),
public val properties: MutableMeta = MutableMeta(),
) {
public fun shape(block: Shape2DBuilder.() -> Unit) {
this.shape = Shape2DBuilder().apply(block).build()
}
public fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) {
layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat()))
}
internal fun build(): Extruded = Extruded(shape, layers).apply {
this.properties.setProperty(Name.EMPTY, this@ExtrudeBuilder.properties)
}
}
@VisionBuilder
public fun MutableVisionContainer<Solid>.extruded(
name: String? = null,
action: ExtrudeBuilder.() -> Unit = {},
): Extruded = ExtrudeBuilder().apply(action).build().also { setChild(name, it) }
action: Extruded.Builder.() -> Unit = {},
): Extruded = Extruded.Builder().apply(action).build().also { setChild(name, it) }

View File

@ -15,7 +15,6 @@ import kotlin.math.sqrt
@Serializable(Float32Euclidean2DSpace.VectorSerializer::class)
public interface Float32Vector2D: Vector2D<Float>
public object Float32Euclidean2DSpace :
GeometrySpace<Float32Vector2D>,
ScaleOperations<Float32Vector2D> {

View File

@ -0,0 +1,136 @@
package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.kmath.geometry.component1
import space.kscience.kmath.geometry.component2
import space.kscience.kmath.structures.Float32
private inline fun <T> Iterable<T>.sumOf(selector: (T) -> Float32): Float32 {
var sum = 0f
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* A layered solid with a hole inside
*/
@Serializable
@SerialName("solid.surface")
public class LayersSurface(
public val layers: List<Layer>,
) : SolidBase<Extruded>(), GeometrySolid {
@Serializable
public data class Layer(val z: Float32, val outer: Shape2D, val inner: Shape2D?) {
init {
require(outer.size >= 3) { "Extruded shape requires more than 2 points per layer" }
require(inner == null || inner.size == outer.size) { "Outer shape size is ${outer.size}, but inner is ${inner?.size}" }
}
public fun outerPoints(): List<Float32Vector3D> = outer.map { (x, y) -> Float32Vector3D(x, y, z) }
public fun innerPoints(): List<Float32Vector3D>? = inner?.map { (x, y) -> Float32Vector3D(x, y, z) }
public val center: Float32Vector3D by lazy {
Float32Vector3D(
outer.sumOf { it.x } / size,
outer.sumOf { it.y } / size,
z
)
}
val size: Int get() = outer.size
}
init {
require(layers.size > 1) { "Number of layers must be 2 or more" }
require(layers.all { it.size == layers.first().size }) { "All layers must have the same size" }
}
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
geometryBuilder.apply {
//outer and inner
for (i in 0 until layers.size - 1) {
val bottom = layers[i]
val top = layers[i+1]
//creating shape in x-y plane with z = 0
val bottomOuterPoints = bottom.outerPoints()
val topOuterPoints = top.outerPoints()
for (it in 1 until bottomOuterPoints.size) {
//outer face
face4(
bottomOuterPoints[it - 1],
bottomOuterPoints[it],
topOuterPoints[it],
topOuterPoints[it - 1]
)
}
//outer face last segment
face4(bottomOuterPoints.last(), bottomOuterPoints[0], topOuterPoints[0], topOuterPoints.last())
val bottomInnerPoints = bottom.innerPoints() ?: bottom.outerPoints().map { bottom.center }
val topInnerPoints = top.innerPoints() ?: top.outerPoints().map { top.center }
for (it in 1 until bottomInnerPoints.size) {
//inner face
face4(
bottomInnerPoints[it],
bottomInnerPoints[it - 1],
topInnerPoints[it - 1],
topInnerPoints[it]
)
}
//inner face last segment
face4(bottomInnerPoints[0], bottomInnerPoints.last(), topInnerPoints.last(), topInnerPoints[0])
}
val bottom = layers.first()
val top = layers.last()
val bottomOuterPoints = bottom.outerPoints()
val topOuterPoints = top.outerPoints()
val bottomInnerPoints = bottom.innerPoints() ?: bottom.outerPoints().map { bottom.center }
val topInnerPoints = top.innerPoints() ?: top.outerPoints().map { top.center }
(1 until bottom.size).forEach {
//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]
)
face4(
bottomInnerPoints.last(),
bottomInnerPoints[0],
bottomOuterPoints[0],
bottomOuterPoints.last()
)
face4(topInnerPoints[0], topInnerPoints.last(), topOuterPoints.last(), topOuterPoints[0])
}
}
}
public companion object {
public const val TYPE: String = "solid.surface"
}
}