forked from kscience/visionforge
Readme update
This commit is contained in:
parent
8c85804eb8
commit
ea276d35e9
@ -1,4 +1,7 @@
|
||||
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||
[![DOI](https://zenodo.org/badge/174502624.svg)](https://zenodo.org/badge/latestdoi/174502624)
|
||||
|
||||
![Gradle build](https://github.com/mipt-npm/visionforge/workflows/Gradle%20build/badge.svg)
|
||||
|
||||
# DataForge Visualization Platform
|
||||
|
||||
@ -142,3 +145,9 @@ Visualization example for geometry defined as GDML file.
|
||||
##### Example view:
|
||||
|
||||
![](doc/resources/gdml-demo.png)
|
||||
|
||||
|
||||
## Thanks and references
|
||||
The original three.js bindings were made by [Lars Ivar Hatledal](https://github.com/markaren), but the project is discontinued right now.
|
||||
|
||||
All other libraries are explicitly shown as dependencies. We would like to express specific thanks to JetBrains Kotlin-JS team for consulting us during the work.
|
@ -1,63 +0,0 @@
|
||||
import org.jetbrains.kotlin.gradle.frontend.KotlinFrontendExtension
|
||||
import org.jetbrains.kotlin.gradle.frontend.npm.NpmExtension
|
||||
import org.jetbrains.kotlin.gradle.frontend.webpack.WebPackExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||
|
||||
plugins {
|
||||
id("kotlin2js")
|
||||
id("kotlin-dce-js")
|
||||
id("org.jetbrains.kotlin.frontend")
|
||||
}
|
||||
|
||||
val kotlinVersion: String by rootProject.extra
|
||||
|
||||
dependencies {
|
||||
implementation(project(":visionforge-spatial-js"))
|
||||
testCompile(kotlin("test-js"))
|
||||
}
|
||||
|
||||
configure<KotlinFrontendExtension> {
|
||||
downloadNodeJsVersion = "latest"
|
||||
|
||||
configure<NpmExtension> {
|
||||
dependency("three","0.106.2")
|
||||
dependency("@hi-level/three-csg")
|
||||
dependency("style-loader")
|
||||
dependency("element-resize-event")
|
||||
devDependency("karma")
|
||||
}
|
||||
|
||||
sourceMaps = true
|
||||
|
||||
bundle<WebPackExtension>("webpack") {
|
||||
this as WebPackExtension
|
||||
bundleName = "main"
|
||||
contentPath = file("src/main/web")
|
||||
sourceMapEnabled = true
|
||||
//mode = "production"
|
||||
mode = "development"
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
"compileKotlin2Js"(Kotlin2JsCompile::class) {
|
||||
kotlinOptions {
|
||||
metaInfo = true
|
||||
outputFile = "${project.buildDir.path}/js/${project.name}.js"
|
||||
sourceMap = true
|
||||
moduleKind = "commonjs"
|
||||
main = "call"
|
||||
kotlinOptions.sourceMapEmbedSources = "always"
|
||||
}
|
||||
}
|
||||
|
||||
"compileTestKotlin2Js"(Kotlin2JsCompile::class) {
|
||||
kotlinOptions {
|
||||
metaInfo = true
|
||||
outputFile = "${project.buildDir.path}/js/${project.name}-test.js"
|
||||
sourceMap = true
|
||||
moduleKind = "commonjs"
|
||||
kotlinOptions.sourceMapEmbedSources = "always"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
external val module: Module
|
||||
|
||||
external interface Module {
|
||||
val hot: Hot?
|
||||
}
|
||||
|
||||
external interface Hot {
|
||||
val data: dynamic
|
||||
|
||||
fun accept()
|
||||
fun accept(dependency: String, callback: () -> Unit)
|
||||
fun accept(dependencies: Array<String>, callback: (updated: Array<String>) -> Unit)
|
||||
|
||||
fun dispose(callback: (data: dynamic) -> Unit)
|
||||
}
|
||||
|
||||
external fun require(name: String): dynamic
|
@ -1,6 +0,0 @@
|
||||
@file:JsModule("JSRootUtils")
|
||||
@file:JsNonModule
|
||||
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
external fun parse(obj: String): dynamic
|
@ -1,78 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
import org.w3c.dom.events.Event
|
||||
import org.w3c.files.FileList
|
||||
import org.w3c.files.FileReader
|
||||
import org.w3c.files.get
|
||||
import kotlin.browser.document
|
||||
import kotlin.dom.clear
|
||||
|
||||
|
||||
class JSRootDemoApp : ApplicationBase() {
|
||||
|
||||
override val stateKeys: List<String> = emptyList()
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
|
||||
//TODO remove after DI fix
|
||||
// Global.plugins.load(ThreePlugin())
|
||||
// Global.plugins.load(JSRootPlugin())
|
||||
|
||||
Global.plugins.load(JSRootPlugin)
|
||||
|
||||
|
||||
|
||||
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
|
||||
addEventListener("dragover", { handleDragOver(it) }, false)
|
||||
addEventListener("drop", { loadData(it) }, false)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse drag according to https://www.html5rocks.com/en/tutorials/file/dndfiles/
|
||||
*/
|
||||
private fun handleDragOver(event: Event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
event.asDynamic().dataTransfer.dropEffect = "copy"
|
||||
}
|
||||
|
||||
/**
|
||||
* Load data from text file
|
||||
*/
|
||||
private fun loadData(event: Event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
|
||||
|
||||
val file = (event.asDynamic().dataTransfer.files as FileList)[0]
|
||||
?: throw RuntimeException("Failed to load file");
|
||||
FileReader().apply {
|
||||
onload = {
|
||||
val string = result as String
|
||||
val renderer = Global.plugins.fetch(ThreePlugin).output()
|
||||
val canvas = document.getElementById("canvas")!!
|
||||
canvas.clear()
|
||||
renderer.attach(canvas)
|
||||
println("started")
|
||||
|
||||
renderer.render {
|
||||
val json = parse(string)
|
||||
JSRootObject(this, EmptyMeta, json).also { add(it) }
|
||||
}
|
||||
}
|
||||
readAsText(file)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() = emptyMap<String, Any>()//mapOf("lines" put presenter.dispose())
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
@file:JsModule("JSRootGeoBase")
|
||||
@file:JsNonModule
|
||||
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
|
||||
external fun createGeometry(shape: dynamic, limit: Int): BufferGeometry
|
||||
|
||||
external fun createCubeBuffer(shape: dynamic, limit: Int): BufferGeometry
|
||||
|
||||
external fun createTubeBuffer(shape: dynamic, limit: Int): BufferGeometry
|
||||
|
||||
external fun createXtruBuffer(shape: dynamic, limit: Int): BufferGeometry
|
||||
|
||||
external fun build(obj: dynamic, opt: dynamic): Object3D
|
@ -1,82 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.toDynamic
|
||||
import hep.dataforge.vis.common.*
|
||||
import hep.dataforge.vis.spatial.three.MeshThreeFactory
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
|
||||
class JSRootGeometry(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) {
|
||||
|
||||
var shape by node()
|
||||
|
||||
var facesLimit by int(0)
|
||||
|
||||
fun box(xSize: Number, ySize: Number, zSize: Number) = buildMeta {
|
||||
"_typename" put "TGeoBBox"
|
||||
"fDX" put xSize
|
||||
"fDY" put ySize
|
||||
"fDZ" put zSize
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a GDML union
|
||||
*/
|
||||
operator fun Meta.plus(other: Meta) = buildMeta {
|
||||
"fNode.fLeft" put this
|
||||
"fNode.fRight" put other
|
||||
"fNode._typename" put "TGeoUnion"
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a GDML subtraction
|
||||
*/
|
||||
operator fun Meta.minus(other: Meta) = buildMeta {
|
||||
"fNode.fLeft" put this
|
||||
"fNode.fRight" put other
|
||||
"fNode._typename" put "TGeoSubtraction"
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect two GDML geometries
|
||||
*/
|
||||
infix fun Meta.intersect(other: Meta) = buildMeta {
|
||||
"fNode.fLeft" put this
|
||||
"fNode.fRight" put other
|
||||
"fNode._typename" put "TGeoIntersection"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE = "geometry.spatial.jsRoot.geometry"
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup.jsRootGeometry(meta: Meta = EmptyMeta, action: JSRootGeometry.() -> Unit = {}) =
|
||||
JSRootGeometry(this, meta).apply(action).also { add(it) }
|
||||
|
||||
//fun Meta.toDynamic(): dynamic {
|
||||
// fun MetaItem<*>.toDynamic(): dynamic = when (this) {
|
||||
// is MetaItem.ValueItem -> this.value.value.asDynamic()
|
||||
// is MetaItem.NodeItem -> this.node.toDynamic()
|
||||
// }
|
||||
//
|
||||
// val res = js("{}")
|
||||
// this.items.entries.groupBy { it.key.body }.forEach { (key, value) ->
|
||||
// val list = value.map { it.value }
|
||||
// res[key] = when (list.size) {
|
||||
// 1 -> list.first().toDynamic()
|
||||
// else -> list.map { it.toDynamic() }
|
||||
// }
|
||||
// }
|
||||
// return res
|
||||
//}
|
||||
|
||||
|
||||
object ThreeJSRootGeometryFactory : MeshThreeFactory<JSRootGeometry>(JSRootGeometry::class) {
|
||||
override fun buildGeometry(obj: JSRootGeometry): BufferGeometry {
|
||||
val shapeMeta = obj.shape?.toDynamic() ?: error("The shape not defined")
|
||||
return createGeometry(shapeMeta, obj.facesLimit)
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.toDynamic
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.DisplayLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.node
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory
|
||||
import info.laht.threekt.core.Object3D
|
||||
|
||||
class JSRootObject(parent: VisualObject?, meta: Meta, val data: dynamic) : DisplayLeaf(parent, meta) {
|
||||
|
||||
var options by node()
|
||||
|
||||
companion object {
|
||||
const val TYPE = "geometry.spatial.jsRoot.object"
|
||||
}
|
||||
}
|
||||
|
||||
object ThreeJSRootObjectFactory : ThreeFactory<JSRootObject> {
|
||||
|
||||
override val type = JSRootObject::class
|
||||
|
||||
override fun invoke(obj: JSRootObject): Object3D {
|
||||
return build(obj.data, obj.options?.toDynamic())
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup.jsRootObject(str: String) {
|
||||
val json = JSON.parse<Any>(str)
|
||||
JSRootObject(this, EmptyMeta, json).also { add(it) }
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import hep.dataforge.context.AbstractPlugin
|
||||
import hep.dataforge.context.PluginFactory
|
||||
import hep.dataforge.context.PluginTag
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
|
||||
class JSRootPlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
override fun dependsOn() = listOf(ThreePlugin)
|
||||
|
||||
override fun provideTop(target: String): Map<Name, Any> {
|
||||
return when(target){
|
||||
ThreeFactory.TYPE -> mapOf(
|
||||
"jsRoot.geometry".toName() to ThreeJSRootGeometryFactory,
|
||||
"jsRoot.object".toName() to ThreeJSRootObjectFactory
|
||||
)
|
||||
else -> emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
companion object: PluginFactory<JSRootPlugin> {
|
||||
override val tag = PluginTag("vis.jsroot", "hep.dataforge")
|
||||
override val type = JSRootPlugin::class
|
||||
override fun invoke(meta: Meta) = JSRootPlugin()
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import kotlin.browser.document
|
||||
import kotlin.dom.hasClass
|
||||
|
||||
|
||||
abstract class ApplicationBase {
|
||||
abstract val stateKeys: List<String>
|
||||
|
||||
abstract fun start(state: Map<String, Any>)
|
||||
abstract fun dispose(): Map<String, Any>
|
||||
}
|
||||
|
||||
|
||||
fun main() {
|
||||
var application: ApplicationBase? = null
|
||||
|
||||
val state: dynamic = module.hot?.let { hot ->
|
||||
hot.accept()
|
||||
|
||||
hot.dispose { data ->
|
||||
data.appState = application?.dispose()
|
||||
application = null
|
||||
}
|
||||
|
||||
hot.data
|
||||
}
|
||||
|
||||
if (document.body != null) {
|
||||
application = start(state)
|
||||
} else {
|
||||
application = null
|
||||
document.addEventListener("DOMContentLoaded", { application = start(state) })
|
||||
}
|
||||
}
|
||||
|
||||
fun start(state: dynamic): ApplicationBase? {
|
||||
return if (document.body?.hasClass("application") == true) {
|
||||
val application = JSRootDemoApp()
|
||||
|
||||
@Suppress("UnsafeCastFromDynamic")
|
||||
application.start(state?.appState ?: emptyMap<String, Any>())
|
||||
|
||||
application
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
3577
dataforge-vis-jsroot/src/main/resources/JSRootGeoBase.js
vendored
3577
dataforge-vis-jsroot/src/main/resources/JSRootGeoBase.js
vendored
File diff suppressed because it is too large
Load Diff
@ -1,765 +0,0 @@
|
||||
|
||||
/** Generate mask for given bit
|
||||
*
|
||||
* @param {number} n bit number
|
||||
* @returns {Number} produced make
|
||||
* @private */
|
||||
export function BIT(n) {
|
||||
return 1 << (n);
|
||||
}
|
||||
|
||||
/** Wrapper for console.log, let redirect output to specified div element
|
||||
* @private */
|
||||
function console(value, divid) {
|
||||
if ((typeof divid == 'string') && document.getElementById(divid))
|
||||
document.getElementById(divid).innerHTML = value;
|
||||
else if ((typeof console != 'undefined') && (typeof console.log == 'function'))
|
||||
console.log(value);
|
||||
}
|
||||
|
||||
|
||||
/** @summary Wrapper for alert, throws Error in Node.js
|
||||
* @private */
|
||||
export function alert(msg) {
|
||||
if (this.nodeis) throw new Error(msg);
|
||||
if (typeof alert === 'function') alert(msg);
|
||||
else console('ALERT: ' + msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Seed simple random generator
|
||||
*
|
||||
* @private
|
||||
* @param {number} i seed value
|
||||
*/
|
||||
export function seed(i) {
|
||||
i = Math.abs(i);
|
||||
if (i > 1e8) i = Math.abs(1e8 * Math.sin(i)); else if (i < 1) i *= 1e8;
|
||||
this.m_w = Math.round(i);
|
||||
this.m_z = 987654321;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Simple random generator
|
||||
*
|
||||
* @desc Works like Math.random(), but with configurable seed - see {@link JSROOT.seed}
|
||||
* @private
|
||||
* @returns {number} random value between 0 (inclusive) and 1.0 (exclusive)
|
||||
*/
|
||||
export function random() {
|
||||
if (this.m_z === undefined) return Math.random();
|
||||
this.m_z = (36969 * (this.m_z & 65535) + (this.m_z >> 16)) & 0xffffffff;
|
||||
this.m_w = (18000 * (this.m_w & 65535) + (this.m_w >> 16)) & 0xffffffff;
|
||||
var result = ((this.m_z << 16) + this.m_w) & 0xffffffff;
|
||||
result /= 4294967296;
|
||||
return result + 0.5;
|
||||
}
|
||||
|
||||
|
||||
/** @summary Should be used to reintroduce objects references, produced by TBufferJSON.
|
||||
*
|
||||
* @desc Replace all references inside object, object should not be null
|
||||
* Idea of the code taken from JSON-R code, found on
|
||||
* https://github.com/graniteds/jsonr
|
||||
* Only unref part was used, arrays are not accounted as objects
|
||||
* @param {object} obj object where references will be replaced
|
||||
* @returns {object} same object with replaced references
|
||||
* @private */
|
||||
function JSONR_unref(obj) {
|
||||
|
||||
let map = [], newfmt = undefined;
|
||||
|
||||
function unref_value(value) {
|
||||
if ((value === null) || (value === undefined)) return;
|
||||
|
||||
/*
|
||||
if object is a reference string in "old format"
|
||||
Old format seems to be single string with "$ref:" prefix. New format is an object
|
||||
*/
|
||||
if (typeof value === 'string') {
|
||||
if (newfmt || (value.length < 6) || (value.indexOf("$ref:") !== 0)) return; //switch to "new format" if needed
|
||||
let ref = parseInt(value.substr(5)); // get ref number
|
||||
if (isNaN(ref) || (ref < 0) || (ref >= map.length)) return; //skip if not a ref
|
||||
newfmt = false;
|
||||
return map[ref]; //return an object from cache
|
||||
}
|
||||
|
||||
if (typeof value !== 'object') return;
|
||||
|
||||
let i, k, res, proto = Object.prototype.toString.apply(value);
|
||||
|
||||
// scan array - it can contain other objects
|
||||
if ((proto.indexOf('[object') === 0) && (proto.indexOf('Array]') > 0)) {
|
||||
for (i = 0; i < value.length; ++i) {
|
||||
res = unref_value(value[i]);
|
||||
if (res !== undefined) value[i] = res;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ks = Object.keys(value), len = ks.length;
|
||||
|
||||
if ((newfmt !== false) && (len === 1) && (ks[0] === '$ref')) {
|
||||
let ref = parseInt(value['$ref']);
|
||||
if (isNaN(ref) || (ref < 0) || (ref >= map.length)) return;
|
||||
newfmt = true;
|
||||
return map[ref];
|
||||
}
|
||||
|
||||
if ((newfmt !== false) && (len > 1) && (ks[0] === '$arr') && (ks[1] === 'len')) {
|
||||
// this is ROOT-coded array
|
||||
var arr = null, dflt = (value.$arr === "Bool") ? false : 0;
|
||||
switch (value.$arr) {
|
||||
case "Int8" :
|
||||
arr = new Int8Array(value.len);
|
||||
break;
|
||||
case "Uint8" :
|
||||
arr = new Uint8Array(value.len);
|
||||
break;
|
||||
case "Int16" :
|
||||
arr = new Int16Array(value.len);
|
||||
break;
|
||||
case "Uint16" :
|
||||
arr = new Uint16Array(value.len);
|
||||
break;
|
||||
case "Int32" :
|
||||
arr = new Int32Array(value.len);
|
||||
break;
|
||||
case "Uint32" :
|
||||
arr = new Uint32Array(value.len);
|
||||
break;
|
||||
case "Float32" :
|
||||
arr = new Float32Array(value.len);
|
||||
break;
|
||||
case "Int64" :
|
||||
case "Uint64" :
|
||||
case "Float64" :
|
||||
arr = new Float64Array(value.len);
|
||||
break;
|
||||
default :
|
||||
arr = new Array(value.len);
|
||||
break;
|
||||
}
|
||||
for (let k = 0; k < value.len; ++k) arr[k] = dflt;
|
||||
|
||||
var nkey = 2, p = 0;
|
||||
while (nkey < len) {
|
||||
if (ks[nkey][0] === "p") p = value[ks[nkey++]]; // position
|
||||
if (ks[nkey][0] !== 'v') throw new Error('Unexpected member ' + ks[nkey] + ' in array decoding');
|
||||
let v = value[ks[nkey++]]; // value
|
||||
if (typeof v === 'object') {
|
||||
for (let k = 0; k < v.length; ++k) arr[p++] = v[k];
|
||||
} else {
|
||||
arr[p++] = v;
|
||||
if ((nkey < len) && (ks[nkey][0] === 'n')) {
|
||||
let cnt = value[ks[nkey++]]; // counter
|
||||
while (--cnt) arr[p++] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
if ((newfmt !== false) && (len === 3) && (ks[0] === '$pair') && (ks[1] === 'first') && (ks[2] === 'second')) {
|
||||
newfmt = true;
|
||||
let f1 = unref_value(value.first),
|
||||
s1 = unref_value(value.second);
|
||||
if (f1 !== undefined) value.first = f1;
|
||||
if (s1 !== undefined) value.second = s1;
|
||||
value._typename = value['$pair'];
|
||||
delete value['$pair'];
|
||||
return; // pair object is not counted in the objects map
|
||||
}
|
||||
|
||||
// debug code, can be commented out later
|
||||
if (map.indexOf(value) >= 0) {
|
||||
console('should never happen - object already in the map');
|
||||
return;
|
||||
}
|
||||
|
||||
// add object to object map
|
||||
map.push(value);
|
||||
|
||||
// add methods to all objects, where _typename is specified
|
||||
//if ('_typename' in value) JSROOT.addMethods(value);
|
||||
|
||||
for (let k = 0; k < len; ++k) {
|
||||
i = ks[k];
|
||||
res = unref_value(value[i]);
|
||||
if (res !== undefined) value[i] = res;
|
||||
}
|
||||
}
|
||||
|
||||
unref_value(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/** @summary Just copies (not clone) all fields from source to the target object
|
||||
* @desc This is simple replacement of jQuery.extend method
|
||||
* @private */
|
||||
function extend(tgt, src) {
|
||||
if ((src === null) || (typeof src !== 'object')) return tgt;
|
||||
if ((tgt === null) || (typeof tgt !== 'object')) tgt = {};
|
||||
|
||||
for (var k in src)
|
||||
tgt[k] = src[k];
|
||||
|
||||
return tgt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @summary Parse JSON code produced with TBufferJSON.
|
||||
*
|
||||
* @param {string} json string to parse
|
||||
* @return {object|null} returns parsed object
|
||||
*/
|
||||
export function parse(json) {
|
||||
if (!json) return null;
|
||||
let obj = JSON.parse(json);
|
||||
if (obj) obj = JSONR_unref(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @summary Parse multi.json request results
|
||||
* @desc Method should be used to parse JSON code, produced by multi.json request of THttpServer
|
||||
*
|
||||
* @param {string} json string to parse
|
||||
* @return {Array|null} returns array of parsed elements
|
||||
*/
|
||||
export function parse_multi(json) {
|
||||
if (!json) return null;
|
||||
let arr = JSON.parse(json);
|
||||
if (arr && arr.length)
|
||||
for (let i = 0; i < arr.length; ++i)
|
||||
arr[i] = JSONR_unref(arr[i]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Method converts JavaScript object into ROOT-like JSON
|
||||
*
|
||||
* @desc Produced JSON can be used in JSROOT.parse() again
|
||||
* When performed properly, JSON can be used in TBufferJSON to read data back with C++
|
||||
*/
|
||||
export function toJSON(obj) {
|
||||
if (!obj || typeof obj !== 'object') return "";
|
||||
|
||||
var map = []; // map of stored objects
|
||||
|
||||
function copy_value(value) {
|
||||
if (typeof value === "function") return undefined;
|
||||
|
||||
if ((value === undefined) || (value === null) || (typeof value !== 'object')) return value;
|
||||
|
||||
var proto = Object.prototype.toString.apply(value);
|
||||
|
||||
// typed array need to be converted into normal array, otherwise looks strange
|
||||
if ((proto.indexOf('[object ') === 0) && (proto.indexOf('Array]') === proto.length - 6)) {
|
||||
var arr = new Array(value.length);
|
||||
for (var i = 0; i < value.length; ++i)
|
||||
arr[i] = copy_value(value[i]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
// this is how reference is code
|
||||
var refid = map.indexOf(value);
|
||||
if (refid >= 0) return {$ref: refid};
|
||||
|
||||
var ks = Object.keys(value), len = ks.length, tgt = {};
|
||||
|
||||
if ((len === 3) && (ks[0] === '$pair') && (ks[1] === 'first') && (ks[2] === 'second')) {
|
||||
// special handling of pair objects which does not included into objects map
|
||||
tgt.$pair = value.$pair;
|
||||
tgt.first = copy_value(value.first);
|
||||
tgt.second = copy_value(value.second);
|
||||
return tgt;
|
||||
}
|
||||
|
||||
map.push(value);
|
||||
|
||||
for (var k = 0; k < len; ++k) {
|
||||
var name = ks[k];
|
||||
tgt[name] = copy_value(value[name]);
|
||||
}
|
||||
|
||||
return tgt;
|
||||
}
|
||||
|
||||
var tgt = copy_value(obj);
|
||||
|
||||
return JSON.stringify(tgt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @summary Parse string value as array.
|
||||
*
|
||||
* @desc It could be just simple string: "value" or
|
||||
* array with or without string quotes: [element], ['elem1',elem2]
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function ParseAsArray(val) {
|
||||
|
||||
var res = [];
|
||||
|
||||
if (typeof val != 'string') return res;
|
||||
|
||||
val = val.trim();
|
||||
if (val === "") return res;
|
||||
|
||||
// return as array with single element
|
||||
if ((val.length < 2) || (val[0] !== '[') || (val[val.length - 1] !== ']')) {
|
||||
res.push(val);
|
||||
return res;
|
||||
}
|
||||
|
||||
// try to split ourself, checking quotes and brackets
|
||||
var nbr = 0, nquotes = 0, ndouble = 0, last = 1;
|
||||
|
||||
for (var indx = 1; indx < val.length; ++indx) {
|
||||
if (nquotes > 0) {
|
||||
if (val[indx] === "'") nquotes--;
|
||||
continue;
|
||||
}
|
||||
if (ndouble > 0) {
|
||||
if (val[indx] === '"') ndouble--;
|
||||
continue;
|
||||
}
|
||||
switch (val[indx]) {
|
||||
case "'":
|
||||
nquotes++;
|
||||
break;
|
||||
case '"':
|
||||
ndouble++;
|
||||
break;
|
||||
case "[":
|
||||
nbr++;
|
||||
break;
|
||||
case "]":
|
||||
if (indx < val.length - 1) {
|
||||
nbr--;
|
||||
break;
|
||||
}
|
||||
case ",":
|
||||
if (nbr === 0) {
|
||||
var sub = val.substring(last, indx).trim();
|
||||
if ((sub.length > 1) && (sub[0] === sub[sub.length - 1]) && ((sub[0] === '"') || (sub[0] === "'")))
|
||||
sub = sub.substr(1, sub.length - 2);
|
||||
res.push(sub);
|
||||
last = indx + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res.length === 0)
|
||||
res.push(val.substr(1, val.length - 2).trim());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Find function with given name.
|
||||
*
|
||||
* @desc Function name may include several namespaces like 'JSROOT.Painter.drawFrame'
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function findFunction(name) {
|
||||
if (typeof name === 'function') return name;
|
||||
if (typeof name !== 'string') return null;
|
||||
var names = name.split('.'), elem = null;
|
||||
if (typeof window === 'object') elem = window;
|
||||
if (names[0] === 'JSROOT') {
|
||||
elem = this;
|
||||
names.shift();
|
||||
}
|
||||
|
||||
for (var n = 0; elem && (n < names.length); ++n)
|
||||
elem = elem[names[n]];
|
||||
|
||||
return (typeof elem == 'function') ? elem : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Generic method to invoke callback function.
|
||||
*
|
||||
* @param {object|function} func either normal function or container like
|
||||
* { obj: object_pointer, func: name of method to call }
|
||||
* @param arg1 first optional argument of callback
|
||||
* @param arg2 second optional argument of callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function callBack(func, arg1, arg2) {
|
||||
|
||||
if (typeof func == 'string') func = findFunction(func);
|
||||
|
||||
if (!func) return;
|
||||
|
||||
if (typeof func == 'function') return func(arg1, arg2);
|
||||
|
||||
if (typeof func != 'object') return;
|
||||
|
||||
if (('obj' in func) && ('func' in func) &&
|
||||
(typeof func.obj == 'object') && (typeof func.func == 'string') &&
|
||||
(typeof func.obj[func.func] == 'function')) {
|
||||
return func.obj[func.func](arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
let methodsCache = {}; // variable used to keep methods for known classes
|
||||
|
||||
|
||||
/** @summary Returns methods for given typename
|
||||
* @private
|
||||
*/
|
||||
function getMethods(typename, obj) {
|
||||
|
||||
var m = methodsCache[typename],
|
||||
has_methods = (m !== undefined);
|
||||
|
||||
if (!has_methods) m = {};
|
||||
|
||||
// Due to binary I/O such TObject methods may not be set for derived classes
|
||||
// Therefore when methods requested for given object, check also that basic methods are there
|
||||
if ((typename === "TObject") || (typename === "TNamed") || (obj && (obj.fBits !== undefined)))
|
||||
if (m.TestBit === undefined) {
|
||||
m.TestBit = function (f) {
|
||||
return (this.fBits & f) !== 0;
|
||||
};
|
||||
m.InvertBit = function (f) {
|
||||
this.fBits = this.fBits ^ (f & 0xffffff);
|
||||
};
|
||||
}
|
||||
|
||||
if (has_methods) return m;
|
||||
|
||||
if ((typename === 'TList') || (typename === 'THashList')) {
|
||||
m.Clear = function () {
|
||||
this.arr = [];
|
||||
this.opt = [];
|
||||
};
|
||||
m.Add = function (obj, opt) {
|
||||
this.arr.push(obj);
|
||||
this.opt.push((opt && typeof opt == 'string') ? opt : "");
|
||||
};
|
||||
m.AddFirst = function (obj, opt) {
|
||||
this.arr.unshift(obj);
|
||||
this.opt.unshift((opt && typeof opt == 'string') ? opt : "");
|
||||
};
|
||||
m.RemoveAt = function (indx) {
|
||||
this.arr.splice(indx, 1);
|
||||
this.opt.splice(indx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// if ((typename === "TPaveText") || (typename === "TPaveStats")) {
|
||||
// m.AddText = function (txt) {
|
||||
// // this.fLines.Add({ _typename: 'TLatex', fTitle: txt, fTextColor: 1 });
|
||||
// var line = JSROOT.Create("TLatex");
|
||||
// line.fTitle = txt;
|
||||
// this.fLines.Add(line);
|
||||
// };
|
||||
// m.Clear = function () {
|
||||
// this.fLines.Clear();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if ((typename.indexOf("TF1") === 0) || (typename === "TF2")) {
|
||||
// m.addFormula = function (obj) {
|
||||
// if (!obj) return;
|
||||
// if (this.formulas === undefined) this.formulas = [];
|
||||
// this.formulas.push(obj);
|
||||
// };
|
||||
//
|
||||
// m.evalPar = function (x, y) {
|
||||
// if (!('_func' in this) || (this._title !== this.fTitle)) {
|
||||
//
|
||||
// var _func = this.fTitle, isformula = false, pprefix = "[";
|
||||
// if (_func === "gaus") _func = "gaus(0)";
|
||||
// if (this.fFormula && typeof this.fFormula.fFormula == "string") {
|
||||
// if (this.fFormula.fFormula.indexOf("[](double*x,double*p)") === 0) {
|
||||
// isformula = true;
|
||||
// pprefix = "p[";
|
||||
// _func = this.fFormula.fFormula.substr(21);
|
||||
// } else {
|
||||
// _func = this.fFormula.fFormula;
|
||||
// pprefix = "[p";
|
||||
// }
|
||||
// if (this.fFormula.fClingParameters && this.fFormula.fParams) {
|
||||
// for (var i = 0; i < this.fFormula.fParams.length; ++i) {
|
||||
// var regex = new RegExp('(\\[' + this.fFormula.fParams[i].first + '\\])', 'g'),
|
||||
// parvalue = this.fFormula.fClingParameters[this.fFormula.fParams[i].second];
|
||||
// _func = _func.replace(regex, (parvalue < 0) ? "(" + parvalue + ")" : parvalue);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if ('formulas' in this)
|
||||
// for (var i = 0; i < this.formulas.length; ++i)
|
||||
// while (_func.indexOf(this.formulas[i].fName) >= 0)
|
||||
// _func = _func.replace(this.formulas[i].fName, this.formulas[i].fTitle);
|
||||
// _func = _func.replace(/\b(abs)\b/g, 'TMath::Abs')
|
||||
// .replace(/TMath::Exp\(/g, 'Math.exp(')
|
||||
// .replace(/TMath::Abs\(/g, 'Math.abs(');
|
||||
// if (typeof JSROOT.Math == 'object') {
|
||||
// this._math = JSROOT.Math;
|
||||
// _func = _func.replace(/TMath::Prob\(/g, 'this._math.Prob(')
|
||||
// .replace(/TMath::Gaus\(/g, 'this._math.Gaus(')
|
||||
// .replace(/TMath::BreitWigner\(/g, 'this._math.BreitWigner(')
|
||||
// .replace(/xygaus\(/g, 'this._math.gausxy(this, x, y, ')
|
||||
// .replace(/gaus\(/g, 'this._math.gaus(this, x, ')
|
||||
// .replace(/gausn\(/g, 'this._math.gausn(this, x, ')
|
||||
// .replace(/expo\(/g, 'this._math.expo(this, x, ')
|
||||
// .replace(/landau\(/g, 'this._math.landau(this, x, ')
|
||||
// .replace(/landaun\(/g, 'this._math.landaun(this, x, ')
|
||||
// .replace(/ROOT::Math::/g, 'this._math.');
|
||||
// }
|
||||
// for (var i = 0; i < this.fNpar; ++i) {
|
||||
// var parname = pprefix + i + "]";
|
||||
// while (_func.indexOf(parname) !== -1)
|
||||
// _func = _func.replace(parname, '(' + this.GetParValue(i) + ')');
|
||||
// }
|
||||
// _func = _func.replace(/\b(sin)\b/gi, 'Math.sin')
|
||||
// .replace(/\b(cos)\b/gi, 'Math.cos')
|
||||
// .replace(/\b(tan)\b/gi, 'Math.tan')
|
||||
// .replace(/\b(exp)\b/gi, 'Math.exp')
|
||||
// .replace(/\b(pow)\b/gi, 'Math.pow')
|
||||
// .replace(/pi/g, 'Math.PI');
|
||||
// for (var n = 2; n < 10; ++n)
|
||||
// _func = _func.replace('x^' + n, 'Math.pow(x,' + n + ')');
|
||||
//
|
||||
// if (isformula) {
|
||||
// _func = _func.replace(/x\[0\]/g, "x");
|
||||
// if (this._typename === "TF2") {
|
||||
// _func = _func.replace(/x\[1\]/g, "y");
|
||||
// this._func = new Function("x", "y", _func).bind(this);
|
||||
// } else {
|
||||
// this._func = new Function("x", _func).bind(this);
|
||||
// }
|
||||
// } else if (this._typename === "TF2")
|
||||
// this._func = new Function("x", "y", "return " + _func).bind(this);
|
||||
// else
|
||||
// this._func = new Function("x", "return " + _func).bind(this);
|
||||
//
|
||||
// this._title = this.fTitle;
|
||||
// }
|
||||
//
|
||||
// return this._func(x, y);
|
||||
// };
|
||||
// m.GetParName = function (n) {
|
||||
// if (this.fFormula && this.fFormula.fParams) return this.fFormula.fParams[n].first;
|
||||
// if (this.fNames && this.fNames[n]) return this.fNames[n];
|
||||
// return "p" + n;
|
||||
// };
|
||||
// m.GetParValue = function (n) {
|
||||
// if (this.fFormula && this.fFormula.fClingParameters) return this.fFormula.fClingParameters[n];
|
||||
// if (this.fParams) return this.fParams[n];
|
||||
// return undefined;
|
||||
// };
|
||||
// m.GetParError = function (n) {
|
||||
// return this.fParErrors ? this.fParErrors[n] : undefined;
|
||||
// };
|
||||
// m.GetNumPars = function () {
|
||||
// return this.fNpar;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (((typename.indexOf("TGraph") === 0) || (typename === "TCutG")) && (typename !== "TGraphPolargram") && (typename !== "TGraphTime")) {
|
||||
// check if point inside figure specified by the TGraph
|
||||
m.IsInside = function (xp, yp) {
|
||||
var i, j = this.fNpoints - 1, x = this.fX, y = this.fY, oddNodes = false;
|
||||
|
||||
for (i = 0; i < this.fNpoints; ++i) {
|
||||
if ((y[i] < yp && y[j] >= yp) || (y[j] < yp && y[i] >= yp)) {
|
||||
if (x[i] + (yp - y[i]) / (y[j] - y[i]) * (x[j] - x[i]) < xp) {
|
||||
oddNodes = !oddNodes;
|
||||
}
|
||||
}
|
||||
j = i;
|
||||
}
|
||||
|
||||
return oddNodes;
|
||||
};
|
||||
}
|
||||
|
||||
if (typename.indexOf("TH1") === 0 ||
|
||||
typename.indexOf("TH2") === 0 ||
|
||||
typename.indexOf("TH3") === 0) {
|
||||
m.getBinError = function (bin) {
|
||||
// -*-*-*-*-*Return value of error associated to bin number bin*-*-*-*-*
|
||||
// if the sum of squares of weights has been defined (via Sumw2),
|
||||
// this function returns the sqrt(sum of w2).
|
||||
// otherwise it returns the sqrt(contents) for this bin.
|
||||
if (bin >= this.fNcells) bin = this.fNcells - 1;
|
||||
if (bin < 0) bin = 0;
|
||||
if (bin < this.fSumw2.length)
|
||||
return Math.sqrt(this.fSumw2[bin]);
|
||||
return Math.sqrt(Math.abs(this.fArray[bin]));
|
||||
};
|
||||
m.setBinContent = function (bin, content) {
|
||||
// Set bin content - only trivial case, without expansion
|
||||
this.fEntries++;
|
||||
this.fTsumw = 0;
|
||||
if ((bin >= 0) && (bin < this.fArray.length))
|
||||
this.fArray[bin] = content;
|
||||
};
|
||||
}
|
||||
|
||||
if (typename.indexOf("TH1") === 0) {
|
||||
m.getBin = function (x) {
|
||||
return x;
|
||||
};
|
||||
m.getBinContent = function (bin) {
|
||||
return this.fArray[bin];
|
||||
};
|
||||
m.Fill = function (x, weight) {
|
||||
var axis = this.fXaxis,
|
||||
bin = 1 + Math.floor((x - axis.fXmin) / (axis.fXmax - axis.fXmin) * axis.fNbins);
|
||||
if (bin < 0) bin = 0; else if (bin > axis.fNbins + 1) bin = axis.fNbins + 1;
|
||||
this.fArray[bin] += ((weight === undefined) ? 1 : weight);
|
||||
}
|
||||
}
|
||||
|
||||
if (typename.indexOf("TH2") === 0) {
|
||||
m.getBin = function (x, y) {
|
||||
return (x + (this.fXaxis.fNbins + 2) * y);
|
||||
};
|
||||
m.getBinContent = function (x, y) {
|
||||
return this.fArray[this.getBin(x, y)];
|
||||
};
|
||||
m.Fill = function (x, y, weight) {
|
||||
var axis1 = this.fXaxis, axis2 = this.fYaxis,
|
||||
bin1 = 1 + Math.floor((x - axis1.fXmin) / (axis1.fXmax - axis1.fXmin) * axis1.fNbins),
|
||||
bin2 = 1 + Math.floor((y - axis2.fXmin) / (axis2.fXmax - axis2.fXmin) * axis2.fNbins);
|
||||
if (bin1 < 0) bin1 = 0; else if (bin1 > axis1.fNbins + 1) bin1 = axis1.fNbins + 1;
|
||||
if (bin2 < 0) bin2 = 0; else if (bin2 > axis2.fNbins + 1) bin2 = axis2.fNbins + 1;
|
||||
this.fArray[bin1 + (axis1.fNbins + 2) * bin2] += ((weight === undefined) ? 1 : weight);
|
||||
}
|
||||
}
|
||||
|
||||
if (typename.indexOf("TH3") === 0) {
|
||||
m.getBin = function (x, y, z) {
|
||||
return (x + (this.fXaxis.fNbins + 2) * (y + (this.fYaxis.fNbins + 2) * z));
|
||||
};
|
||||
m.getBinContent = function (x, y, z) {
|
||||
return this.fArray[this.getBin(x, y, z)];
|
||||
};
|
||||
m.Fill = function (x, y, z, weight) {
|
||||
var axis1 = this.fXaxis, axis2 = this.fYaxis, axis3 = this.fZaxis,
|
||||
bin1 = 1 + Math.floor((x - axis1.fXmin) / (axis1.fXmax - axis1.fXmin) * axis1.fNbins),
|
||||
bin2 = 1 + Math.floor((y - axis2.fXmin) / (axis2.fXmax - axis2.fXmin) * axis2.fNbins),
|
||||
bin3 = 1 + Math.floor((z - axis3.fXmin) / (axis3.fXmax - axis3.fXmin) * axis3.fNbins);
|
||||
if (bin1 < 0) bin1 = 0; else if (bin1 > axis1.fNbins + 1) bin1 = axis1.fNbins + 1;
|
||||
if (bin2 < 0) bin2 = 0; else if (bin2 > axis2.fNbins + 1) bin2 = axis2.fNbins + 1;
|
||||
if (bin3 < 0) bin3 = 0; else if (bin3 > axis3.fNbins + 1) bin3 = axis3.fNbins + 1;
|
||||
this.fArray[bin1 + (axis1.fNbins + 2) * (bin2 + (axis2.fNbins + 2) * bin3)] += ((weight === undefined) ? 1 : weight);
|
||||
}
|
||||
}
|
||||
|
||||
if (typename.indexOf("TProfile") === 0) {
|
||||
if (typename.indexOf("TProfile2D") === 0) {
|
||||
m.getBin = function (x, y) {
|
||||
return (x + (this.fXaxis.fNbins + 2) * y);
|
||||
};
|
||||
m.getBinContent = function (x, y) {
|
||||
var bin = this.getBin(x, y);
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
if (this.fBinEntries[bin] < 1e-300) return 0;
|
||||
if (!this.fArray) return 0;
|
||||
return this.fArray[bin] / this.fBinEntries[bin];
|
||||
};
|
||||
m.getBinEntries = function (x, y) {
|
||||
var bin = this.getBin(x, y);
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
return this.fBinEntries[bin];
|
||||
}
|
||||
} else {
|
||||
m.getBin = function (x) {
|
||||
return x;
|
||||
};
|
||||
m.getBinContent = function (bin) {
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
if (this.fBinEntries[bin] < 1e-300) return 0;
|
||||
if (!this.fArray) return 0;
|
||||
return this.fArray[bin] / this.fBinEntries[bin];
|
||||
};
|
||||
}
|
||||
m.getBinEffectiveEntries = function (bin) {
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
var sumOfWeights = this.fBinEntries[bin];
|
||||
if (!this.fBinSumw2 || this.fBinSumw2.length !== this.fNcells) {
|
||||
// this can happen when reading an old file
|
||||
return sumOfWeights;
|
||||
}
|
||||
var sumOfWeightsSquare = this.fBinSumw2[bin];
|
||||
return (sumOfWeightsSquare > 0) ? sumOfWeights * sumOfWeights / sumOfWeightsSquare : 0;
|
||||
};
|
||||
m.getBinError = function (bin) {
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
var cont = this.fArray[bin], // sum of bin w *y
|
||||
sum = this.fBinEntries[bin], // sum of bin weights
|
||||
err2 = this.fSumw2[bin], // sum of bin w * y^2
|
||||
neff = this.getBinEffectiveEntries(bin); // (sum of w)^2 / (sum of w^2)
|
||||
if (sum < 1e-300) return 0; // for empty bins
|
||||
var EErrorType = {kERRORMEAN: 0, kERRORSPREAD: 1, kERRORSPREADI: 2, kERRORSPREADG: 3};
|
||||
// case the values y are gaussian distributed y +/- sigma and w = 1/sigma^2
|
||||
if (this.fErrorMode === EErrorType.kERRORSPREADG)
|
||||
return 1.0 / Math.sqrt(sum);
|
||||
// compute variance in y (eprim2) and standard deviation in y (eprim)
|
||||
var contsum = cont / sum, eprim = Math.sqrt(Math.abs(err2 / sum - contsum * contsum));
|
||||
if (this.fErrorMode === EErrorType.kERRORSPREADI) {
|
||||
if (eprim !== 0) return eprim / Math.sqrt(neff);
|
||||
// in case content y is an integer (so each my has an error +/- 1/sqrt(12)
|
||||
// when the std(y) is zero
|
||||
return 1.0 / Math.sqrt(12 * neff);
|
||||
}
|
||||
// if approximate compute the sums (of w, wy and wy2) using all the bins
|
||||
// when the variance in y is zero
|
||||
// case option "S" return standard deviation in y
|
||||
if (this.fErrorMode === EErrorType.kERRORSPREAD) return eprim;
|
||||
// default case : fErrorMode = kERRORMEAN
|
||||
// return standard error on the mean of y
|
||||
return (eprim / Math.sqrt(neff));
|
||||
};
|
||||
}
|
||||
|
||||
if (typename === "TAxis") {
|
||||
m.GetBinLowEdge = function (bin) {
|
||||
if (this.fNbins <= 0) return 0;
|
||||
if ((this.fXbins.length > 0) && (bin > 0) && (bin <= this.fNbins)) return this.fXbins[bin - 1];
|
||||
return this.fXmin + (bin - 1) * (this.fXmax - this.fXmin) / this.fNbins;
|
||||
};
|
||||
m.GetBinCenter = function (bin) {
|
||||
if (this.fNbins <= 0) return 0;
|
||||
if ((this.fXbins.length > 0) && (bin > 0) && (bin < this.fNbins)) return (this.fXbins[bin - 1] + this.fXbins[bin]) / 2;
|
||||
return this.fXmin + (bin - 0.5) * (this.fXmax - this.fXmin) / this.fNbins;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof getMoreMethods == "function")
|
||||
getMoreMethods(m, typename, obj);
|
||||
|
||||
methodsCache[typename] = m;
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/** @summary Adds specific methods to the object.
|
||||
*
|
||||
* JSROOT implements some basic methods for different ROOT classes.
|
||||
* @param {object} obj - object where methods are assigned
|
||||
* @param {string} typename - optional typename, if not specified, obj._typename will be used
|
||||
* @private
|
||||
*/
|
||||
function addMethods(obj, typename) {
|
||||
this.extend(obj, getMethods(typename || obj._typename, obj));
|
||||
}
|
869
dataforge-vis-jsroot/src/main/resources/ThreeCSG.js
vendored
869
dataforge-vis-jsroot/src/main/resources/ThreeCSG.js
vendored
@ -1,869 +0,0 @@
|
||||
import * as THREE from "three"
|
||||
|
||||
const EPSILON = 1e-5,
|
||||
COPLANAR = 0,
|
||||
FRONT = 1,
|
||||
BACK = 2,
|
||||
SPANNING = 3;
|
||||
|
||||
export function Geometry(geometry, transfer_matrix, nodeid, flippedMesh) {
|
||||
// Convert THREE.Geometry to ThreeBSP
|
||||
|
||||
if (geometry instanceof THREE.Geometry) {
|
||||
this.matrix = null; // new THREE.Matrix4; not create matrix when do not needed
|
||||
} else if (geometry instanceof THREE.Mesh) {
|
||||
// #todo: add hierarchy support
|
||||
geometry.updateMatrix();
|
||||
transfer_matrix = this.matrix = geometry.matrix.clone();
|
||||
geometry = geometry.geometry;
|
||||
} else if (geometry instanceof Node) {
|
||||
this.tree = geometry;
|
||||
this.matrix = null; // new THREE.Matrix4;
|
||||
return this;
|
||||
} else if (geometry instanceof THREE.BufferGeometry) {
|
||||
var pos_buf = geometry.getAttribute('position').array,
|
||||
norm_buf = geometry.getAttribute('normal').array,
|
||||
polygons = [], polygon, vert1, vert2, vert3;
|
||||
|
||||
for (var i = 0; i < pos_buf.length; i += 9) {
|
||||
polygon = new Polygon;
|
||||
|
||||
vert1 = new Vertex(pos_buf[i], pos_buf[i + 1], pos_buf[i + 2], norm_buf[i], norm_buf[i + 1], norm_buf[i + 2]);
|
||||
if (transfer_matrix) vert1.applyMatrix4(transfer_matrix);
|
||||
|
||||
vert2 = new Vertex(pos_buf[i + 3], pos_buf[i + 4], pos_buf[i + 5], norm_buf[i + 3], norm_buf[i + 4], norm_buf[i + 5]);
|
||||
if (transfer_matrix) vert2.applyMatrix4(transfer_matrix);
|
||||
|
||||
vert3 = new Vertex(pos_buf[i + 6], pos_buf[i + 7], pos_buf[i + 8], norm_buf[i + 6], norm_buf[i + 7], norm_buf[i + 8]);
|
||||
if (transfer_matrix) vert3.applyMatrix4(transfer_matrix);
|
||||
|
||||
if (flippedMesh) polygon.vertices.push(vert1, vert3, vert2);
|
||||
else polygon.vertices.push(vert1, vert2, vert3);
|
||||
|
||||
polygon.calculateProperties();
|
||||
polygons.push(polygon);
|
||||
}
|
||||
|
||||
this.tree = new Node(polygons, nodeid);
|
||||
if (nodeid !== undefined) this.maxid = this.tree.maxnodeid;
|
||||
return this;
|
||||
|
||||
} else if (geometry.polygons && (geometry.polygons[0] instanceof Polygon)) {
|
||||
var polygons = geometry.polygons;
|
||||
|
||||
for (var i = 0; i < polygons.length; ++i) {
|
||||
var polygon = polygons[i];
|
||||
if (transfer_matrix) {
|
||||
for (var n = 0; n < polygon.vertices.length; ++n)
|
||||
polygon.vertices[n].applyMatrix4(transfer_matrix);
|
||||
}
|
||||
|
||||
polygon.calculateProperties();
|
||||
}
|
||||
|
||||
this.tree = new Node(polygons, nodeid);
|
||||
if (nodeid !== undefined) this.maxid = this.tree.maxnodeid;
|
||||
return this;
|
||||
|
||||
} else {
|
||||
throw 'ThreeBSP: Given geometry is unsupported';
|
||||
}
|
||||
|
||||
var polygons = [],
|
||||
nfaces = geometry.faces.length,
|
||||
face, polygon, vertex;
|
||||
|
||||
for (var i = 0; i < nfaces; ++i) {
|
||||
face = geometry.faces[i];
|
||||
// faceVertexUvs = geometry.faceVertexUvs[0][i];
|
||||
polygon = new Polygon;
|
||||
|
||||
if (face instanceof THREE.Face3) {
|
||||
vertex = geometry.vertices[face.a];
|
||||
// uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;
|
||||
vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0].x, face.vertexNormals[0].y, face.vertexNormals[0].z /*face.normal , uvs */);
|
||||
if (transfer_matrix) vertex.applyMatrix4(transfer_matrix);
|
||||
polygon.vertices.push(vertex);
|
||||
|
||||
vertex = geometry.vertices[face.b];
|
||||
//uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;
|
||||
vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[1].x, face.vertexNormals[1].y, face.vertexNormals[1].z/*face.normal , uvs */);
|
||||
if (transfer_matrix) vertex.applyMatrix4(transfer_matrix);
|
||||
polygon.vertices.push(vertex);
|
||||
|
||||
vertex = geometry.vertices[face.c];
|
||||
// uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;
|
||||
vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[2].x, face.vertexNormals[2].y, face.vertexNormals[2].z /*face.normal, uvs */);
|
||||
if (transfer_matrix) vertex.applyMatrix4(transfer_matrix);
|
||||
polygon.vertices.push(vertex);
|
||||
} else {
|
||||
throw 'Invalid face type at index ' + i;
|
||||
}
|
||||
|
||||
polygon.calculateProperties();
|
||||
polygons.push(polygon);
|
||||
}
|
||||
|
||||
this.tree = new Node(polygons, nodeid);
|
||||
if (nodeid !== undefined) this.maxid = this.tree.maxnodeid;
|
||||
}
|
||||
|
||||
Geometry.prototype.subtract = function (other_tree) {
|
||||
var a = this.tree.clone(),
|
||||
b = other_tree.tree.clone();
|
||||
|
||||
a.invert();
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.build(b.allPolygons());
|
||||
a.invert();
|
||||
a = new Geometry(a);
|
||||
a.matrix = this.matrix;
|
||||
return a;
|
||||
};
|
||||
|
||||
Geometry.prototype.union = function (other_tree) {
|
||||
var a = this.tree.clone(),
|
||||
b = other_tree.tree.clone();
|
||||
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.build(b.allPolygons());
|
||||
a = new Geometry(a);
|
||||
a.matrix = this.matrix;
|
||||
return a;
|
||||
};
|
||||
|
||||
Geometry.prototype.intersect = function (other_tree) {
|
||||
var a = this.tree.clone(),
|
||||
b = other_tree.tree.clone();
|
||||
|
||||
a.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
a.build(b.allPolygons());
|
||||
a.invert();
|
||||
a = new Geometry(a);
|
||||
a.matrix = this.matrix;
|
||||
return a;
|
||||
};
|
||||
|
||||
Geometry.prototype.tryToCompress = function (polygons) {
|
||||
|
||||
if (this.maxid === undefined) return;
|
||||
|
||||
var arr = [], parts, foundpair,
|
||||
nreduce = 0, n, len = polygons.length,
|
||||
p, p1, p2, i1, i2;
|
||||
|
||||
// sort out polygons
|
||||
for (n = 0; n < len; ++n) {
|
||||
p = polygons[n];
|
||||
if (p.id === undefined) continue;
|
||||
if (arr[p.id] === undefined) arr[p.id] = [];
|
||||
|
||||
arr[p.id].push(p);
|
||||
}
|
||||
|
||||
for (n = 0; n < arr.length; ++n) {
|
||||
parts = arr[n];
|
||||
if (parts === undefined) continue;
|
||||
|
||||
len = parts.length;
|
||||
|
||||
foundpair = (len > 1);
|
||||
|
||||
while (foundpair) {
|
||||
foundpair = false;
|
||||
|
||||
for (i1 = 0; i1 < len - 1; ++i1) {
|
||||
p1 = parts[i1];
|
||||
if (!p1 || !p1.parent) continue;
|
||||
for (i2 = i1 + 1; i2 < len; ++i2) {
|
||||
p2 = parts[i2];
|
||||
if (p2 && (p1.parent === p2.parent) && (p1.nsign === p2.nsign)) {
|
||||
|
||||
if (p1.nsign !== p1.parent.nsign) p1.parent.flip();
|
||||
|
||||
nreduce++;
|
||||
parts[i1] = p1.parent;
|
||||
parts[i2] = null;
|
||||
if (p1.parent.vertices.length < 3) console.log('something wrong with parent');
|
||||
foundpair = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nreduce > 0) {
|
||||
polygons.splice(0, polygons.length);
|
||||
|
||||
for (n = 0; n < arr.length; ++n) {
|
||||
parts = arr[n];
|
||||
if (parts !== undefined)
|
||||
for (i1 = 0, len = parts.length; i1 < len; ++i1)
|
||||
if (parts[i1]) polygons.push(parts[i1]);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Geometry.prototype.direct_subtract = function (other_tree) {
|
||||
var a = this.tree,
|
||||
b = other_tree.tree;
|
||||
a.invert();
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.build(b.collectPolygons([]));
|
||||
a.invert();
|
||||
return this;
|
||||
};
|
||||
|
||||
Geometry.prototype.direct_union = function (other_tree) {
|
||||
var a = this.tree,
|
||||
b = other_tree.tree;
|
||||
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.build(b.collectPolygons([]));
|
||||
return this;
|
||||
};
|
||||
|
||||
Geometry.prototype.direct_intersect = function (other_tree) {
|
||||
var a = this.tree,
|
||||
b = other_tree.tree;
|
||||
|
||||
a.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
a.build(b.collectPolygons([]));
|
||||
a.invert();
|
||||
return this;
|
||||
};
|
||||
|
||||
export function CreateNormal(axis_name, pos, size) {
|
||||
// create geometry to make cut on specified axis
|
||||
|
||||
var vert1, vert2, vert3;
|
||||
|
||||
if (!size || (size < 10000)) size = 10000;
|
||||
|
||||
switch (axis_name) {
|
||||
case "x":
|
||||
vert1 = new Vertex(pos, -3 * size, size, 1, 0, 0),
|
||||
vert3 = new Vertex(pos, size, size, 1, 0, 0),
|
||||
vert2 = new Vertex(pos, size, -3 * size, 1, 0, 0);
|
||||
break;
|
||||
case "y":
|
||||
vert1 = new Vertex(-3 * size, pos, size, 0, 1, 0),
|
||||
vert2 = new Vertex(size, pos, size, 0, 1, 0),
|
||||
vert3 = new Vertex(size, pos, -3 * size, 0, 1, 0);
|
||||
break;
|
||||
case "z":
|
||||
vert1 = new Vertex(-3 * size, size, pos, 0, 0, 1),
|
||||
vert3 = new Vertex(size, size, pos, 0, 0, 1),
|
||||
vert2 = new Vertex(size, -3 * size, pos, 0, 0, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
var polygon = new Polygon([vert1, vert2, vert3]);
|
||||
polygon.calculateProperties();
|
||||
|
||||
var node = new Node([polygon]);
|
||||
|
||||
return new Geometry(node);
|
||||
}
|
||||
|
||||
Geometry.prototype.cut_from_plane = function (other_tree) {
|
||||
// just cut peaces from second geometry, which just simple plane
|
||||
|
||||
var a = this.tree,
|
||||
b = other_tree.tree;
|
||||
|
||||
a.invert();
|
||||
b.clipTo(a);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
Geometry.prototype.toGeometry = function () {
|
||||
var i, j,
|
||||
matrix = this.matrix ? new THREE.Matrix4().getInverse(this.matrix) : null,
|
||||
geometry = new THREE.Geometry(),
|
||||
polygons = this.tree.collectPolygons([]),
|
||||
polygon_count = polygons.length,
|
||||
polygon, polygon_vertice_count,
|
||||
vertice_dict = {},
|
||||
vertex_idx_a, vertex_idx_b, vertex_idx_c,
|
||||
vertex, face;
|
||||
|
||||
for (i = 0; i < polygon_count; ++i) {
|
||||
polygon = polygons[i];
|
||||
polygon_vertice_count = polygon.vertices.length;
|
||||
|
||||
for (j = 2; j < polygon_vertice_count; ++j) {
|
||||
// verticeUvs = [];
|
||||
|
||||
vertex = polygon.vertices[0];
|
||||
// verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
|
||||
vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
|
||||
if (matrix) vertex.applyMatrix4(matrix);
|
||||
|
||||
if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
|
||||
vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
|
||||
} else {
|
||||
geometry.vertices.push(vertex);
|
||||
vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
|
||||
}
|
||||
|
||||
vertex = polygon.vertices[j - 1];
|
||||
// verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
|
||||
vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
|
||||
if (matrix) vertex.applyMatrix4(matrix);
|
||||
if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
|
||||
vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
|
||||
} else {
|
||||
geometry.vertices.push(vertex);
|
||||
vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
|
||||
}
|
||||
|
||||
vertex = polygon.vertices[j];
|
||||
// verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
|
||||
vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
|
||||
if (matrix) vertex.applyMatrix4(matrix);
|
||||
if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
|
||||
vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
|
||||
} else {
|
||||
geometry.vertices.push(vertex);
|
||||
vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
|
||||
}
|
||||
|
||||
face = new THREE.Face3(
|
||||
vertex_idx_a,
|
||||
vertex_idx_b,
|
||||
vertex_idx_c,
|
||||
new THREE.Vector3(polygon.normal.x, polygon.normal.y, polygon.normal.z)
|
||||
);
|
||||
|
||||
geometry.faces.push(face);
|
||||
// geometry.faceVertexUvs[0].push( verticeUvs );
|
||||
}
|
||||
|
||||
}
|
||||
return geometry;
|
||||
};
|
||||
|
||||
Geometry.prototype.scale = function (x, y, z) {
|
||||
// try to scale as THREE.BufferGeometry
|
||||
var polygons = this.tree.collectPolygons([]);
|
||||
|
||||
for (var i = 0; i < polygons.length; ++i) {
|
||||
var polygon = polygons[i];
|
||||
for (var k = 0; k < polygon.vertices.length; ++k) {
|
||||
var v = polygon.vertices[k];
|
||||
v.x *= x;
|
||||
v.y *= y;
|
||||
v.z *= z;
|
||||
}
|
||||
delete polygon.normal;
|
||||
polygon.calculateProperties();
|
||||
}
|
||||
};
|
||||
|
||||
Geometry.prototype.toPolygons = function () {
|
||||
var polygons = this.tree.collectPolygons([]);
|
||||
|
||||
this.tryToCompress(polygons);
|
||||
|
||||
for (var i = 0; i < polygons.length; ++i) {
|
||||
delete polygons[i].id;
|
||||
delete polygons[i].parent;
|
||||
}
|
||||
|
||||
return polygons;
|
||||
};
|
||||
|
||||
Geometry.prototype.toBufferGeometry = function () {
|
||||
return CreateBufferGeometry(this.toPolygons());
|
||||
};
|
||||
|
||||
export function CreateBufferGeometry(polygons) {
|
||||
var i, j, polygon_count = polygons.length, buf_size = 0;
|
||||
|
||||
for (i = 0; i < polygon_count; ++i)
|
||||
buf_size += (polygons[i].vertices.length - 2) * 9;
|
||||
|
||||
var positions_buf = new Float32Array(buf_size),
|
||||
normals_buf = new Float32Array(buf_size),
|
||||
iii = 0, polygon;
|
||||
|
||||
function CopyVertex(vertex) {
|
||||
|
||||
positions_buf[iii] = vertex.x;
|
||||
positions_buf[iii + 1] = vertex.y;
|
||||
positions_buf[iii + 2] = vertex.z;
|
||||
|
||||
normals_buf[iii] = polygon.nsign * vertex.nx;
|
||||
normals_buf[iii + 1] = polygon.nsign * vertex.ny;
|
||||
normals_buf[iii + 2] = polygon.nsign * vertex.nz;
|
||||
iii += 3;
|
||||
}
|
||||
|
||||
for (i = 0; i < polygon_count; ++i) {
|
||||
polygon = polygons[i];
|
||||
for (j = 2; j < polygon.vertices.length; ++j) {
|
||||
CopyVertex(polygon.vertices[0]);
|
||||
CopyVertex(polygon.vertices[j - 1]);
|
||||
CopyVertex(polygon.vertices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
var geometry = new THREE.BufferGeometry();
|
||||
geometry.addAttribute('position', new THREE.BufferAttribute(positions_buf, 3));
|
||||
geometry.addAttribute('normal', new THREE.BufferAttribute(normals_buf, 3));
|
||||
|
||||
// geometry.computeVertexNormals();
|
||||
return geometry;
|
||||
}
|
||||
|
||||
Geometry.prototype.toMesh = function (material) {
|
||||
var geometry = this.toGeometry(),
|
||||
mesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
if (this.matrix) {
|
||||
mesh.position.setFromMatrixPosition(this.matrix);
|
||||
mesh.rotation.setFromRotationMatrix(this.matrix);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
};
|
||||
|
||||
export class Polygon {
|
||||
constructor(vertices, normal, w) {
|
||||
if (!(vertices instanceof Array)) {
|
||||
vertices = [];
|
||||
}
|
||||
|
||||
this.vertices = vertices;
|
||||
this.nsign = 1;
|
||||
if (vertices.length > 0) {
|
||||
this.calculateProperties();
|
||||
} else {
|
||||
this.normal = this.w = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Polygon.prototype.copyProperties = function (parent, more) {
|
||||
this.normal = parent.normal; // .clone();
|
||||
this.w = parent.w;
|
||||
this.nsign = parent.nsign;
|
||||
if (more && (parent.id !== undefined)) {
|
||||
this.id = parent.id;
|
||||
this.parent = parent;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Polygon.prototype.calculateProperties = function () {
|
||||
if (this.normal) return;
|
||||
|
||||
var a = this.vertices[0],
|
||||
b = this.vertices[1],
|
||||
c = this.vertices[2];
|
||||
|
||||
this.nsign = 1;
|
||||
|
||||
this.normal = b.clone().subtract(a).cross(
|
||||
c.clone().subtract(a)
|
||||
).normalize();
|
||||
|
||||
this.w = this.normal.clone().dot(a);
|
||||
return this;
|
||||
};
|
||||
|
||||
Polygon.prototype.clone = function () {
|
||||
var vertice_count = this.vertices.length,
|
||||
polygon = new Polygon;
|
||||
|
||||
for (var i = 0; i < vertice_count; ++i)
|
||||
polygon.vertices.push(this.vertices[i].clone());
|
||||
|
||||
return polygon.copyProperties(this);
|
||||
};
|
||||
|
||||
Polygon.prototype.flip = function () {
|
||||
|
||||
/// normal is not changed, only sign variable
|
||||
//this.normal.multiplyScalar( -1 );
|
||||
//this.w *= -1;
|
||||
|
||||
this.nsign *= -1;
|
||||
|
||||
this.vertices.reverse();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Polygon.prototype.classifyVertex = function (vertex) {
|
||||
var side_value = this.nsign * (this.normal.dot(vertex) - this.w);
|
||||
|
||||
if (side_value < -EPSILON) return BACK;
|
||||
if (side_value > EPSILON) return FRONT;
|
||||
return COPLANAR;
|
||||
};
|
||||
|
||||
Polygon.prototype.classifySide = function (polygon) {
|
||||
var i, classification,
|
||||
num_positive = 0, num_negative = 0,
|
||||
vertice_count = polygon.vertices.length;
|
||||
|
||||
for (i = 0; i < vertice_count; ++i) {
|
||||
classification = this.classifyVertex(polygon.vertices[i]);
|
||||
if (classification === FRONT) {
|
||||
++num_positive;
|
||||
} else if (classification === BACK) {
|
||||
++num_negative;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_positive > 0 && num_negative === 0) return FRONT;
|
||||
if (num_positive === 0 && num_negative > 0) return BACK;
|
||||
if (num_positive === 0 && num_negative === 0) return COPLANAR;
|
||||
return SPANNING;
|
||||
};
|
||||
|
||||
Polygon.prototype.splitPolygon = function (polygon, coplanar_front, coplanar_back, front, back) {
|
||||
var classification = this.classifySide(polygon);
|
||||
|
||||
if (classification === COPLANAR) {
|
||||
|
||||
((this.nsign * polygon.nsign * this.normal.dot(polygon.normal) > 0) ? coplanar_front : coplanar_back).push(polygon);
|
||||
|
||||
} else if (classification === FRONT) {
|
||||
|
||||
front.push(polygon);
|
||||
|
||||
} else if (classification === BACK) {
|
||||
|
||||
back.push(polygon);
|
||||
|
||||
} else {
|
||||
|
||||
var vertice_count = polygon.vertices.length,
|
||||
nnx = this.normal.x,
|
||||
nny = this.normal.y,
|
||||
nnz = this.normal.z,
|
||||
i, j, ti, tj, vi, vj,
|
||||
t, v,
|
||||
f = [], b = [];
|
||||
|
||||
for (i = 0; i < vertice_count; ++i) {
|
||||
|
||||
j = (i + 1) % vertice_count;
|
||||
vi = polygon.vertices[i];
|
||||
vj = polygon.vertices[j];
|
||||
ti = this.classifyVertex(vi);
|
||||
tj = this.classifyVertex(vj);
|
||||
|
||||
if (ti != BACK) f.push(vi);
|
||||
if (ti != FRONT) b.push(vi);
|
||||
if ((ti | tj) === SPANNING) {
|
||||
// t = ( this.w - this.normal.dot( vi ) ) / this.normal.dot( vj.clone().subtract( vi ) );
|
||||
//v = vi.clone().lerp( vj, t );
|
||||
|
||||
t = (this.w - (nnx * vi.x + nny * vi.y + nnz * vi.z)) / (nnx * (vj.x - vi.x) + nny * (vj.y - vi.y) + nnz * (vj.z - vi.z));
|
||||
|
||||
v = vi.interpolate(vj, t);
|
||||
f.push(v);
|
||||
b.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
//if ( f.length >= 3 ) front.push( new Polygon( f ).calculateProperties() );
|
||||
//if ( b.length >= 3 ) back.push( new Polygon( b ).calculateProperties() );
|
||||
if (f.length >= 3) front.push(new Polygon(f).copyProperties(polygon, true));
|
||||
if (b.length >= 3) back.push(new Polygon(b).copyProperties(polygon, true));
|
||||
}
|
||||
};
|
||||
|
||||
export class Vertex {
|
||||
constructor(x, y, z, nx, ny, nz) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.nx = nx;
|
||||
this.ny = ny;
|
||||
this.nz = nz;
|
||||
};
|
||||
}
|
||||
|
||||
Vertex.prototype.setnormal = function (nx, ny, nz) {
|
||||
this.nx = nx;
|
||||
this.ny = ny;
|
||||
this.nz = nz;
|
||||
};
|
||||
|
||||
Vertex.prototype.clone = function () {
|
||||
return new Vertex(this.x, this.y, this.z, this.nx, this.ny, this.nz);
|
||||
};
|
||||
|
||||
Vertex.prototype.add = function (vertex) {
|
||||
this.x += vertex.x;
|
||||
this.y += vertex.y;
|
||||
this.z += vertex.z;
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.subtract = function (vertex) {
|
||||
this.x -= vertex.x;
|
||||
this.y -= vertex.y;
|
||||
this.z -= vertex.z;
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.multiplyScalar = function (scalar) {
|
||||
this.x *= scalar;
|
||||
this.y *= scalar;
|
||||
this.z *= scalar;
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.cross = function (vertex) {
|
||||
var x = this.x,
|
||||
y = this.y,
|
||||
z = this.z;
|
||||
|
||||
this.x = y * vertex.z - z * vertex.y;
|
||||
this.y = z * vertex.x - x * vertex.z;
|
||||
this.z = x * vertex.y - y * vertex.x;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.normalize = function () {
|
||||
var length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
||||
|
||||
this.x /= length;
|
||||
this.y /= length;
|
||||
this.z /= length;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.dot = function (vertex) {
|
||||
return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;
|
||||
};
|
||||
|
||||
Vertex.prototype.diff = function (vertex) {
|
||||
var dx = (this.x - vertex.x),
|
||||
dy = (this.y - vertex.y),
|
||||
dz = (this.z - vertex.z),
|
||||
len2 = this.x * this.x + this.y * this.y + this.z * this.z;
|
||||
|
||||
return (dx * dx + dy * dy + dz * dz) / (len2 > 0 ? len2 : 1e-10);
|
||||
};
|
||||
|
||||
/*
|
||||
Vertex.prototype.lerp = function( a, t ) {
|
||||
this.add(
|
||||
a.clone().subtract( this ).multiplyScalar( t )
|
||||
);
|
||||
|
||||
this.normal.add(
|
||||
a.normal.clone().sub( this.normal ).multiplyScalar( t )
|
||||
);
|
||||
|
||||
//this.uv.add(
|
||||
// a.uv.clone().sub( this.uv ).multiplyScalar( t )
|
||||
//);
|
||||
|
||||
return this;
|
||||
};
|
||||
Vertex.prototype.interpolate = function( other, t ) {
|
||||
return this.clone().lerp( other, t );
|
||||
};
|
||||
*/
|
||||
|
||||
Vertex.prototype.interpolate = function (a, t) {
|
||||
var t1 = 1 - t;
|
||||
return new Vertex(this.x * t1 + a.x * t, this.y * t1 + a.y * t, this.z * t1 + a.z * t,
|
||||
this.nx * t1 + a.nx * t, this.ny * t1 + a.ny * t, this.nz * t1 + a.nz * t);
|
||||
};
|
||||
|
||||
Vertex.prototype.applyMatrix4 = function (m) {
|
||||
|
||||
// input: THREE.Matrix4 affine matrix
|
||||
|
||||
var x = this.x, y = this.y, z = this.z, e = m.elements;
|
||||
|
||||
this.x = e[0] * x + e[4] * y + e[8] * z + e[12];
|
||||
this.y = e[1] * x + e[5] * y + e[9] * z + e[13];
|
||||
this.z = e[2] * x + e[6] * y + e[10] * z + e[14];
|
||||
|
||||
x = this.nx;
|
||||
y = this.ny;
|
||||
z = this.nz;
|
||||
|
||||
this.nx = e[0] * x + e[4] * y + e[8] * z;
|
||||
this.ny = e[1] * x + e[5] * y + e[9] * z;
|
||||
this.nz = e[2] * x + e[6] * y + e[10] * z;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
|
||||
export class Node {
|
||||
constructor(polygons, nodeid) {
|
||||
this.polygons = [];
|
||||
this.front = this.back = undefined;
|
||||
|
||||
if (!(polygons instanceof Array) || polygons.length === 0) return;
|
||||
|
||||
this.divider = polygons[0].clone();
|
||||
|
||||
var polygon_count = polygons.length,
|
||||
front = [], back = [];
|
||||
|
||||
for (var i = 0; i < polygon_count; ++i) {
|
||||
if (nodeid !== undefined) {
|
||||
polygons[i].id = nodeid++;
|
||||
delete polygons[i].parent;
|
||||
}
|
||||
|
||||
this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
|
||||
}
|
||||
|
||||
if (nodeid !== undefined) this.maxnodeid = nodeid;
|
||||
|
||||
if (front.length > 0)
|
||||
this.front = new Node(front);
|
||||
|
||||
if (back.length > 0)
|
||||
this.back = new Node(back);
|
||||
};
|
||||
}
|
||||
|
||||
Node.isConvex = function (polygons) {
|
||||
var i, j, len = polygons.length;
|
||||
for (i = 0; i < len; ++i)
|
||||
for (j = 0; j < len; ++j)
|
||||
if (i !== j && polygons[i].classifySide(polygons[j]) !== BACK) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
Node.prototype.build = function (polygons) {
|
||||
var polygon_count = polygons.length,
|
||||
front = [], back = [];
|
||||
|
||||
if (!this.divider)
|
||||
this.divider = polygons[0].clone();
|
||||
|
||||
for (var i = 0; i < polygon_count; ++i)
|
||||
this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
|
||||
|
||||
if (front.length > 0) {
|
||||
if (!this.front) this.front = new Node();
|
||||
this.front.build(front);
|
||||
}
|
||||
|
||||
if (back.length > 0) {
|
||||
if (!this.back) this.back = new Node();
|
||||
this.back.build(back);
|
||||
}
|
||||
};
|
||||
|
||||
Node.prototype.collectPolygons = function (arr) {
|
||||
var len = this.polygons.length;
|
||||
for (var i = 0; i < len; ++i) arr.push(this.polygons[i]);
|
||||
if (this.front) this.front.collectPolygons(arr);
|
||||
if (this.back) this.back.collectPolygons(arr);
|
||||
return arr;
|
||||
};
|
||||
|
||||
Node.prototype.allPolygons = function () {
|
||||
var polygons = this.polygons.slice();
|
||||
if (this.front) polygons = polygons.concat(this.front.allPolygons());
|
||||
if (this.back) polygons = polygons.concat(this.back.allPolygons());
|
||||
return polygons;
|
||||
};
|
||||
|
||||
Node.prototype.numPolygons = function () {
|
||||
var res = this.polygons.length;
|
||||
if (this.front) res += this.front.numPolygons();
|
||||
if (this.back) res += this.back.numPolygons();
|
||||
return res;
|
||||
};
|
||||
|
||||
Node.prototype.clone = function () {
|
||||
var node = new Node();
|
||||
|
||||
node.divider = this.divider.clone();
|
||||
node.polygons = this.polygons.map(function (polygon) {
|
||||
return polygon.clone();
|
||||
});
|
||||
node.front = this.front && this.front.clone();
|
||||
node.back = this.back && this.back.clone();
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
Node.prototype.invert = function () {
|
||||
var polygon_count = this.polygons.length;
|
||||
|
||||
for (var i = 0; i < polygon_count; ++i)
|
||||
this.polygons[i].flip();
|
||||
|
||||
this.divider.flip();
|
||||
if (this.front) this.front.invert();
|
||||
if (this.back) this.back.invert();
|
||||
|
||||
var temp = this.front;
|
||||
this.front = this.back;
|
||||
this.back = temp;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Node.prototype.clipPolygons = function (polygons) {
|
||||
|
||||
if (!this.divider) return polygons.slice();
|
||||
|
||||
var polygon_count = polygons.length, front = [], back = [];
|
||||
|
||||
for (var i = 0; i < polygon_count; ++i)
|
||||
this.divider.splitPolygon(polygons[i], front, back, front, back);
|
||||
|
||||
if (this.front) front = this.front.clipPolygons(front);
|
||||
if (this.back) back = this.back.clipPolygons(back);
|
||||
else back = [];
|
||||
|
||||
return front.concat(back);
|
||||
};
|
||||
|
||||
Node.prototype.clipTo = function (node) {
|
||||
this.polygons = node.clipPolygons(this.polygons);
|
||||
if (this.front) this.front.clipTo(node);
|
||||
if (this.back) this.back.clipTo(node);
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Three js demo for particle physics</title>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<script type="text/javascript" src="visionforge-spatial-gdml-0.1.0-dev.js"></script>
|
||||
</head>
|
||||
<body class="application">
|
||||
<div class="container" id="drop_zone" data-toggle="tooltip" data-placement="right"
|
||||
title="Для загрузки данных в текстовом формате, надо перетащить файл сюда">
|
||||
Загрузить данные
|
||||
<br/>
|
||||
(перетащить файл сюда)
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1>Demo grid</h1>
|
||||
</div>
|
||||
<div class="container" id="canvas"></div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
|
||||
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
|
||||
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
|
||||
crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user