v0.2.0-dev-22 #47
@ -1,6 +1,5 @@
|
|||||||
import scientifik.DependencyConfiguration
|
import scientifik.DependencyConfiguration
|
||||||
import scientifik.FXModule
|
import scientifik.FXModule
|
||||||
import scientifik.useFx
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("scientifik.mpp")
|
id("scientifik.mpp")
|
||||||
@ -37,7 +36,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClassName = "hep.dataforge.vision.gdml.demo.GDMLDemoAppKt"
|
mainClassName = "hep.dataforge.vision.gdml.demo.GdmlFxDemoAppKt"
|
||||||
}
|
}
|
||||||
|
|
||||||
val convertGdmlToJson by tasks.creating(JavaExec::class) {
|
val convertGdmlToJson by tasks.creating(JavaExec::class) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package hep.dataforge.vision.gdml.demo
|
package hep.dataforge.vision.gdml.demo
|
||||||
|
|
||||||
import hep.dataforge.context.Context
|
import hep.dataforge.context.Context
|
||||||
|
import hep.dataforge.meta.set
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.isEmpty
|
import hep.dataforge.names.isEmpty
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
@ -8,7 +9,6 @@ import hep.dataforge.vision.VisionGroup
|
|||||||
import hep.dataforge.vision.bootstrap.*
|
import hep.dataforge.vision.bootstrap.*
|
||||||
import hep.dataforge.vision.gdml.toVision
|
import hep.dataforge.vision.gdml.toVision
|
||||||
import hep.dataforge.vision.react.component
|
import hep.dataforge.vision.react.component
|
||||||
import hep.dataforge.vision.react.flexColumn
|
|
||||||
import hep.dataforge.vision.react.objectTree
|
import hep.dataforge.vision.react.objectTree
|
||||||
import hep.dataforge.vision.react.state
|
import hep.dataforge.vision.react.state
|
||||||
import hep.dataforge.vision.solid.Solid
|
import hep.dataforge.vision.solid.Solid
|
||||||
@ -29,7 +29,6 @@ import react.dom.h1
|
|||||||
import scientifik.gdml.GDML
|
import scientifik.gdml.GDML
|
||||||
import scientifik.gdml.parse
|
import scientifik.gdml.parse
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
|
||||||
import kotlin.browser.window
|
import kotlin.browser.window
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
|
||||||
@ -50,14 +49,14 @@ private val canvasConfig = Canvas3DOptions {
|
|||||||
val GDMLApp = component<GDMLAppProps> { props ->
|
val GDMLApp = component<GDMLAppProps> { props ->
|
||||||
var selected by state { props.selected }
|
var selected by state { props.selected }
|
||||||
var canvas: ThreeCanvas? by state { null }
|
var canvas: ThreeCanvas? by state { null }
|
||||||
var visual: Vision? by state { props.rootObject }
|
var vision: Vision? by state { props.rootObject }
|
||||||
|
|
||||||
val select: (Name?) -> Unit = {
|
val select: (Name?) -> Unit = {
|
||||||
selected = it
|
selected = it
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadData(name: String, data: String) {
|
fun loadData(name: String, data: String) {
|
||||||
visual = when {
|
val parsedVision = when {
|
||||||
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
||||||
val gdml = GDML.parse(data)
|
val gdml = GDML.parse(data)
|
||||||
gdml.toVision(gdmlConfiguration)
|
gdml.toVision(gdmlConfiguration)
|
||||||
@ -68,20 +67,26 @@ val GDMLApp = component<GDMLAppProps> { props ->
|
|||||||
error("File extension is not recognized: $name")
|
error("File extension is not recognized: $name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
parsedVision.config["edges.enabled"] = false
|
||||||
|
|
||||||
|
vision = parsedVision
|
||||||
}
|
}
|
||||||
|
|
||||||
flexColumn {
|
gridColumn {
|
||||||
css {
|
css {
|
||||||
flex(1.0, 1.0, FlexBasis.auto)
|
flex(1.0, 1.0, FlexBasis.auto)
|
||||||
}
|
}
|
||||||
h1 { +"GDML/JSON loader demo" }
|
h1 { +"GDML/JSON loader demo" }
|
||||||
styledDiv {
|
gridRow {
|
||||||
css {
|
css {
|
||||||
classes.add("row")
|
+"p-1"
|
||||||
classes.add("p-1")
|
|
||||||
overflow = Overflow.auto
|
overflow = Overflow.auto
|
||||||
}
|
}
|
||||||
gridColumn(3, maxSize = GridMaxSize.XL, classes = "order-2 order-xl-1") {
|
gridColumn(3, maxSize = GridMaxSize.XL) {
|
||||||
|
css {
|
||||||
|
+"order-2"
|
||||||
|
+"order-xl-1"
|
||||||
|
}
|
||||||
card("Load data") {
|
card("Load data") {
|
||||||
fileDrop("(drag file here)") { files ->
|
fileDrop("(drag file here)") { files ->
|
||||||
val file = files?.get(0)
|
val file = files?.get(0)
|
||||||
@ -99,15 +104,19 @@ val GDMLApp = component<GDMLAppProps> { props ->
|
|||||||
}
|
}
|
||||||
//tree
|
//tree
|
||||||
card("Object tree") {
|
card("Object tree") {
|
||||||
visual?.let {
|
vision?.let {
|
||||||
objectTree(it, selected, select)
|
objectTree(it, selected, select)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gridColumn(6, maxSize = GridMaxSize.XL, classes = "order-1 order-xl-2") {
|
gridColumn(6, maxSize = GridMaxSize.XL) {
|
||||||
|
css {
|
||||||
|
+"order-1"
|
||||||
|
+"order-xl-2"
|
||||||
|
}
|
||||||
//canvas
|
//canvas
|
||||||
(visual as? Solid)?.let { visual3D ->
|
(vision as? Solid)?.let { visual3D ->
|
||||||
child(ThreeCanvasComponent::class) {
|
child(ThreeCanvasComponent::class) {
|
||||||
attrs {
|
attrs {
|
||||||
this.context = props.context
|
this.context = props.context
|
||||||
@ -121,7 +130,10 @@ val GDMLApp = component<GDMLAppProps> { props ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gridColumn(3, maxSize = GridMaxSize.XL, classes = "order-3") {
|
gridColumn(3, maxSize = GridMaxSize.XL) {
|
||||||
|
css {
|
||||||
|
+"order-3"
|
||||||
|
}
|
||||||
container {
|
container {
|
||||||
//settings
|
//settings
|
||||||
canvas?.let {
|
canvas?.let {
|
||||||
@ -136,8 +148,8 @@ val GDMLApp = component<GDMLAppProps> { props ->
|
|||||||
selected.let { selected ->
|
selected.let { selected ->
|
||||||
val selectedObject: Vision? = when {
|
val selectedObject: Vision? = when {
|
||||||
selected == null -> null
|
selected == null -> null
|
||||||
selected.isEmpty() -> visual
|
selected.isEmpty() -> vision
|
||||||
else -> (visual as? VisionGroup)?.get(selected)
|
else -> (vision as? VisionGroup)?.get(selected)
|
||||||
}
|
}
|
||||||
if (selectedObject != null) {
|
if (selectedObject != null) {
|
||||||
visionPropertyEditor(
|
visionPropertyEditor(
|
||||||
|
@ -16,23 +16,9 @@ import kotlin.browser.document
|
|||||||
|
|
||||||
val gdmlConfiguration: GDMLTransformer.() -> Unit = {
|
val gdmlConfiguration: GDMLTransformer.() -> Unit = {
|
||||||
lUnit = LUnit.CM
|
lUnit = LUnit.CM
|
||||||
volumeAction = { volume ->
|
|
||||||
when {
|
|
||||||
volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT
|
|
||||||
volume.name.startsWith("UPBL") -> GDMLTransformer.Action.REJECT
|
|
||||||
volume.name.startsWith("USCL") -> GDMLTransformer.Action.REJECT
|
|
||||||
volume.name.startsWith("VPBL") -> GDMLTransformer.Action.REJECT
|
|
||||||
volume.name.startsWith("VSCL") -> GDMLTransformer.Action.REJECT
|
|
||||||
else -> GDMLTransformer.Action.CACHE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
solidConfiguration = { parent, solid ->
|
solidConfiguration = { parent, _ ->
|
||||||
if (
|
if (parent.physVolumes.isNotEmpty()) {
|
||||||
solid.name.startsWith("Yoke")
|
|
||||||
|| solid.name.startsWith("Pole")
|
|
||||||
|| parent.physVolumes.isNotEmpty()
|
|
||||||
) {
|
|
||||||
useStyle("opaque") {
|
useStyle("opaque") {
|
||||||
MATERIAL_OPACITY_KEY put 0.3
|
MATERIAL_OPACITY_KEY put 0.3
|
||||||
}
|
}
|
||||||
@ -64,19 +50,6 @@ private class GDMLDemoApp : Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// (document.getElementById("file_load_button") as? HTMLInputElement)?.apply {
|
|
||||||
// addEventListener("change", {
|
|
||||||
// (it.target as HTMLInputElement).files?.asList()?.first()?.let { file ->
|
|
||||||
// FileReader().apply {
|
|
||||||
// onload = {
|
|
||||||
// val string = result as String
|
|
||||||
// action(file.name, string)
|
|
||||||
// }
|
|
||||||
// readAsText(file)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }, false)
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -36,12 +36,11 @@ class GDMLView : View() {
|
|||||||
buttonbar {
|
buttonbar {
|
||||||
button("Load GDML/json") {
|
button("Load GDML/json") {
|
||||||
action {
|
action {
|
||||||
runAsync {
|
|
||||||
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
|
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
|
||||||
?: return@runAsync null
|
if(file!= null) {
|
||||||
|
runAsync {
|
||||||
SolidManager.readFile(file)
|
SolidManager.readFile(file)
|
||||||
} ui {
|
} ui {
|
||||||
if (it != null) {
|
|
||||||
canvas.render(it)
|
canvas.render(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -71,7 +71,7 @@ private object VariableBoxThreeFactory : ThreeFactory<Solid> {
|
|||||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||||
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
||||||
|
|
||||||
val mesh = Mesh(geometry, getMaterial(obj)).apply {
|
val mesh = Mesh(geometry, getMaterial(obj,true)).apply {
|
||||||
applyEdges(obj)
|
applyEdges(obj)
|
||||||
applyWireFrame(obj)
|
applyWireFrame(obj)
|
||||||
|
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
21
gradlew.bat
vendored
21
gradlew.bat
vendored
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@ -54,7 +54,7 @@ goto fail
|
|||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
@ -64,21 +64,6 @@ echo location of your Java installation.
|
|||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windows variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
@ -13,6 +13,9 @@ import react.RBuilder
|
|||||||
import react.ReactElement
|
import react.ReactElement
|
||||||
import react.child
|
import react.child
|
||||||
import react.dom.*
|
import react.dom.*
|
||||||
|
import styled.StyledDOMBuilder
|
||||||
|
import styled.css
|
||||||
|
import styled.styledDiv
|
||||||
|
|
||||||
inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {
|
inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {
|
||||||
div("card w-100") {
|
div("card w-100") {
|
||||||
@ -180,10 +183,14 @@ enum class ContainerSize(val suffix: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline fun RBuilder.container(
|
inline fun RBuilder.container(
|
||||||
classes: String? = null,
|
|
||||||
size: ContainerSize = ContainerSize.FLUID,
|
size: ContainerSize = ContainerSize.FLUID,
|
||||||
block: RDOMBuilder<DIV>.() -> Unit
|
block: StyledDOMBuilder<DIV>.() -> Unit
|
||||||
): ReactElement = div(joinStyles(classes, "container${size.suffix}"), block)
|
): ReactElement = styledDiv{
|
||||||
|
css{
|
||||||
|
classes.add("container${size.suffix}")
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum class GridMaxSize(val suffix: String) {
|
enum class GridMaxSize(val suffix: String) {
|
||||||
@ -196,19 +203,23 @@ enum class GridMaxSize(val suffix: String) {
|
|||||||
|
|
||||||
inline fun RBuilder.gridColumn(
|
inline fun RBuilder.gridColumn(
|
||||||
weight: Int? = null,
|
weight: Int? = null,
|
||||||
classes: String? = null,
|
|
||||||
maxSize: GridMaxSize = GridMaxSize.NONE,
|
maxSize: GridMaxSize = GridMaxSize.NONE,
|
||||||
block: RDOMBuilder<DIV>.() -> Unit
|
block: StyledDOMBuilder<DIV>.() -> Unit
|
||||||
): ReactElement {
|
): ReactElement = styledDiv {
|
||||||
val weightSuffix = weight?.let { "-$it" } ?: ""
|
val weightSuffix = weight?.let { "-$it" } ?: ""
|
||||||
return div(joinStyles(classes, "col${maxSize.suffix}$weightSuffix"), block)
|
css {
|
||||||
|
classes.add("col${maxSize.suffix}$weightSuffix")
|
||||||
|
}
|
||||||
|
block()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun RBuilder.gridRow(
|
inline fun RBuilder.gridRow(
|
||||||
classes: String? = null,
|
block: StyledDOMBuilder<DIV>.() -> Unit
|
||||||
block: RDOMBuilder<DIV>.() -> Unit
|
): ReactElement = styledDiv{
|
||||||
): ReactElement {
|
css{
|
||||||
return div(joinStyles(classes, "row"), block)
|
classes.add("row")
|
||||||
|
}
|
||||||
|
block()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Element.renderObjectTree(
|
fun Element.renderObjectTree(
|
||||||
|
@ -91,8 +91,7 @@ interface MutableVisionGroup : VisionGroup {
|
|||||||
|
|
||||||
operator fun VisionGroup.get(str: String?): Vision? = get(str?.toName() ?: Name.EMPTY)
|
operator fun VisionGroup.get(str: String?): Vision? = get(str?.toName() ?: Name.EMPTY)
|
||||||
|
|
||||||
operator fun MutableVisionGroup.set(key: String, child: Vision?) {
|
operator fun MutableVisionGroup.set(token: NameToken, child: Vision?): Unit = set(token.asName(), child)
|
||||||
set(key.toName(), child)
|
operator fun MutableVisionGroup.set(key: String, child: Vision?): Unit = set(key.toName(), child)
|
||||||
}
|
|
||||||
|
|
||||||
fun MutableVisionGroup.removeAll() = children.keys.map { it.asName() }.forEach { this[it] = null }
|
fun MutableVisionGroup.removeAll() = children.keys.map { it.asName() }.forEach { this[it] = null }
|
@ -0,0 +1,35 @@
|
|||||||
|
package hep.dataforge.vision.visitor
|
||||||
|
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.vision.Vision
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
suspend fun <T> Vision.flowStatistics(statistics: (Name, Vision) -> T): Flow<T> = callbackFlow<T> {
|
||||||
|
val visitor = object : VisionVisitor {
|
||||||
|
override suspend fun visit(name: Name, vision: Vision){
|
||||||
|
send(statistics(name, vision))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val job: Job = VisionVisitor.visitTree(visitor, this, this@flowStatistics)
|
||||||
|
job.invokeOnCompletion {
|
||||||
|
channel.close()
|
||||||
|
}
|
||||||
|
awaitClose {
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DefaultVisionStatistics(val name: Name, val type: KClass<out Vision>) {
|
||||||
|
val depth get() = name.length
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun Vision.flowStatistics(): Flow<DefaultVisionStatistics> = flowStatistics { name, vision ->
|
||||||
|
DefaultVisionStatistics(name, vision::class)
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package hep.dataforge.vision.visitor
|
||||||
|
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.plus
|
||||||
|
import hep.dataforge.vision.Vision
|
||||||
|
import hep.dataforge.vision.VisionGroup
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
interface VisionVisitor {
|
||||||
|
/**
|
||||||
|
* Visit a vision possibly mutating in in the process. Should not rearrange children.
|
||||||
|
* @param name full name of a [Vision] being visited
|
||||||
|
* @param vision the visited [Vision]
|
||||||
|
*/
|
||||||
|
suspend fun visit(name: Name, vision: Vision)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rearrange children of given group
|
||||||
|
*/
|
||||||
|
suspend fun visitChildren(name: Name, group: VisionGroup) {
|
||||||
|
//Do nothing by default
|
||||||
|
}
|
||||||
|
|
||||||
|
fun skip(name: Name, vision: Vision): Boolean = false
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
private fun CoroutineScope.visitTreeAsync(
|
||||||
|
visionVisitor: VisionVisitor,
|
||||||
|
name: Name,
|
||||||
|
vision: Vision
|
||||||
|
): Job = launch {
|
||||||
|
if (visionVisitor.skip(name, vision)) return@launch
|
||||||
|
visionVisitor.visit(name, vision)
|
||||||
|
|
||||||
|
if (vision is VisionGroup) {
|
||||||
|
visionVisitor.visitChildren(name, vision)
|
||||||
|
|
||||||
|
for ((token, child) in vision.children) {
|
||||||
|
visitTreeAsync(visionVisitor, name + token, child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively visit this [Vision] and all children
|
||||||
|
*/
|
||||||
|
fun visitTree(visionVisitor: VisionVisitor, scope: CoroutineScope, root: Vision): Job =
|
||||||
|
scope.visitTreeAsync(visionVisitor, Name.EMPTY, root)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
|||||||
|
package hep.dataforge.vision.visitor
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
suspend fun <T, K> Flow<T>.countDistinctBy(selector: (T) -> K): Map<K, Int> {
|
||||||
|
val counter = LinkedHashMap<K, AtomicInteger>()
|
||||||
|
collect {
|
||||||
|
val key = selector(it)
|
||||||
|
counter.getOrPut(key) { AtomicInteger() }.incrementAndGet()
|
||||||
|
}
|
||||||
|
return counter.mapValues { it.value.toInt() }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> Flow<T>.countDistinct(): Map<T, Int> = countDistinctBy { it }
|
@ -4,11 +4,16 @@ import hep.dataforge.meta.Meta
|
|||||||
import hep.dataforge.meta.MetaBuilder
|
import hep.dataforge.meta.MetaBuilder
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.vision.get
|
||||||
|
import hep.dataforge.vision.set
|
||||||
import hep.dataforge.vision.solid.*
|
import hep.dataforge.vision.solid.*
|
||||||
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
||||||
import hep.dataforge.vision.useStyle
|
import hep.dataforge.vision.useStyle
|
||||||
import scientifik.gdml.*
|
import scientifik.gdml.*
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
class GDMLTransformer(val root: GDML) {
|
class GDMLTransformer(val root: GDML) {
|
||||||
@ -21,17 +26,16 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
CACHE
|
CACHE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A special group for local templates
|
|
||||||
*/
|
|
||||||
val proto by lazy { SolidGroup() }
|
|
||||||
private val styleCache = HashMap<Name, Meta>()
|
|
||||||
|
|
||||||
var lUnit: LUnit = LUnit.MM
|
var lUnit: LUnit = LUnit.MM
|
||||||
|
|
||||||
var solidAction: (GDMLSolid) -> Action = { Action.CACHE }
|
var solidAction: (GDMLSolid) -> Action = { Action.CACHE }
|
||||||
var volumeAction: (GDMLGroup) -> Action = { Action.CACHE }
|
var volumeAction: (GDMLGroup) -> Action = { Action.CACHE }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special group for local templates
|
||||||
|
*/
|
||||||
|
internal val proto by lazy { SolidGroup() }
|
||||||
|
private val styleCache = HashMap<Name, Meta>()
|
||||||
|
|
||||||
var solidConfiguration: Solid.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { parent, _ ->
|
var solidConfiguration: Solid.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { parent, _ ->
|
||||||
lUnit = LUnit.CM
|
lUnit = LUnit.CM
|
||||||
@ -87,3 +91,274 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Solid.withPosition(
|
||||||
|
lUnit: LUnit,
|
||||||
|
newPos: GDMLPosition? = null,
|
||||||
|
newRotation: GDMLRotation? = null,
|
||||||
|
newScale: GDMLScale? = null
|
||||||
|
): Solid = apply {
|
||||||
|
newPos?.let {
|
||||||
|
val point = Point3D(it.x(lUnit), it.y(lUnit), it.z(lUnit))
|
||||||
|
if (position != null || point != World.ZERO) {
|
||||||
|
position = point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRotation?.let {
|
||||||
|
val point = Point3D(it.x(), it.y(), it.z())
|
||||||
|
if (rotation != null || point != World.ZERO) {
|
||||||
|
rotation = point
|
||||||
|
}
|
||||||
|
//this@withPosition.rotationOrder = RotationOrder.ZXY
|
||||||
|
}
|
||||||
|
newScale?.let {
|
||||||
|
val point = Point3D(it.x, it.y, it.z)
|
||||||
|
if (scale != null || point != World.ONE) {
|
||||||
|
scale = point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO convert units if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline operator fun Number.times(d: Double) = toDouble() * d
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline operator fun Number.times(f: Float) = toFloat() * f
|
||||||
|
|
||||||
|
private fun SolidGroup.addSolid(
|
||||||
|
context: GDMLTransformer,
|
||||||
|
solid: GDMLSolid,
|
||||||
|
name: String = "",
|
||||||
|
block: Solid.() -> Unit = {}
|
||||||
|
): Solid {
|
||||||
|
//context.solidAdded(solid)
|
||||||
|
val lScale = solid.lscale(context.lUnit)
|
||||||
|
val aScale = solid.ascale()
|
||||||
|
return when (solid) {
|
||||||
|
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
|
||||||
|
is GDMLTube -> tube(
|
||||||
|
solid.rmax * lScale,
|
||||||
|
solid.z * lScale,
|
||||||
|
solid.rmin * lScale,
|
||||||
|
solid.startphi * aScale,
|
||||||
|
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 GDMLXtru -> extrude(name) {
|
||||||
|
shape {
|
||||||
|
solid.vertices.forEach {
|
||||||
|
point(it.x * lScale, it.y * lScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
solid.sections.sortedBy { it.zOrder }.forEach { section ->
|
||||||
|
layer(
|
||||||
|
section.zPosition * lScale,
|
||||||
|
section.xOffset * lScale,
|
||||||
|
section.yOffset * lScale,
|
||||||
|
section.scalingFactor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is GDMLScaledSolid -> {
|
||||||
|
//Add solid with modified scale
|
||||||
|
val innerSolid = solid.solidref.resolve(context.root)
|
||||||
|
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
|
||||||
|
|
||||||
|
addSolid(context, innerSolid, name) {
|
||||||
|
block()
|
||||||
|
scaleX *= solid.scale.x.toFloat()
|
||||||
|
scaleY *= solid.scale.y.toFloat()
|
||||||
|
scaleZ = solid.scale.z.toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) {
|
||||||
|
phiStart = solid.startphi * aScale
|
||||||
|
thetaStart = solid.starttheta * aScale
|
||||||
|
}
|
||||||
|
is GDMLOrb -> sphere(solid.r * lScale, name = name)
|
||||||
|
is GDMLPolyhedra -> extrude(name) {
|
||||||
|
//getting the radius of first
|
||||||
|
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
|
||||||
|
val baseRadius = solid.planes.first().rmax * lScale
|
||||||
|
shape {
|
||||||
|
(0..solid.numsides).forEach {
|
||||||
|
val phi = solid.deltaphi * aScale / solid.numsides * it + solid.startphi * aScale
|
||||||
|
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
solid.planes.forEach { plane ->
|
||||||
|
//scaling all radii relative to first layer radius
|
||||||
|
layer(plane.z * lScale, scale = plane.rmax * lScale / baseRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is GDMLBoolSolid -> {
|
||||||
|
val first = solid.first.resolve(context.root) ?: error("")
|
||||||
|
val second = solid.second.resolve(context.root) ?: error("")
|
||||||
|
val type: CompositeType = when (solid) {
|
||||||
|
is GDMLUnion -> CompositeType.UNION
|
||||||
|
is GDMLSubtraction -> CompositeType.SUBTRACT
|
||||||
|
is GDMLIntersection -> CompositeType.INTERSECT
|
||||||
|
}
|
||||||
|
|
||||||
|
return composite(type, name) {
|
||||||
|
addSolid(context, first) {
|
||||||
|
withPosition(
|
||||||
|
context.lUnit,
|
||||||
|
solid.resolveFirstPosition(context.root),
|
||||||
|
solid.resolveFirstRotation(context.root),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
addSolid(context, second) {
|
||||||
|
withPosition(
|
||||||
|
context.lUnit,
|
||||||
|
solid.resolvePosition(context.root),
|
||||||
|
solid.resolveRotation(context.root),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> error("Renderer for $solid not supported yet")
|
||||||
|
}.apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun SolidGroup.addSolidWithCaching(
|
||||||
|
context: GDMLTransformer,
|
||||||
|
solid: GDMLSolid,
|
||||||
|
volume: GDMLVolume,
|
||||||
|
name: String = solid.name
|
||||||
|
) {
|
||||||
|
when (context.solidAction(solid)) {
|
||||||
|
GDMLTransformer.Action.ACCEPT -> {
|
||||||
|
addSolid(context, solid, name) {
|
||||||
|
context.configureSolid(this, volume, solid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GDMLTransformer.Action.CACHE -> {
|
||||||
|
if (context.proto[solid.name] == null) {
|
||||||
|
context.proto.addSolid(context, solid, name) {
|
||||||
|
context.configureSolid(this, volume, solid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ref(solid.name.asName(), name)
|
||||||
|
}
|
||||||
|
GDMLTransformer.Action.REJECT -> {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private val volumesName = "volumes".asName()
|
||||||
|
|
||||||
|
private fun SolidGroup.addPhysicalVolume(
|
||||||
|
context: GDMLTransformer,
|
||||||
|
physVolume: GDMLPhysVolume
|
||||||
|
) {
|
||||||
|
val volume: GDMLGroup = physVolume.volumeref.resolve(context.root)
|
||||||
|
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
|
||||||
|
|
||||||
|
// a special case for single solid volume
|
||||||
|
// if (volume is GDMLVolume && volume.physVolumes.isEmpty() && volume.placement == null) {
|
||||||
|
// val solid = volume.solidref.resolve(context.root)
|
||||||
|
// ?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined")
|
||||||
|
// addSolidWithCaching(context, solid, volume, physVolume.name ?: "").apply {
|
||||||
|
// withPosition(
|
||||||
|
// context.lUnit,
|
||||||
|
// physVolume.resolvePosition(context.root),
|
||||||
|
// physVolume.resolveRotation(context.root),
|
||||||
|
// physVolume.resolveScale(context.root)
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
when (context.volumeAction(volume)) {
|
||||||
|
GDMLTransformer.Action.ACCEPT -> {
|
||||||
|
val group: SolidGroup = volume(context, volume)
|
||||||
|
this[physVolume.name ?: ""] = group.apply {
|
||||||
|
withPosition(
|
||||||
|
context.lUnit,
|
||||||
|
physVolume.resolvePosition(context.root),
|
||||||
|
physVolume.resolveRotation(context.root),
|
||||||
|
physVolume.resolveScale(context.root)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GDMLTransformer.Action.CACHE -> {
|
||||||
|
val fullName = volumesName + volume.name.asName()
|
||||||
|
if (context.proto[fullName] == null) {
|
||||||
|
context.proto[fullName] = volume(context, volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
this[physVolume.name ?: ""] = Proxy(this, fullName).apply {
|
||||||
|
withPosition(
|
||||||
|
context.lUnit,
|
||||||
|
physVolume.resolvePosition(context.root),
|
||||||
|
physVolume.resolveRotation(context.root),
|
||||||
|
physVolume.resolveScale(context.root)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GDMLTransformer.Action.REJECT -> {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SolidGroup.addDivisionVolume(
|
||||||
|
context: GDMLTransformer,
|
||||||
|
divisionVolume: GDMLDivisionVolume
|
||||||
|
) {
|
||||||
|
val volume: GDMLGroup = divisionVolume.volumeref.resolve(context.root)
|
||||||
|
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
|
||||||
|
|
||||||
|
//TODO add divisions
|
||||||
|
set(Name.EMPTY, volume(context, volume))
|
||||||
|
}
|
||||||
|
|
||||||
|
//private val solidsName = "solids".asName()
|
||||||
|
|
||||||
|
private fun volume(
|
||||||
|
context: GDMLTransformer,
|
||||||
|
group: GDMLGroup
|
||||||
|
): SolidGroup = SolidGroup().apply {
|
||||||
|
if (group is GDMLVolume) {
|
||||||
|
val solid: GDMLSolid = group.solidref.resolve(context.root)
|
||||||
|
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
|
||||||
|
|
||||||
|
addSolidWithCaching(context, solid, group)
|
||||||
|
|
||||||
|
when (val vol: GDMLPlacement? = group.placement) {
|
||||||
|
is GDMLPhysVolume -> addPhysicalVolume(context, vol)
|
||||||
|
is GDMLDivisionVolume -> addDivisionVolume(context, vol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group.physVolumes.forEach { physVolume ->
|
||||||
|
addPhysicalVolume(context, physVolume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun GDML.toVision(block: GDMLTransformer.() -> Unit = {}): SolidGroup {
|
||||||
|
val context = GDMLTransformer(this).apply(block)
|
||||||
|
return context.finalize(volume(context, world))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append gdml node to the group
|
||||||
|
*/
|
||||||
|
fun SolidGroup.gdml(gdml: GDML, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) {
|
||||||
|
val visual = gdml.toVision(transformer)
|
||||||
|
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
|
||||||
|
set(key, visual)
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package hep.dataforge.vision.gdml
|
||||||
|
|
||||||
|
import hep.dataforge.meta.DFExperimental
|
||||||
|
import hep.dataforge.meta.sequence
|
||||||
|
import hep.dataforge.meta.set
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.vision.*
|
||||||
|
import hep.dataforge.vision.solid.*
|
||||||
|
import hep.dataforge.vision.visitor.VisionVisitor
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import mu.KotlinLogging
|
||||||
|
|
||||||
|
expect class Counter() {
|
||||||
|
fun get(): Int
|
||||||
|
fun incrementAndGet(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
private class GdmlOptimizer() : VisionVisitor {
|
||||||
|
val logger = KotlinLogging.logger("SingleChildReducer")
|
||||||
|
|
||||||
|
private operator fun Point3D?.plus(other: Point3D?): Point3D? = if (this == null && other == null) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
(this ?: Point3D(0, 0, 0)) + (other ?: Point3D(0, 0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Vision.updateFrom(other: Vision): Vision {
|
||||||
|
if (this is Solid && other is Solid) {
|
||||||
|
position += other.position
|
||||||
|
rotation += other.rotation
|
||||||
|
if (this.scale != null || other.scale != null) {
|
||||||
|
scaleX = scaleX.toDouble() * other.scaleX.toDouble()
|
||||||
|
scaleY = scaleY.toDouble() * other.scaleY.toDouble()
|
||||||
|
scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble()
|
||||||
|
}
|
||||||
|
other.properties?.sequence()?.forEach { (name, item) ->
|
||||||
|
if (properties?.getItem(name) == null) {
|
||||||
|
config[name] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private val depthCount = HashMap<Int, Counter>()
|
||||||
|
|
||||||
|
override suspend fun visit(name: Name, vision: Vision) {
|
||||||
|
val depth = name.length
|
||||||
|
depthCount.getOrPut(depth) { Counter() }.incrementAndGet()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun skip(name: Name, vision: Vision): Boolean = vision is Proxy.ProxyChild
|
||||||
|
|
||||||
|
override suspend fun visitChildren(name: Name, group: VisionGroup) {
|
||||||
|
if (name == "volumes".toName()) return
|
||||||
|
if (group !is MutableVisionGroup) return
|
||||||
|
|
||||||
|
val newChildren = group.children.entries.associate { (visionToken, vision) ->
|
||||||
|
//Reduce single child groups
|
||||||
|
if (vision is VisionGroup && vision !is Proxy && vision.children.size == 1) {
|
||||||
|
val (token, child) = vision.children.entries.first()
|
||||||
|
child.parent = null
|
||||||
|
if (token != visionToken) {
|
||||||
|
child.config["solidName"] = token.toString()
|
||||||
|
}
|
||||||
|
visionToken to child.updateFrom(vision)
|
||||||
|
} else {
|
||||||
|
visionToken to vision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newChildren != group.children) {
|
||||||
|
group.removeAll()
|
||||||
|
newChildren.forEach { (token, child) ->
|
||||||
|
group[token] = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
suspend fun SolidGroup.optimizeGdml(): Job = coroutineScope {
|
||||||
|
prototypes?.let {
|
||||||
|
VisionVisitor.visitTree(GdmlOptimizer(), this, it)
|
||||||
|
} ?: CompletableDeferred(Unit)
|
||||||
|
}
|
@ -1,268 +0,0 @@
|
|||||||
package hep.dataforge.vision.gdml
|
|
||||||
|
|
||||||
|
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.names.asName
|
|
||||||
import hep.dataforge.names.plus
|
|
||||||
import hep.dataforge.vision.get
|
|
||||||
import hep.dataforge.vision.set
|
|
||||||
import hep.dataforge.vision.solid.*
|
|
||||||
import hep.dataforge.vision.solid.World.ONE
|
|
||||||
import hep.dataforge.vision.solid.World.ZERO
|
|
||||||
import scientifik.gdml.*
|
|
||||||
import kotlin.math.cos
|
|
||||||
import kotlin.math.sin
|
|
||||||
|
|
||||||
|
|
||||||
private fun Solid.withPosition(
|
|
||||||
lUnit: LUnit,
|
|
||||||
newPos: GDMLPosition? = null,
|
|
||||||
newRotation: GDMLRotation? = null,
|
|
||||||
newScale: GDMLScale? = null
|
|
||||||
): Solid = apply {
|
|
||||||
newPos?.let {
|
|
||||||
val point = Point3D(it.x(lUnit), it.y(lUnit), it.z(lUnit))
|
|
||||||
if (position != null || point != ZERO) {
|
|
||||||
position = point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newRotation?.let {
|
|
||||||
val point = Point3D(it.x(), it.y(), it.z())
|
|
||||||
if (rotation != null || point != ZERO) {
|
|
||||||
rotation = point
|
|
||||||
}
|
|
||||||
//this@withPosition.rotationOrder = RotationOrder.ZXY
|
|
||||||
}
|
|
||||||
newScale?.let {
|
|
||||||
val point = Point3D(it.x, it.y, it.z)
|
|
||||||
if (scale != null || point != ONE) {
|
|
||||||
scale = point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//TODO convert units if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline operator fun Number.times(d: Double) = toDouble() * d
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline operator fun Number.times(f: Float) = toFloat() * f
|
|
||||||
|
|
||||||
private fun SolidGroup.addSolid(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
solid: GDMLSolid,
|
|
||||||
name: String = "",
|
|
||||||
block: Solid.() -> Unit = {}
|
|
||||||
): Solid {
|
|
||||||
//context.solidAdded(solid)
|
|
||||||
val lScale = solid.lscale(context.lUnit)
|
|
||||||
val aScale = solid.ascale()
|
|
||||||
return when (solid) {
|
|
||||||
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
|
|
||||||
is GDMLTube -> tube(
|
|
||||||
solid.rmax * lScale,
|
|
||||||
solid.z * lScale,
|
|
||||||
solid.rmin * lScale,
|
|
||||||
solid.startphi * aScale,
|
|
||||||
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 GDMLXtru -> extrude(name) {
|
|
||||||
shape {
|
|
||||||
solid.vertices.forEach {
|
|
||||||
point(it.x * lScale, it.y * lScale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
solid.sections.sortedBy { it.zOrder }.forEach { section ->
|
|
||||||
layer(
|
|
||||||
section.zPosition * lScale,
|
|
||||||
section.xOffset * lScale,
|
|
||||||
section.yOffset * lScale,
|
|
||||||
section.scalingFactor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is GDMLScaledSolid -> {
|
|
||||||
//Add solid with modified scale
|
|
||||||
val innerSolid = solid.solidref.resolve(context.root)
|
|
||||||
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
|
|
||||||
|
|
||||||
addSolid(context, innerSolid, name) {
|
|
||||||
block()
|
|
||||||
scaleX *= solid.scale.x.toFloat()
|
|
||||||
scaleY *= solid.scale.y.toFloat()
|
|
||||||
scaleZ = solid.scale.z.toFloat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) {
|
|
||||||
phiStart = solid.startphi * aScale
|
|
||||||
thetaStart = solid.starttheta * aScale
|
|
||||||
}
|
|
||||||
is GDMLOrb -> sphere(solid.r * lScale, name = name)
|
|
||||||
is GDMLPolyhedra -> extrude(name) {
|
|
||||||
//getting the radius of first
|
|
||||||
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
|
|
||||||
val baseRadius = solid.planes.first().rmax * lScale
|
|
||||||
shape {
|
|
||||||
(0..solid.numsides).forEach {
|
|
||||||
val phi = solid.deltaphi * aScale / solid.numsides * it + solid.startphi * aScale
|
|
||||||
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
solid.planes.forEach { plane ->
|
|
||||||
//scaling all radii relative to first layer radius
|
|
||||||
layer(plane.z * lScale, scale = plane.rmax * lScale / baseRadius)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is GDMLBoolSolid -> {
|
|
||||||
val first = solid.first.resolve(context.root) ?: error("")
|
|
||||||
val second = solid.second.resolve(context.root) ?: error("")
|
|
||||||
val type: CompositeType = when (solid) {
|
|
||||||
is GDMLUnion -> CompositeType.UNION
|
|
||||||
is GDMLSubtraction -> CompositeType.SUBTRACT
|
|
||||||
is GDMLIntersection -> CompositeType.INTERSECT
|
|
||||||
}
|
|
||||||
|
|
||||||
return composite(type, name) {
|
|
||||||
addSolid(context, first) {
|
|
||||||
withPosition(
|
|
||||||
context.lUnit,
|
|
||||||
solid.resolveFirstPosition(context.root),
|
|
||||||
solid.resolveFirstRotation(context.root),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
addSolid(context, second) {
|
|
||||||
withPosition(
|
|
||||||
context.lUnit,
|
|
||||||
solid.resolvePosition(context.root),
|
|
||||||
solid.resolveRotation(context.root),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("Renderer for $solid not supported yet")
|
|
||||||
}.apply(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val volumesName = "volumes".asName()
|
|
||||||
|
|
||||||
private fun SolidGroup.addPhysicalVolume(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
physVolume: GDMLPhysVolume
|
|
||||||
) {
|
|
||||||
val volume: GDMLGroup = physVolume.volumeref.resolve(context.root)
|
|
||||||
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
|
|
||||||
|
|
||||||
when (context.volumeAction(volume)) {
|
|
||||||
GDMLTransformer.Action.ACCEPT -> {
|
|
||||||
val group = volume(context, volume)
|
|
||||||
this[physVolume.name ?: ""] = group.apply {
|
|
||||||
withPosition(
|
|
||||||
context.lUnit,
|
|
||||||
physVolume.resolvePosition(context.root),
|
|
||||||
physVolume.resolveRotation(context.root),
|
|
||||||
physVolume.resolveScale(context.root)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GDMLTransformer.Action.CACHE -> {
|
|
||||||
val fullName = volumesName + volume.name.asName()
|
|
||||||
if (context.proto[fullName] == null) {
|
|
||||||
context.proto[fullName] = volume(context, volume)
|
|
||||||
}
|
|
||||||
|
|
||||||
this[physVolume.name ?: ""] = Proxy(this, fullName).apply {
|
|
||||||
withPosition(
|
|
||||||
context.lUnit,
|
|
||||||
physVolume.resolvePosition(context.root),
|
|
||||||
physVolume.resolveRotation(context.root),
|
|
||||||
physVolume.resolveScale(context.root)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GDMLTransformer.Action.REJECT -> {
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SolidGroup.addDivisionVolume(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
divisionVolume: GDMLDivisionVolume
|
|
||||||
) {
|
|
||||||
val volume: GDMLGroup = divisionVolume.volumeref.resolve(context.root)
|
|
||||||
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
|
|
||||||
|
|
||||||
//TODO add divisions
|
|
||||||
set(
|
|
||||||
Name.EMPTY,
|
|
||||||
volume(
|
|
||||||
context,
|
|
||||||
volume
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val solidsName = "solids".asName()
|
|
||||||
|
|
||||||
private fun volume(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
group: GDMLGroup
|
|
||||||
): SolidGroup {
|
|
||||||
return SolidGroup().apply {
|
|
||||||
if (group is GDMLVolume) {
|
|
||||||
val solid = group.solidref.resolve(context.root)
|
|
||||||
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
|
|
||||||
|
|
||||||
when (context.solidAction(solid)) {
|
|
||||||
GDMLTransformer.Action.ACCEPT -> {
|
|
||||||
addSolid(context, solid, solid.name) {
|
|
||||||
context.configureSolid(this, group, solid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GDMLTransformer.Action.CACHE -> {
|
|
||||||
if (context.proto[solid.name] == null) {
|
|
||||||
context.proto.addSolid(context, solid, solid.name) {
|
|
||||||
context.configureSolid(this, group, solid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ref(solid.name.asName(), solid.name)
|
|
||||||
}
|
|
||||||
GDMLTransformer.Action.REJECT -> {
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (val vol = group.placement) {
|
|
||||||
is GDMLPhysVolume -> addPhysicalVolume(context, vol)
|
|
||||||
is GDMLDivisionVolume -> addDivisionVolume(context, vol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
group.physVolumes.forEach { physVolume ->
|
|
||||||
addPhysicalVolume(context, physVolume)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun GDML.toVision(block: GDMLTransformer.() -> Unit = {}): SolidGroup {
|
|
||||||
val context = GDMLTransformer(this).apply(block)
|
|
||||||
return context.finalize(volume(context, world))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append gdml node to the group
|
|
||||||
*/
|
|
||||||
fun SolidGroup.gdml(gdml: GDML, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) {
|
|
||||||
val visual = gdml.toVision(transformer)
|
|
||||||
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
|
|
||||||
set(key, visual)
|
|
||||||
}
|
|
@ -0,0 +1,8 @@
|
|||||||
|
package hep.dataforge.vision.gdml
|
||||||
|
|
||||||
|
actual class Counter {
|
||||||
|
private var count: Int = 0
|
||||||
|
actual fun get(): Int = count
|
||||||
|
|
||||||
|
actual fun incrementAndGet(): Int = count++
|
||||||
|
}
|
@ -5,6 +5,9 @@ import nl.adaptivity.xmlutil.StAXReader
|
|||||||
import scientifik.gdml.GDML
|
import scientifik.gdml.GDML
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
actual typealias Counter = AtomicInteger
|
||||||
|
|
||||||
fun GDML.Companion.readFile(file: Path): GDML {
|
fun GDML.Companion.readFile(file: Path): GDML {
|
||||||
val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8")
|
val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8")
|
||||||
@ -15,4 +18,3 @@ fun SolidGroup.gdml(file: Path, key: String = "", transformer: GDMLTransformer.(
|
|||||||
val gdml = GDML.readFile(file)
|
val gdml = GDML.readFile(file)
|
||||||
gdml(gdml, key, transformer)
|
gdml(gdml, key, transformer)
|
||||||
}
|
}
|
||||||
|
|
@ -10,11 +10,10 @@ class TestConvertor {
|
|||||||
@Test
|
@Test
|
||||||
fun testBMNGeometry() {
|
fun testBMNGeometry() {
|
||||||
val stream = javaClass.getResourceAsStream("/gdml/BM@N.gdml")
|
val stream = javaClass.getResourceAsStream("/gdml/BM@N.gdml")
|
||||||
|
|
||||||
val xmlReader = StAXReader(stream, "UTF-8")
|
val xmlReader = StAXReader(stream, "UTF-8")
|
||||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||||
val visual = xml.toVision()
|
val vision = xml.toVision()
|
||||||
println(visual.stringify())
|
println(vision.stringify())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package hep.dataforge.vision.gdml
|
||||||
|
|
||||||
|
import hep.dataforge.vision.visitor.countDistinctBy
|
||||||
|
import hep.dataforge.vision.visitor.flowStatistics
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import nl.adaptivity.xmlutil.StAXReader
|
||||||
|
import scientifik.gdml.GDML
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
suspend fun main() {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
//val stream = SingleChildReducer::class.java.getResourceAsStream("/gdml/BM@N.gdml")
|
||||||
|
val stream =
|
||||||
|
File("D:\\Work\\Projects\\dataforge-vis\\visionforge-gdml\\src\\jvmTest\\resources\\gdml\\BM@N.gdml").inputStream()
|
||||||
|
|
||||||
|
val xmlReader = StAXReader(stream, "UTF-8")
|
||||||
|
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||||
|
val vision = xml.toVision()
|
||||||
|
|
||||||
|
|
||||||
|
vision.flowStatistics().countDistinctBy { it.type }.forEach { (depth, size) ->
|
||||||
|
println("$depth\t$size")
|
||||||
|
}
|
||||||
|
|
||||||
|
println("***REDUCED***")
|
||||||
|
|
||||||
|
vision.optimizeGdml()
|
||||||
|
|
||||||
|
vision.flowStatistics().countDistinctBy { it.type }.forEach { (depth, size) ->
|
||||||
|
println("$depth\t$size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,9 @@ import kotlinx.serialization.Transient
|
|||||||
import kotlinx.serialization.UseSerializers
|
import kotlinx.serialization.UseSerializers
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractProxy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A proxy [Solid] to reuse a template object
|
* A proxy [Solid] to reuse a template object
|
||||||
*/
|
*/
|
||||||
@ -39,8 +42,7 @@ class Proxy private constructor(
|
|||||||
get() = (parent as? SolidGroup)?.getPrototype(templateName)
|
get() = (parent as? SolidGroup)?.getPrototype(templateName)
|
||||||
?: error("Prototype with name $templateName not found in $parent")
|
?: error("Prototype with name $templateName not found in $parent")
|
||||||
|
|
||||||
override val styleSheet: StyleSheet
|
override val styleSheet: StyleSheet get() = parent?.styleSheet ?: StyleSheet(this)
|
||||||
get() = parent?.styleSheet ?: StyleSheet(this)
|
|
||||||
|
|
||||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||||
return if (inherit) {
|
return if (inherit) {
|
||||||
|
@ -103,9 +103,9 @@ interface Solid : Vision {
|
|||||||
* Count number of layers to the top object. Return 1 if this is top layer
|
* Count number of layers to the top object. Return 1 if this is top layer
|
||||||
*/
|
*/
|
||||||
var Solid.layer: Int
|
var Solid.layer: Int
|
||||||
get() = getItem(LAYER_KEY).int ?: 0
|
get() = properties?.getItem(LAYER_KEY).int ?: 0
|
||||||
set(value) {
|
set(value) {
|
||||||
setItem(LAYER_KEY, value.asValue())
|
config[LAYER_KEY] = value.asValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Renderer<Solid>.render(meta: Meta = Meta.EMPTY, action: SolidGroup.() -> Unit) =
|
fun Renderer<Solid>.render(meta: Meta = Meta.EMPTY, action: SolidGroup.() -> Unit) =
|
||||||
|
@ -35,7 +35,7 @@ abstract class MeshThreeFactory<in T : Solid>(
|
|||||||
|
|
||||||
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
||||||
|
|
||||||
val mesh = Mesh(geometry, getMaterial(obj)).apply {
|
val mesh = Mesh(geometry, getMaterial(obj, true)).apply {
|
||||||
matrixAutoUpdate = false
|
matrixAutoUpdate = false
|
||||||
applyEdges(obj)
|
applyEdges(obj)
|
||||||
applyWireFrame(obj)
|
applyWireFrame(obj)
|
||||||
@ -50,7 +50,7 @@ abstract class MeshThreeFactory<in T : Solid>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//add listener to object properties
|
//add listener to object properties
|
||||||
obj.onPropertyChange(this) { name->
|
obj.onPropertyChange(this) { name ->
|
||||||
when {
|
when {
|
||||||
name.startsWith(Solid.GEOMETRY_KEY) -> {
|
name.startsWith(Solid.GEOMETRY_KEY) -> {
|
||||||
val oldGeometry = mesh.geometry as BufferGeometry
|
val oldGeometry = mesh.geometry as BufferGeometry
|
||||||
@ -80,22 +80,28 @@ abstract class MeshThreeFactory<in T : Solid>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Mesh.applyEdges(obj: Solid) {
|
fun Mesh.applyEdges(obj: Solid) {
|
||||||
children.find { it.name == "@edges" }?.let {
|
val edges = children.find { it.name == "@edges" } as? LineSegments
|
||||||
remove(it)
|
|
||||||
(it as LineSegments).dispose()
|
|
||||||
}
|
|
||||||
//inherited edges definition, enabled by default
|
//inherited edges definition, enabled by default
|
||||||
if (obj.getItem(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) {
|
if (obj.getItem(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) {
|
||||||
|
val bufferGeometry = geometry as? BufferGeometry ?: return
|
||||||
val material = ThreeMaterials.getLineMaterial(obj.getItem(MeshThreeFactory.EDGES_MATERIAL_KEY).node)
|
val material = ThreeMaterials.getLineMaterial(obj.getItem(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true)
|
||||||
|
if (edges == null) {
|
||||||
add(
|
add(
|
||||||
LineSegments(
|
LineSegments(
|
||||||
EdgesGeometry(geometry as BufferGeometry),
|
EdgesGeometry(bufferGeometry),
|
||||||
material
|
material
|
||||||
).apply {
|
).apply {
|
||||||
name = "@edges"
|
name = "@edges"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
edges.material = material
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
edges?.let {
|
||||||
|
remove(it)
|
||||||
|
it.dispose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,10 +112,11 @@ fun Mesh.applyWireFrame(obj: Solid) {
|
|||||||
}
|
}
|
||||||
//inherited wireframe definition, disabled by default
|
//inherited wireframe definition, disabled by default
|
||||||
if (obj.getItem(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
|
if (obj.getItem(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
|
||||||
val material = ThreeMaterials.getLineMaterial(obj.getItem(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node)
|
val bufferGeometry = geometry as? BufferGeometry ?: return
|
||||||
|
val material = ThreeMaterials.getLineMaterial(obj.getItem(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true)
|
||||||
add(
|
add(
|
||||||
LineSegments(
|
LineSegments(
|
||||||
WireframeGeometry(geometry as BufferGeometry),
|
WireframeGeometry(bufferGeometry),
|
||||||
material
|
material
|
||||||
).apply {
|
).apply {
|
||||||
name = "@wireframe"
|
name = "@wireframe"
|
||||||
|
@ -58,7 +58,7 @@ fun Object3D.updatePosition(obj: Vision) {
|
|||||||
*/
|
*/
|
||||||
fun Object3D.updateProperty(source: Vision, propertyName: Name) {
|
fun Object3D.updateProperty(source: Vision, propertyName: Name) {
|
||||||
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
||||||
this.material = getMaterial(source)
|
this.material = getMaterial(source, false)
|
||||||
} else if (
|
} else if (
|
||||||
propertyName.startsWith(Solid.POSITION_KEY)
|
propertyName.startsWith(Solid.POSITION_KEY)
|
||||||
|| propertyName.startsWith(Solid.ROTATION)
|
|| propertyName.startsWith(Solid.ROTATION)
|
||||||
|
@ -21,7 +21,7 @@ object ThreeLabelFactory : ThreeFactory<SolidLabel> {
|
|||||||
height = 1
|
height = 1
|
||||||
curveSegments = 1
|
curveSegments = 1
|
||||||
})
|
})
|
||||||
return Mesh(textGeo, getMaterial(obj)).apply {
|
return Mesh(textGeo, getMaterial(obj,true)).apply {
|
||||||
updatePosition(obj)
|
updatePosition(obj)
|
||||||
obj.onPropertyChange(this@ThreeLabelFactory) { _ ->
|
obj.onPropertyChange(this@ThreeLabelFactory) { _ ->
|
||||||
//TODO
|
//TODO
|
||||||
|
@ -18,7 +18,7 @@ object ThreeLineFactory : ThreeFactory<PolyLine> {
|
|||||||
vertices = obj.points.toTypedArray()
|
vertices = obj.points.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
val material = ThreeMaterials.getLineMaterial(obj.getItem(MeshThreeFactory.EDGES_MATERIAL_KEY).node)
|
val material = ThreeMaterials.getLineMaterial(obj.getItem(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true)
|
||||||
|
|
||||||
material.linewidth = obj.thickness.toDouble()
|
material.linewidth = obj.thickness.toDouble()
|
||||||
material.color = obj.color?.let { Color(it) } ?: DEFAULT_LINE_COLOR
|
material.color = obj.color?.let { Color(it) } ?: DEFAULT_LINE_COLOR
|
||||||
|
@ -33,18 +33,27 @@ object ThreeMaterials {
|
|||||||
linewidth = 8.0
|
linewidth = 8.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLineMaterial(meta: Meta?): LineBasicMaterial {
|
private val lineMaterialCache = HashMap<Meta, LineBasicMaterial>()
|
||||||
if (meta == null) return DEFAULT_LINE
|
|
||||||
return LineBasicMaterial().apply {
|
private fun buildLineMaterial(meta: Meta): LineBasicMaterial = LineBasicMaterial().apply {
|
||||||
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_LINE_COLOR
|
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_LINE_COLOR
|
||||||
opacity = meta[SolidMaterial.OPACITY_KEY].double ?: 1.0
|
opacity = meta[SolidMaterial.OPACITY_KEY].double ?: 1.0
|
||||||
transparent = opacity < 1.0
|
transparent = opacity < 1.0
|
||||||
linewidth = meta["thickness"].double ?: 1.0
|
linewidth = meta["thickness"].double ?: 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getLineMaterial(meta: Meta?, cache: Boolean): LineBasicMaterial {
|
||||||
|
if (meta == null) return DEFAULT_LINE
|
||||||
|
return if (cache) {
|
||||||
|
lineMaterialCache.getOrPut(meta) { buildLineMaterial(meta) }
|
||||||
|
} else {
|
||||||
|
buildLineMaterial(meta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMaterial(vision3D: Vision): Material {
|
private val materialCache = HashMap<Meta, Material>()
|
||||||
val meta = vision3D.getItem(SolidMaterial.MATERIAL_KEY).node ?: return DEFAULT
|
|
||||||
|
private fun buildMaterial(meta: Meta): Material {
|
||||||
return if (meta[SolidMaterial.SPECULAR_COLOR_KEY] != null) {
|
return if (meta[SolidMaterial.SPECULAR_COLOR_KEY] != null) {
|
||||||
MeshPhongMaterial().apply {
|
MeshPhongMaterial().apply {
|
||||||
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR
|
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR
|
||||||
@ -65,6 +74,15 @@ object ThreeMaterials {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getMaterial(vision3D: Vision, cache: Boolean): Material {
|
||||||
|
val meta = vision3D.getItem(SolidMaterial.MATERIAL_KEY).node ?: return DEFAULT
|
||||||
|
return if (cache) {
|
||||||
|
materialCache.getOrPut(meta) { buildMaterial(meta) }
|
||||||
|
} else {
|
||||||
|
buildMaterial(meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +70,7 @@ class ThreePlugin : AbstractPlugin() {
|
|||||||
|
|
||||||
obj.onChildrenChange(this) { name, child ->
|
obj.onChildrenChange(this) { name, child ->
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
logger.error { "Children change with empty namr on $group" }
|
logger.error { "Children change with empty name on $group" }
|
||||||
return@onChildrenChange
|
return@onChildrenChange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,26 @@ import hep.dataforge.names.toName
|
|||||||
import hep.dataforge.vision.solid.Proxy
|
import hep.dataforge.vision.solid.Proxy
|
||||||
import hep.dataforge.vision.solid.Proxy.Companion.PROXY_CHILD_PROPERTY_PREFIX
|
import hep.dataforge.vision.solid.Proxy.Companion.PROXY_CHILD_PROPERTY_PREFIX
|
||||||
import hep.dataforge.vision.solid.Solid
|
import hep.dataforge.vision.solid.Solid
|
||||||
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
|
import info.laht.threekt.objects.Mesh
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
|
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
|
||||||
private val cache = HashMap<Solid, Object3D>()
|
private val cache = HashMap<Solid, Object3D>()
|
||||||
|
|
||||||
override val type = Proxy::class
|
override val type: KClass<Proxy> = Proxy::class
|
||||||
|
|
||||||
|
private fun Object3D.replicate(): Object3D {
|
||||||
|
return when (this) {
|
||||||
|
is Mesh -> Mesh(geometry as BufferGeometry, material)
|
||||||
|
else -> clone(false)
|
||||||
|
}.also { obj: Object3D ->
|
||||||
|
children.forEach { child: Object3D ->
|
||||||
|
obj.add(child.replicate())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun invoke(obj: Proxy): Object3D {
|
override fun invoke(obj: Proxy): Object3D {
|
||||||
val template = obj.prototype
|
val template = obj.prototype
|
||||||
@ -17,16 +31,16 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
|
|||||||
three.buildObject3D(template)
|
three.buildObject3D(template)
|
||||||
}
|
}
|
||||||
|
|
||||||
//val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material)
|
val object3D: Object3D = cachedObject.replicate()
|
||||||
val object3D = cachedObject.clone()
|
|
||||||
object3D.updatePosition(obj)
|
object3D.updatePosition(obj)
|
||||||
|
|
||||||
obj.onPropertyChange(this) { name->
|
obj.onPropertyChange(this) { name ->
|
||||||
if (name.first()?.body == PROXY_CHILD_PROPERTY_PREFIX) {
|
if (name.first()?.body == PROXY_CHILD_PROPERTY_PREFIX) {
|
||||||
val childName = name.first()?.index?.toName() ?: error("Wrong syntax for proxy child property: '$name'")
|
val childName = name.first()?.index?.toName() ?: error("Wrong syntax for proxy child property: '$name'")
|
||||||
val propertyName = name.cutFirst()
|
val propertyName = name.cutFirst()
|
||||||
val proxyChild = obj[childName] ?: error("Proxy child with name '$childName' not found")
|
val proxyChild = obj[childName] ?: error("Proxy child with name '$childName' not found")
|
||||||
val child = object3D.findChild(childName)?: error("Object child with name '$childName' not found")
|
val child = object3D.findChild(childName) ?: error("Object child with name '$childName' not found")
|
||||||
child.updateProperty(proxyChild, propertyName)
|
child.updateProperty(proxyChild, propertyName)
|
||||||
} else {
|
} else {
|
||||||
object3D.updateProperty(obj, name)
|
object3D.updateProperty(obj, name)
|
||||||
|
@ -34,4 +34,7 @@ import info.laht.threekt.materials.Material
|
|||||||
|
|
||||||
open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D {
|
open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D {
|
||||||
constructor(geometry: Geometry, material: Material)
|
constructor(geometry: Geometry, material: Material)
|
||||||
|
|
||||||
|
var geometry: BufferGeometry
|
||||||
|
var material: Material
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user