New properties #34
@ -17,5 +17,5 @@ fun main() {
|
||||
}
|
||||
}
|
||||
|
||||
fragment.makeFile(resourceLocation = ResourceLocation.LOCAL)
|
||||
fragment.makeFile(resourceLocation = ResourceLocation.SYSTEM)
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package hep.dataforge.vision.solid
|
||||
|
||||
import hep.dataforge.meta.DFExperimental
|
||||
import hep.dataforge.vision.ResourceLocation
|
||||
import hep.dataforge.vision.VisionManager
|
||||
import hep.dataforge.vision.html.fragment
|
||||
import hep.dataforge.vision.three.server.makeFile
|
||||
import hep.dataforge.vision.three.server.solid
|
||||
import kotlinx.html.h1
|
||||
import java.nio.file.Paths
|
||||
import kotlin.random.Random
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
fun main() {
|
||||
val random = Random(112233)
|
||||
val fragment = VisionManager.fragment {
|
||||
h1 { +"Happy new year!" }
|
||||
vision {
|
||||
solid {
|
||||
repeat(100) {
|
||||
sphere(5) {
|
||||
x = random.nextDouble(-300.0, 300.0)
|
||||
y = random.nextDouble(-300.0, 300.0)
|
||||
z = random.nextDouble(-300.0, 300.0)
|
||||
material {
|
||||
color(random.nextInt())
|
||||
specularColor(random.nextInt())
|
||||
}
|
||||
detail = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment.makeFile(Paths.get("stars.html"), resourceLocation = ResourceLocation.EMBED)
|
||||
}
|
@ -32,7 +32,7 @@ public external interface PropertyEditorProps : RProps {
|
||||
public var provider: MutableItemProvider
|
||||
|
||||
/**
|
||||
* Provide default item (greyed out if used
|
||||
* Provide default item (greyed out if used)
|
||||
*/
|
||||
public var defaultProvider: ItemProvider?
|
||||
|
||||
@ -65,11 +65,12 @@ private val PropertyEditorItem: FunctionalComponent<PropertyEditorProps> =
|
||||
|
||||
private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
||||
var expanded: Boolean by useState { true }
|
||||
val itemName = useMemo( { props.name ?: Name.EMPTY }, arrayOf(props.name))
|
||||
var item: MetaItem? by useState { props.provider.getItem(itemName) }
|
||||
val itemName = props.name ?: Name.EMPTY
|
||||
val descriptorItem: ItemDescriptor? =
|
||||
useMemo({ props.descriptor?.get(itemName) }, arrayOf(props.descriptor, itemName))
|
||||
|
||||
var item: MetaItem? by useState { props.provider.getItem(itemName) }
|
||||
|
||||
if (descriptorItem?.hidden == true) return //fail fast for hidden property
|
||||
|
||||
var actualItem: MetaItem? by useState {
|
||||
|
@ -9,12 +9,10 @@ import hep.dataforge.vision.widgetType
|
||||
import kotlinx.html.InputType
|
||||
import kotlinx.html.js.onChangeFunction
|
||||
import kotlinx.html.js.onKeyDownFunction
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
import org.w3c.dom.HTMLSelectElement
|
||||
import org.w3c.dom.events.Event
|
||||
import react.*
|
||||
import react.dom.defaultValue
|
||||
import react.dom.option
|
||||
import styled.styledInput
|
||||
import styled.styledSelect
|
||||
@ -25,116 +23,142 @@ public external interface ValueChooserProps : RProps {
|
||||
public var valueChanged: ((Value?) -> Unit)?
|
||||
}
|
||||
|
||||
public external interface ValueChooserState : RState {
|
||||
public var rawInput: Boolean?
|
||||
@JsExport
|
||||
public val StringValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
functionalComponent("StringValueChooser") { props ->
|
||||
var value by useState(props.item.string ?: "")
|
||||
val keyDown: (Event) -> Unit = { event ->
|
||||
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
||||
value = (event.target as HTMLInputElement).value
|
||||
if(value!= props.item.string) {
|
||||
props.valueChanged?.invoke(value.asValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
val handleChange: (Event) -> Unit = {
|
||||
value = (it.target as HTMLInputElement).value
|
||||
}
|
||||
styledInput(type = InputType.text) {
|
||||
attrs {
|
||||
this.value = value
|
||||
onKeyDownFunction = keyDown
|
||||
onChangeFunction = handleChange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public class ValueChooserComponent(props: ValueChooserProps) : RComponent<ValueChooserProps, ValueChooserState>(props) {
|
||||
private val element = createRef<HTMLElement>()
|
||||
|
||||
private fun getValue(): Value? {
|
||||
val element = element.current ?: return null//state.element ?: return null
|
||||
return when (element) {
|
||||
is HTMLInputElement -> if (element.type == "checkbox") {
|
||||
if (element.checked) True else False
|
||||
} else {
|
||||
element.value.asValue()
|
||||
public val BooleanValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
functionalComponent("BooleanValueChooser") { props ->
|
||||
var checkedValue by useState(props.item.boolean ?: false)
|
||||
val handleChange: (Event) -> Unit = {
|
||||
val newValue = (it.target as HTMLInputElement).checked
|
||||
checkedValue = newValue
|
||||
props.valueChanged?.invoke(newValue.asValue())
|
||||
}
|
||||
is HTMLSelectElement -> element.value.asValue()
|
||||
else -> error("Unknown event target: $element")
|
||||
}
|
||||
}
|
||||
|
||||
private val commit: (Event) -> Unit = { _ ->
|
||||
props.valueChanged?.invoke(getValue())
|
||||
}
|
||||
|
||||
private val keyDown: (Event) -> Unit = { event ->
|
||||
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
||||
commit(event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldComponentUpdate(
|
||||
nextProps: ValueChooserProps,
|
||||
nextState: ValueChooserState
|
||||
): Boolean = nextProps.item !== props.item
|
||||
|
||||
override fun componentDidUpdate(prevProps: ValueChooserProps, prevState: ValueChooserState, snapshot: Any) {
|
||||
(element.current as? HTMLInputElement)?.let { element ->
|
||||
if (element.type == "checkbox") {
|
||||
element.defaultChecked = props.item?.boolean ?: false
|
||||
} else {
|
||||
element.defaultValue = props.item?.string ?: ""
|
||||
}
|
||||
element.indeterminate = props.item == null
|
||||
}
|
||||
}
|
||||
|
||||
private fun RBuilder.stringInput() = styledInput(type = InputType.text) {
|
||||
attrs {
|
||||
this.defaultValue = props.item?.string ?: ""
|
||||
onKeyDownFunction = keyDown
|
||||
}
|
||||
ref = element
|
||||
}
|
||||
|
||||
override fun RBuilder.render() {
|
||||
val descriptor = props.descriptor
|
||||
val type = descriptor?.type?.firstOrNull()
|
||||
when {
|
||||
state.rawInput == true -> stringInput()
|
||||
descriptor?.widgetType == "color" -> styledInput(type = InputType.color) {
|
||||
ref = element
|
||||
attrs {
|
||||
this.defaultValue = props.item?.value?.let { value ->
|
||||
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
||||
else value.string
|
||||
} ?: "#000000"
|
||||
onChangeFunction = commit
|
||||
}
|
||||
}
|
||||
type == ValueType.BOOLEAN -> {
|
||||
styledInput(type = InputType.checkBox) {
|
||||
ref = element
|
||||
attrs {
|
||||
defaultChecked = props.item?.boolean ?: false
|
||||
onChangeFunction = commit
|
||||
this.attributes["indeterminate"] = (checkedValue == null).toString()
|
||||
checked = checkedValue
|
||||
onChangeFunction = handleChange
|
||||
}
|
||||
}
|
||||
}
|
||||
type == ValueType.NUMBER -> styledInput(type = InputType.number) {
|
||||
ref = element
|
||||
|
||||
@JsExport
|
||||
public val NumberValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
functionalComponent("NumberValueChooser") { props ->
|
||||
var value by useState(props.item.string ?: "")
|
||||
val keyDown: (Event) -> Unit = { event ->
|
||||
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
||||
value = (event.target as HTMLInputElement).value
|
||||
val number = value.toDoubleOrNull()
|
||||
if (number == null) {
|
||||
console.error("The input value $value is not a number")
|
||||
} else {
|
||||
props.valueChanged?.invoke(number.asValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
val handleChange: (Event) -> Unit = {
|
||||
value = (it.target as HTMLInputElement).value
|
||||
}
|
||||
styledInput(type = InputType.number) {
|
||||
attrs {
|
||||
descriptor.attributes["step"].string?.let {
|
||||
this.value = value
|
||||
onKeyDownFunction = keyDown
|
||||
onChangeFunction = handleChange
|
||||
props.descriptor?.attributes?.get("step").string?.let {
|
||||
step = it
|
||||
}
|
||||
descriptor.attributes["min"].string?.let {
|
||||
props.descriptor?.attributes?.get("min").string?.let {
|
||||
min = it
|
||||
}
|
||||
descriptor.attributes["max"].string?.let {
|
||||
props.descriptor?.attributes?.get("max").string?.let {
|
||||
max = it
|
||||
}
|
||||
defaultValue = props.item?.string ?: ""
|
||||
onKeyDownFunction = keyDown
|
||||
}
|
||||
}
|
||||
descriptor?.allowedValues?.isNotEmpty() ?: false -> styledSelect {
|
||||
descriptor!!.allowedValues.forEach {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ComboValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
functionalComponent("ComboValueChooser") { props ->
|
||||
var selected by useState(props.item.string ?: "")
|
||||
val handleChange: (Event) -> Unit = {
|
||||
selected = (it.target as HTMLSelectElement).value
|
||||
props.valueChanged?.invoke(selected.asValue())
|
||||
}
|
||||
styledSelect {
|
||||
props.descriptor?.allowedValues?.forEach {
|
||||
option {
|
||||
+it.string
|
||||
}
|
||||
}
|
||||
ref = element
|
||||
attrs {
|
||||
this.value = props.item?.string ?: ""
|
||||
multiple = false
|
||||
onChangeFunction = commit
|
||||
onChangeFunction = handleChange
|
||||
}
|
||||
}
|
||||
else -> stringInput()
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ColorValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
functionalComponent("ColorValueChooser") { props ->
|
||||
var value by useState(
|
||||
props.item.value?.let { value ->
|
||||
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
||||
else value.string
|
||||
} ?: "#000000"
|
||||
)
|
||||
val handleChange: (Event) -> Unit = {
|
||||
value = (it.target as HTMLInputElement).value
|
||||
props.valueChanged?.invoke(value.asValue())
|
||||
}
|
||||
styledInput(type = InputType.color) {
|
||||
attrs {
|
||||
this.value = value
|
||||
onChangeFunction = handleChange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ValueChooser: FunctionalComponent<ValueChooserProps> = functionalComponent("ValueChooser") { props ->
|
||||
val rawInput by useState(false)
|
||||
|
||||
val descriptor = props.descriptor
|
||||
val type = descriptor?.type?.firstOrNull()
|
||||
|
||||
when {
|
||||
rawInput -> child(StringValueChooser, props)
|
||||
descriptor?.widgetType == "color" -> child(ColorValueChooser, props)
|
||||
type == ValueType.BOOLEAN -> child(BooleanValueChooser, props)
|
||||
type == ValueType.NUMBER -> child(NumberValueChooser, props)
|
||||
descriptor?.allowedValues?.isNotEmpty() ?: false -> child(ComboValueChooser, props)
|
||||
//TODO handle lists
|
||||
else -> child(StringValueChooser, props)
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,9 +166,9 @@ internal fun RBuilder.valueChooser(
|
||||
name: Name,
|
||||
item: MetaItem?,
|
||||
descriptor: ValueDescriptor? = null,
|
||||
callback: (Value?) -> Unit
|
||||
callback: (Value?) -> Unit,
|
||||
) {
|
||||
child(ValueChooserComponent::class) {
|
||||
child(ValueChooser) {
|
||||
attrs {
|
||||
key = name.toString()
|
||||
this.item = item
|
||||
|
@ -1,5 +1,6 @@
|
||||
package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.DFExperimental
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.MutableItemProvider
|
||||
@ -73,6 +74,7 @@ public interface Vision : Described {
|
||||
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
|
||||
* if it should include inherited properties etc.
|
||||
*/
|
||||
@DFExperimental
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
public val propertyChanges: Flow<Name>
|
||||
get() = callbackFlow<Name> {
|
||||
|
@ -73,7 +73,7 @@ public abstract class VisionTagConsumer<R>(
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
public inline fun <T> TagConsumer<T>.vision(
|
||||
name: String,
|
||||
name: String = DEFAULT_VISION_NAME,
|
||||
visionProvider: VisionOutput.() -> Vision,
|
||||
): T = vision(name.toName(), visionProvider)
|
||||
|
||||
@ -103,5 +103,7 @@ public abstract class VisionTagConsumer<R>(
|
||||
public const val OUTPUT_NAME_ATTRIBUTE: String = "data-output-name"
|
||||
public const val OUTPUT_ENDPOINT_ATTRIBUTE: String = "data-output-endpoint"
|
||||
public const val DEFAULT_ENDPOINT: String = "."
|
||||
|
||||
public const val DEFAULT_VISION_NAME = "vision"
|
||||
}
|
||||
}
|
@ -18,9 +18,10 @@ public fun FlowContent.embedVisionFragment(
|
||||
val consumer = object : VisionTagConsumer<Any?>(consumer, idPrefix) {
|
||||
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
|
||||
script {
|
||||
type = "text/json"
|
||||
attributes["class"] = OUTPUT_DATA_CLASS
|
||||
unsafe {
|
||||
+manager.encodeToString(vision)
|
||||
+"\n${manager.encodeToString(vision)}\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public enum class ResourceLocation {
|
||||
EMBED
|
||||
}
|
||||
|
||||
internal const val DATAFORGE_ASSETS_PATH = ".dataforge/assets"
|
||||
internal const val VISIONFORGE_ASSETS_PATH = ".dataforge/vision/assets"
|
||||
|
||||
|
||||
/**
|
||||
@ -43,14 +43,14 @@ internal const val DATAFORGE_ASSETS_PATH = ".dataforge/assets"
|
||||
* @param
|
||||
*/
|
||||
internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String): Path {
|
||||
val fullPath = basePath.resolveSibling(filePath).toAbsolutePath()
|
||||
val fullPath = basePath.resolveSibling(filePath).toAbsolutePath().resolve(resource)
|
||||
|
||||
if (Files.exists(fullPath)) {
|
||||
//TODO checksum
|
||||
} else {
|
||||
//TODO add logging
|
||||
|
||||
val bytes = VisionManager::class.java.getResourceAsStream(resource).readAllBytes()
|
||||
val bytes = VisionManager::class.java.getResourceAsStream("/$resource").readAllBytes()
|
||||
Files.createDirectories(fullPath.parent)
|
||||
Files.write(fullPath, bytes, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
|
||||
}
|
||||
@ -58,7 +58,7 @@ internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String):
|
||||
return if (basePath.isAbsolute && fullPath.startsWith(basePath)) {
|
||||
basePath.relativize(fullPath)
|
||||
} else {
|
||||
filePath
|
||||
fullPath
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ internal fun embedScriptHeader(resource: String): HtmlFragment = {
|
||||
script {
|
||||
type = "text/javascript"
|
||||
unsafe {
|
||||
val bytes = VisionManager::class.java.getResourceAsStream(resource).readAllBytes()
|
||||
val bytes = VisionManager::class.java.getResourceAsStream("/$resource").readAllBytes()
|
||||
+bytes.toString(Charsets.UTF_8)
|
||||
}
|
||||
}
|
||||
@ -108,12 +108,12 @@ public fun Context.Companion.scriptHeader(
|
||||
val targetPath = when (resourceLocation) {
|
||||
ResourceLocation.LOCAL -> checkOrStoreFile(
|
||||
basePath,
|
||||
Path.of(DATAFORGE_ASSETS_PATH),
|
||||
Path.of(VISIONFORGE_ASSETS_PATH),
|
||||
scriptResource
|
||||
)
|
||||
ResourceLocation.SYSTEM -> checkOrStoreFile(
|
||||
Path.of("."),
|
||||
Path.of(System.getProperty("user.home")).resolve(DATAFORGE_ASSETS_PATH),
|
||||
Path.of(System.getProperty("user.home")).resolve(VISIONFORGE_ASSETS_PATH),
|
||||
scriptResource
|
||||
)
|
||||
ResourceLocation.EMBED -> null
|
||||
|
@ -20,8 +20,10 @@ public fun HtmlVisionFragment.makeFile(
|
||||
title: String = "VisionForge page",
|
||||
show: Boolean = true,
|
||||
) {
|
||||
val actualFile = path ?: Files.createTempFile("tempPlot", ".html")
|
||||
Files.createDirectories(actualFile.parent)
|
||||
val actualFile = path?.let {
|
||||
Path.of(System.getProperty("user.home")).resolve(path)
|
||||
} ?: Files.createTempFile("tempPlot", ".html")
|
||||
//Files.createDirectories(actualFile.parent)
|
||||
val htmlString = createHTML().apply {
|
||||
head {
|
||||
meta {
|
||||
|
@ -130,7 +130,7 @@ class FX3DPlugin : AbstractPlugin() {
|
||||
}
|
||||
|
||||
companion object : PluginFactory<FX3DPlugin> {
|
||||
override val tag = PluginTag("visual.fx3D", PluginTag.DATAFORGE_GROUP)
|
||||
override val tag = PluginTag("vision.fx3D", PluginTag.DATAFORGE_GROUP)
|
||||
override val type = FX3DPlugin::class
|
||||
override fun invoke(meta: Meta, context: Context) = FX3DPlugin()
|
||||
}
|
||||
|
@ -44,13 +44,13 @@ public interface Solid : Vision {
|
||||
public val Y_POSITION_KEY: Name = POSITION_KEY + Y_KEY
|
||||
public val Z_POSITION_KEY: Name = POSITION_KEY + Z_KEY
|
||||
|
||||
public val ROTATION: Name = "rotation".asName()
|
||||
public val ROTATION_KEY: Name = "rotation".asName()
|
||||
|
||||
public val X_ROTATION_KEY: Name = ROTATION + X_KEY
|
||||
public val Y_ROTATION_KEY: Name = ROTATION + Y_KEY
|
||||
public val Z_ROTATION_KEY: Name = ROTATION + Z_KEY
|
||||
public val X_ROTATION_KEY: Name = ROTATION_KEY + X_KEY
|
||||
public val Y_ROTATION_KEY: Name = ROTATION_KEY + Y_KEY
|
||||
public val Z_ROTATION_KEY: Name = ROTATION_KEY + Z_KEY
|
||||
|
||||
public val ROTATION_ORDER_KEY: Name = ROTATION + "order"
|
||||
public val ROTATION_ORDER_KEY: Name = ROTATION_KEY + "order"
|
||||
|
||||
public val SCALE_KEY: Name = "scale".asName()
|
||||
|
||||
|
@ -35,6 +35,6 @@ internal fun Meta.toVector(default: Float = 0f) = Point3D(
|
||||
|
||||
internal fun Solid.updatePosition(meta: Meta?) {
|
||||
meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it }
|
||||
meta[Solid.ROTATION].node?.toVector()?.let { rotation = it }
|
||||
meta[Solid.ROTATION_KEY].node?.toVector()?.let { rotation = it }
|
||||
meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it }
|
||||
}
|
@ -33,7 +33,7 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
}
|
||||
|
||||
public companion object : PluginFactory<SolidManager> {
|
||||
override val tag: PluginTag = PluginTag(name = "visual.spatial", group = PluginTag.DATAFORGE_GROUP)
|
||||
override val tag: PluginTag = PluginTag(name = "vision.solid", group = PluginTag.DATAFORGE_GROUP)
|
||||
override val type: KClass<out SolidManager> = SolidManager::class
|
||||
override fun invoke(meta: Meta, context: Context): SolidManager = SolidManager(meta)
|
||||
|
||||
|
@ -19,12 +19,12 @@ public class SolidMaterial : Scheme() {
|
||||
/**
|
||||
* Primary web-color for the material
|
||||
*/
|
||||
public var color: ColorAccessor = ColorAccessor(this, COLOR_KEY)
|
||||
public val color: ColorAccessor = ColorAccessor(this, COLOR_KEY)
|
||||
|
||||
/**
|
||||
* Specular color for phong material
|
||||
*/
|
||||
public var specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY)
|
||||
public val specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY)
|
||||
|
||||
/**
|
||||
* Opacity
|
||||
|
@ -1,9 +1,12 @@
|
||||
package hep.dataforge.vision.solid.specifications
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.Scheme
|
||||
import hep.dataforge.meta.SchemeSpec
|
||||
import hep.dataforge.meta.boolean
|
||||
import hep.dataforge.meta.double
|
||||
|
||||
public class Axes : Scheme() {
|
||||
public var visible: Boolean by boolean(rootNode?.isEmpty() != false)
|
||||
public var visible: Boolean by boolean(false)
|
||||
public var size: Double by double(AXIS_SIZE)
|
||||
public var width: Double by double(AXIS_WIDTH)
|
||||
|
||||
|
@ -20,6 +20,7 @@ import info.laht.threekt.external.controls.OrbitControls
|
||||
import info.laht.threekt.external.controls.TrackballControls
|
||||
import info.laht.threekt.geometries.EdgesGeometry
|
||||
import info.laht.threekt.helpers.AxesHelper
|
||||
import info.laht.threekt.lights.AmbientLight
|
||||
import info.laht.threekt.materials.LineBasicMaterial
|
||||
import info.laht.threekt.math.Vector2
|
||||
import info.laht.threekt.objects.LineSegments
|
||||
@ -50,8 +51,11 @@ public class ThreeCanvas(
|
||||
public var axes: AxesHelper = AxesHelper(options.axes.size.toInt()).apply { visible = options.axes.visible }
|
||||
private set
|
||||
|
||||
private val light = AmbientLight(0x404040)
|
||||
|
||||
private val scene: Scene = Scene().apply {
|
||||
add(axes)
|
||||
add(light)
|
||||
}
|
||||
|
||||
public var camera: PerspectiveCamera = buildCamera(options.camera)
|
||||
@ -66,6 +70,7 @@ public class ThreeCanvas(
|
||||
}
|
||||
|
||||
private val canvas = (renderer.domElement as HTMLCanvasElement).apply {
|
||||
className += "three-canvas"
|
||||
width = 600
|
||||
height = 600
|
||||
style.apply {
|
||||
@ -131,7 +136,7 @@ public class ThreeCanvas(
|
||||
}
|
||||
|
||||
internal fun attach(element: Element) {
|
||||
check(element.children.length == 0){"The element for Three canvas is not empty"}
|
||||
check(element.getElementsByClassName("three-canvas").length == 0){"Three canvas already created in this element"}
|
||||
element.appendChild(canvas)
|
||||
updateSize()
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public fun Object3D.updateProperty(source: Vision, propertyName: Name) {
|
||||
updateMaterialProperty(source, propertyName)
|
||||
} else if (
|
||||
propertyName.startsWith(Solid.POSITION_KEY)
|
||||
|| propertyName.startsWith(Solid.ROTATION)
|
||||
|| propertyName.startsWith(Solid.ROTATION_KEY)
|
||||
|| propertyName.startsWith(Solid.SCALE_KEY)
|
||||
) {
|
||||
//update position of mesh using this object
|
||||
|
@ -70,7 +70,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
obj.onPropertyChange(updateScope) { name ->
|
||||
if (
|
||||
name.startsWith(Solid.POSITION_KEY) ||
|
||||
name.startsWith(Solid.ROTATION) ||
|
||||
name.startsWith(Solid.ROTATION_KEY) ||
|
||||
name.startsWith(Solid.SCALE_KEY)
|
||||
) {
|
||||
//update position of mesh using this object
|
||||
@ -81,14 +81,6 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
}
|
||||
|
||||
obj.structureChanges.onEach { (nameToken, _, child) ->
|
||||
// if (name.isEmpty()) {
|
||||
// logger.error { "Children change with empty name on $group" }
|
||||
// return@onChildrenChange
|
||||
// }
|
||||
|
||||
// val parentName = name.cutLast()
|
||||
// val childName = name.last()!!
|
||||
|
||||
//removing old object
|
||||
findChild(nameToken.asName())?.let { oldChild ->
|
||||
oldChild.parent?.remove(oldChild)
|
||||
@ -153,7 +145,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
}
|
||||
|
||||
public companion object : PluginFactory<ThreePlugin> {
|
||||
override val tag: PluginTag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP)
|
||||
override val tag: PluginTag = PluginTag("vision.threejs", PluginTag.DATAFORGE_GROUP)
|
||||
override val type: KClass<ThreePlugin> = ThreePlugin::class
|
||||
override fun invoke(meta: Meta, context: Context): ThreePlugin = ThreePlugin()
|
||||
}
|
||||
|
@ -39,7 +39,9 @@ public fun HtmlVisionFragment.makeFile(
|
||||
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
|
||||
show: Boolean = true,
|
||||
) {
|
||||
val actualPath = path ?: Files.createTempFile("tempPlot", ".html")
|
||||
val scriptHeader = Context.scriptHeader("/js/visionforge-three.js", actualPath, resourceLocation)
|
||||
val actualPath = path?.let {
|
||||
Path.of(System.getProperty("user.home")).resolve(path)
|
||||
} ?: Files.createTempFile("tempPlot", ".html")
|
||||
val scriptHeader = Context.scriptHeader("js/visionforge-three.js", actualPath, resourceLocation)
|
||||
makeFile(visionManager, path = path, show = show, title = title, headers = arrayOf(scriptHeader))
|
||||
}
|
Loading…
Reference in New Issue
Block a user