forked from kscience/visionforge
Demo app
This commit is contained in:
parent
b5164916bb
commit
598030f970
@ -1,5 +1,13 @@
|
||||
val dataforgeVersion by extra("0.1.3-dev-7")
|
||||
|
||||
plugins{
|
||||
kotlin("jvm") version "1.3.40" apply false
|
||||
id("kotlin2js") version "1.3.40" apply false
|
||||
id("kotlin-dce-js") version "1.3.40" apply false
|
||||
id("org.jetbrains.kotlin.frontend") version "0.0.45" apply false
|
||||
id("scientifik.mpp") version "0.1.0" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
|
@ -1,22 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
jcenter()
|
||||
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
}
|
||||
|
||||
val kotlinVersion = "1.3.40"
|
||||
|
||||
// Add plugins used in buildSrc as dependencies, also we should specify version only here
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
||||
implementation("org.jfrog.buildinfo:build-info-extractor-gradle:4.9.6")
|
||||
implementation("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4")
|
||||
implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.9.18")
|
||||
implementation("com.moowork.gradle:gradle-node-plugin:1.3.1")
|
||||
implementation("org.openjfx:javafx-plugin:0.0.7")
|
||||
implementation("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45")
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
// Instead of defining runtime properties and use them dynamically
|
||||
// define version in buildSrc and have autocompletion and compile-time check
|
||||
// Also dependencies itself can be moved here
|
||||
object Versions {
|
||||
val ioVersion = "0.1.10"
|
||||
val coroutinesVersion = "1.2.1"
|
||||
val atomicfuVersion = "0.12.6"
|
||||
val serializationVersion = "0.11.1"
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
import org.jetbrains.dokka.gradle.DokkaTask
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
|
||||
plugins {
|
||||
id("org.jetbrains.dokka")
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
|
||||
extensions.findByType<KotlinMultiplatformExtension>()?.apply{
|
||||
val dokka by tasks.getting(DokkaTask::class) {
|
||||
outputFormat = "html"
|
||||
outputDirectory = "$buildDir/javadoc"
|
||||
jdkVersion = 8
|
||||
|
||||
kotlinTasks {
|
||||
// dokka fails to retrieve sources from MPP-tasks so we only define the jvm task
|
||||
listOf(tasks.getByPath("compileKotlinJvm"))
|
||||
}
|
||||
sourceRoot {
|
||||
// assuming only single source dir
|
||||
path = sourceSets["commonMain"].kotlin.srcDirs.first().toString()
|
||||
platforms = listOf("Common")
|
||||
}
|
||||
// although the JVM sources are now taken from the task,
|
||||
// we still define the jvm source root to get the JVM marker in the generated html
|
||||
sourceRoot {
|
||||
// assuming only single source dir
|
||||
path = sourceSets["jvmMain"].kotlin.srcDirs.first().toString()
|
||||
platforms = listOf("JVM")
|
||||
}
|
||||
}
|
||||
|
||||
val kdocJar by tasks.registering(Jar::class) {
|
||||
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||
dependsOn(dokka)
|
||||
archiveClassifier.set("javadoc")
|
||||
from("$buildDir/javadoc")
|
||||
}
|
||||
|
||||
configure<PublishingExtension> {
|
||||
|
||||
targets.all {
|
||||
val publication = publications.findByName(name) as MavenPublication
|
||||
|
||||
// Patch publications with fake javadoc
|
||||
publication.artifact(kdocJar.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extensions.findByType<KotlinJvmProjectExtension>()?.apply{
|
||||
val dokka by tasks.getting(DokkaTask::class) {
|
||||
outputFormat = "html"
|
||||
outputDirectory = "$buildDir/javadoc"
|
||||
jdkVersion = 8
|
||||
}
|
||||
|
||||
val kdocJar by tasks.registering(Jar::class) {
|
||||
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||
dependsOn(dokka)
|
||||
archiveClassifier.set("javadoc")
|
||||
from("$buildDir/javadoc")
|
||||
}
|
||||
|
||||
configure<PublishingExtension> {
|
||||
publications.filterIsInstance<MavenPublication>().forEach { publication ->
|
||||
publication.artifact(kdocJar.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import com.moowork.gradle.node.npm.NpmTask
|
||||
import com.moowork.gradle.node.task.NodeTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||
|
||||
plugins {
|
||||
id("com.moowork.node")
|
||||
kotlin("multiplatform")
|
||||
}
|
||||
|
||||
node {
|
||||
nodeModulesDir = file("$buildDir/node_modules")
|
||||
}
|
||||
|
||||
val compileKotlinJs by tasks.getting(Kotlin2JsCompile::class)
|
||||
val compileTestKotlinJs by tasks.getting(Kotlin2JsCompile::class)
|
||||
|
||||
val populateNodeModules by tasks.registering(Copy::class) {
|
||||
dependsOn(compileKotlinJs)
|
||||
from(compileKotlinJs.destinationDir)
|
||||
|
||||
kotlin.js().compilations["test"].runtimeDependencyFiles.forEach {
|
||||
if (it.exists() && !it.isDirectory) {
|
||||
from(zipTree(it.absolutePath).matching { include("*.js") })
|
||||
}
|
||||
}
|
||||
|
||||
into("$buildDir/node_modules")
|
||||
}
|
||||
|
||||
val installMocha by tasks.registering(NpmTask::class) {
|
||||
setWorkingDir(buildDir)
|
||||
setArgs(listOf("install", "mocha"))
|
||||
}
|
||||
|
||||
val runMocha by tasks.registering(NodeTask::class) {
|
||||
dependsOn(compileTestKotlinJs, populateNodeModules, installMocha)
|
||||
setScript(file("$buildDir/node_modules/mocha/bin/mocha"))
|
||||
setArgs(listOf(compileTestKotlinJs.outputFile))
|
||||
}
|
||||
|
||||
tasks["jsTest"].dependsOn(runMocha)
|
||||
|
||||
|
@ -1,82 +0,0 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
js {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
metaInfo = true
|
||||
sourceMap = true
|
||||
sourceMapEmbedSources = "always"
|
||||
moduleKind = "commonjs"
|
||||
}
|
||||
}
|
||||
|
||||
compilations.named("main") {
|
||||
kotlinOptions {
|
||||
main = "call"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api(kotlin("stdlib"))
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
api(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
}
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(kotlin("test-junit"))
|
||||
}
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
api(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
val jsTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test-js"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targets.all {
|
||||
sourceSets.all {
|
||||
languageSettings.progressiveMode = true
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
}
|
||||
}
|
||||
|
||||
// Apply JS test configuration
|
||||
val runJsTests by ext(false)
|
||||
|
||||
if (runJsTests) {
|
||||
apply(plugin = "js-test")
|
||||
}
|
||||
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.jfrog.bintray.gradle.tasks.BintrayUploadTask
|
||||
import groovy.lang.GroovyObject
|
||||
import org.gradle.api.publish.maven.internal.artifact.FileBasedMavenArtifact
|
||||
import org.jfrog.gradle.plugin.artifactory.dsl.PublisherConfig
|
||||
import org.jfrog.gradle.plugin.artifactory.dsl.ResolverConfig
|
||||
|
||||
// Old bintray.gradle script converted to real Gradle plugin (precompiled script plugin)
|
||||
// It now has own dependencies and support type safe accessors
|
||||
// Syntax is pretty close to what we had in Groovy
|
||||
// (excluding Property.set and bintray dynamic configs)
|
||||
|
||||
plugins {
|
||||
`maven-publish`
|
||||
id("com.jfrog.bintray")
|
||||
id("com.jfrog.artifactory")
|
||||
}
|
||||
|
||||
val vcs = "https://github.com/altavir/dataforge-core"
|
||||
val bintrayRepo = "https://bintray.com/mipt-npm/dataforge"
|
||||
|
||||
// Configure publishing
|
||||
publishing {
|
||||
repositories {
|
||||
maven(bintrayRepo)
|
||||
}
|
||||
|
||||
// Process each publication we have in this project
|
||||
publications.filterIsInstance<MavenPublication>().forEach { publication ->
|
||||
|
||||
// use type safe pom config GSL instead of old dynamic
|
||||
publication.pom {
|
||||
name.set(project.name)
|
||||
description.set(project.description)
|
||||
url.set(vcs)
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name.set("The Apache Software License, Version 2.0")
|
||||
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
distribution.set("repo")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("MIPT-NPM")
|
||||
name.set("MIPT nuclear physics methods laboratory")
|
||||
organization.set("MIPT")
|
||||
organizationUrl.set("http://npm.mipt.ru")
|
||||
}
|
||||
|
||||
}
|
||||
scm {
|
||||
url.set(vcs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bintray {
|
||||
user = findProperty("bintrayUser") as? String ?: System.getenv("BINTRAY_USER")
|
||||
key = findProperty("bintrayApiKey") as? String? ?: System.getenv("BINTRAY_API_KEY")
|
||||
publish = true
|
||||
override = true // for multi-platform Kotlin/Native publishing
|
||||
|
||||
// We have to use delegateClosureOf because bintray supports only dynamic groovy syntax
|
||||
// this is a problem of this plugin
|
||||
pkg.apply {
|
||||
userOrg = "mipt-npm"
|
||||
repo = "dataforge"
|
||||
name = project.name
|
||||
issueTrackerUrl = "$vcs/issues"
|
||||
setLicenses("Apache-2.0")
|
||||
vcsUrl = vcs
|
||||
version.apply {
|
||||
name = project.version.toString()
|
||||
vcsTag = project.version.toString()
|
||||
released = java.util.Date().toString()
|
||||
}
|
||||
}
|
||||
|
||||
//workaround bintray bug
|
||||
afterEvaluate {
|
||||
setPublications(*publishing.publications.names.toTypedArray())
|
||||
}
|
||||
|
||||
tasks {
|
||||
bintrayUpload {
|
||||
dependsOn(publishToMavenLocal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//workaround for bintray
|
||||
tasks.withType<BintrayUploadTask> {
|
||||
doFirst {
|
||||
publishing.publications
|
||||
.filterIsInstance<MavenPublication>()
|
||||
.forEach { publication ->
|
||||
val moduleFile = buildDir.resolve("publications/${publication.name}/module.json")
|
||||
if (moduleFile.exists()) {
|
||||
publication.artifact(object : FileBasedMavenArtifact(moduleFile) {
|
||||
override fun getDefaultExtension() = "module"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
artifactory {
|
||||
val artifactoryUser: String? by project
|
||||
val artifactoryPassword: String? by project
|
||||
val artifactoryContextUrl = "http://npm.mipt.ru:8081/artifactory"
|
||||
|
||||
setContextUrl(artifactoryContextUrl)//The base Artifactory URL if not overridden by the publisher/resolver
|
||||
publish(delegateClosureOf<PublisherConfig> {
|
||||
repository(delegateClosureOf<GroovyObject> {
|
||||
setProperty("repoKey", "gradle-dev-local")
|
||||
setProperty("username", artifactoryUser)
|
||||
setProperty("password", artifactoryPassword)
|
||||
})
|
||||
|
||||
defaults(delegateClosureOf<GroovyObject> {
|
||||
invokeMethod("publications", arrayOf("jvm", "js", "kotlinMultiplatform", "metadata"))
|
||||
})
|
||||
})
|
||||
resolve(delegateClosureOf<ResolverConfig> {
|
||||
repository(delegateClosureOf<GroovyObject> {
|
||||
setProperty("repoKey", "gradle-dev")
|
||||
setProperty("username", artifactoryUser)
|
||||
setProperty("password", artifactoryPassword)
|
||||
})
|
||||
})
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
`npm-multiplatform`
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
||||
val dataforgeVersion: String by rootProject.extra
|
||||
@ -19,6 +19,7 @@ kotlin {
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
|
||||
// api("hep.dataforge:dataforge-output-js:$dataforgeVersion")
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,177 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
/**
|
||||
* Taken from https://github.com/markaren/three.kt/blob/master/threejs-wrapper/src/main/kotlin/info/laht/threekt/math/ColorConstants.kt
|
||||
*/
|
||||
object Colors {
|
||||
const val aliceblue = 0xF0F8FF
|
||||
const val antiquewhite = 0xFAEBD7
|
||||
const val aqua = 0x00FFFF
|
||||
const val aquamarine = 0x7FFFD4
|
||||
const val azure = 0xF0FFFF
|
||||
|
||||
const val beige = 0xF5F5DC
|
||||
const val bisque = 0xFFE4C4
|
||||
const val black = 0x000000
|
||||
const val blanchedalmond = 0xFFEBCD
|
||||
const val blue = 0x0000FF
|
||||
const val blueviolet = 0x8A2BE2
|
||||
|
||||
const val brown = 0xA52A2A
|
||||
const val burlywood = 0xDEB887
|
||||
const val cadetblue = 0x5F9EA0
|
||||
const val chartreuse = 0x7FFF00
|
||||
const val chocolate = 0xD2691E
|
||||
const val coral = 0xFF7F50
|
||||
|
||||
const val cornflowerblue = 0x6495ED
|
||||
const val cornsilk = 0xFFF8DC
|
||||
const val crimson = 0xDC143C
|
||||
const val cyan = 0x00FFFF
|
||||
const val darkblue = 0x00008B
|
||||
const val darkcyan = 0x008B8B
|
||||
|
||||
const val darkgoldenrod = 0xB8860B
|
||||
const val darkgray = 0xA9A9A9
|
||||
const val darkgreen = 0x006400
|
||||
const val darkgrey = 0xA9A9A9
|
||||
const val darkkhaki = 0xBDB76B
|
||||
const val darkmagenta = 0x8B008B
|
||||
|
||||
const val darkolivegreen = 0x556B2F
|
||||
const val darkorange = 0xFF8C00
|
||||
const val darkorchid = 0x9932CC
|
||||
const val darkred = 0x8B0000
|
||||
const val darksalmon = 0xE9967A
|
||||
const val darkseagreen = 0x8FBC8F
|
||||
|
||||
const val darkslateblue = 0x483D8B
|
||||
const val darkslategray = 0x2F4F4F
|
||||
const val darkslategrey = 0x2F4F4F
|
||||
const val darkturquoise = 0x00CED1
|
||||
const val darkviolet = 0x9400D3
|
||||
|
||||
const val deeppink = 0xFF1493
|
||||
const val deepskyblue = 0x00BFFF
|
||||
const val dimgray = 0x696969
|
||||
const val dimgrey = 0x696969
|
||||
const val dodgerblue = 0x1E90FF
|
||||
const val firebrick = 0xB22222
|
||||
|
||||
const val floralwhite = 0xFFFAF0
|
||||
const val forestgreen = 0x228B22
|
||||
const val fuchsia = 0xFF00FF
|
||||
const val gainsboro = 0xDCDCDC
|
||||
const val ghostwhite = 0xF8F8FF
|
||||
const val gold = 0xFFD700
|
||||
|
||||
const val goldenrod = 0xDAA520
|
||||
const val gray = 0x808080
|
||||
const val green = 0x008000
|
||||
const val greenyellow = 0xADFF2F
|
||||
const val grey = 0x808080
|
||||
const val honeydew = 0xF0FFF0
|
||||
const val hotpink = 0xFF69B4
|
||||
|
||||
const val indianred = 0xCD5C5C
|
||||
const val indigo = 0x4B0082
|
||||
const val ivory = 0xFFFFF0
|
||||
const val khaki = 0xF0E68C
|
||||
const val lavender = 0xE6E6FA
|
||||
const val lavenderblush = 0xFFF0F5
|
||||
const val lawngreen = 0x7CFC00
|
||||
|
||||
const val lemonchiffon = 0xFFFACD
|
||||
const val lightblue = 0xADD8E6
|
||||
const val lightcoral = 0xF08080
|
||||
const val lightcyan = 0xE0FFFF
|
||||
const val lightgoldenrodyellow = 0xFAFAD2
|
||||
const val lightgray = 0xD3D3D3
|
||||
|
||||
const val lightgreen = 0x90EE90
|
||||
const val lightgrey = 0xD3D3D3
|
||||
const val lightpink = 0xFFB6C1
|
||||
const val lightsalmon = 0xFFA07A
|
||||
const val lightseagreen = 0x20B2AA
|
||||
const val lightskyblue = 0x87CEFA
|
||||
|
||||
const val lightslategray = 0x778899
|
||||
const val lightslategrey = 0x778899
|
||||
const val lightsteelblue = 0xB0C4DE
|
||||
const val lightyellow = 0xFFFFE0
|
||||
const val lime = 0x00FF00
|
||||
const val limegreen = 0x32CD32
|
||||
|
||||
const val linen = 0xFAF0E6
|
||||
const val magenta = 0xFF00FF
|
||||
const val maroon = 0x800000
|
||||
const val mediumaquamarine = 0x66CDAA
|
||||
const val mediumblue = 0x0000CD
|
||||
const val mediumorchid = 0xBA55D3
|
||||
|
||||
const val mediumpurple = 0x9370DB
|
||||
const val mediumseagreen = 0x3CB371
|
||||
const val mediumslateblue = 0x7B68EE
|
||||
const val mediumspringgreen = 0x00FA9A
|
||||
const val mediumturquoise = 0x48D1CC
|
||||
|
||||
const val mediumvioletred = 0xC71585
|
||||
const val midnightblue = 0x191970
|
||||
const val mintcream = 0xF5FFFA
|
||||
const val mistyrose = 0xFFE4E1
|
||||
const val moccasin = 0xFFE4B5
|
||||
const val navajowhite = 0xFFDEAD
|
||||
|
||||
const val navy = 0x000080
|
||||
const val oldlace = 0xFDF5E6
|
||||
const val olive = 0x808000
|
||||
const val olivedrab = 0x6B8E23
|
||||
const val orange = 0xFFA500
|
||||
const val orangered = 0xFF4500
|
||||
const val orchid = 0xDA70D6
|
||||
|
||||
const val palegoldenrod = 0xEEE8AA
|
||||
const val palegreen = 0x98FB98
|
||||
const val paleturquoise = 0xAFEEEE
|
||||
const val palevioletred = 0xDB7093
|
||||
const val papayawhip = 0xFFEFD5
|
||||
const val peachpuff = 0xFFDAB9
|
||||
const val peru = 0xCD853F
|
||||
const val pink = 0xFFC0CB
|
||||
const val plum = 0xDDA0DD
|
||||
const val powderblue = 0xB0E0E6
|
||||
const val purple = 0x800080
|
||||
const val rebeccapurple = 0x663399
|
||||
const val red = 0xFF0000
|
||||
const val rosybrown = 0xBC8F8F
|
||||
|
||||
const val royalblue = 0x4169E1
|
||||
const val saddlebrown = 0x8B4513
|
||||
const val salmon = 0xFA8072
|
||||
const val sandybrown = 0xF4A460
|
||||
const val seagreen = 0x2E8B57
|
||||
const val seashell = 0xFFF5EE
|
||||
|
||||
const val sienna = 0xA0522D
|
||||
const val silver = 0xC0C0C0
|
||||
const val skyblue = 0x87CEEB
|
||||
const val slateblue = 0x6A5ACD
|
||||
const val slategray = 0x708090
|
||||
const val slategrey = 0x708090
|
||||
const val snow = 0xFFFAFA
|
||||
|
||||
const val springgreen = 0x00FF7F
|
||||
const val steelblue = 0x4682B4
|
||||
const val tan = 0xD2B48C
|
||||
const val teal = 0x008080
|
||||
const val thistle = 0xD8BFD8
|
||||
const val tomato = 0xFF6347
|
||||
const val turquoise = 0x40E0D0
|
||||
|
||||
const val violet = 0xEE82EE
|
||||
const val wheat = 0xF5DEB3
|
||||
const val white = 0xFFFFFF
|
||||
const val whitesmoke = 0xF5F5F5
|
||||
const val yellow = 0xFFFF00
|
||||
const val yellowgreen = 0x9ACD32
|
||||
}
|
@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.openjfx.gradle.JavaFXOptions
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("jvm")
|
||||
id("org.openjfx.javafxplugin")
|
||||
}
|
||||
|
||||
|
@ -1,70 +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")
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://kotlin.bintray.com/kotlin-js-wrappers")
|
||||
}
|
||||
|
||||
val kotlinVersion: String by rootProject.extra
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-js"))
|
||||
implementation(project(":dataforge-vis-common"))
|
||||
implementation("org.jetbrains:kotlin-react:16.6.0-pre.73-kotlin-1.3.40")
|
||||
implementation("org.jetbrains:kotlin-react-dom:16.6.0-pre.73-kotlin-1.3.40")
|
||||
testCompile(kotlin("test-js"))
|
||||
}
|
||||
|
||||
configure<KotlinFrontendExtension> {
|
||||
downloadNodeJsVersion = "latest"
|
||||
|
||||
configure<NpmExtension> {
|
||||
dependency("core-js", "3.1.4")
|
||||
dependency("cp-react-tree-table","1.0.0-beta.6")
|
||||
dependency("react")
|
||||
dependency("react-dom")
|
||||
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.js.tree
|
||||
|
||||
import react.RBuilder
|
||||
import react.RComponent
|
||||
import react.RProps
|
||||
import react.RState
|
||||
|
||||
class MetaEditorState : RState {
|
||||
|
||||
}
|
||||
|
||||
class MetaEditorProps : RProps
|
||||
|
||||
class MetaEditor(props: MetaEditorProps) : RComponent<MetaEditorProps, MetaEditorState>(props) {
|
||||
override fun RBuilder.render() {
|
||||
//this.
|
||||
}
|
||||
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
@file:JsModule("cp-react-tree-table")
|
||||
@file:JsNonModule
|
||||
|
||||
package hep.dataforge.vis.js.tree
|
||||
|
||||
import react.Component
|
||||
|
||||
external interface RowMetadata {
|
||||
var depth: Number
|
||||
var index: Number
|
||||
var height: Number
|
||||
var hasChildren: Boolean
|
||||
}
|
||||
|
||||
external interface RowState {
|
||||
var isVisible: Boolean
|
||||
var top: Number
|
||||
}
|
||||
|
||||
open external class RowModel(data: RowData, metadata: RowMetadata, state: RowState) {
|
||||
var data: RowData
|
||||
var metadata: RowMetadata
|
||||
var `$state`: RowState
|
||||
|
||||
companion object {
|
||||
var DEFAULT_HEIGHT: Number
|
||||
}
|
||||
}
|
||||
|
||||
external interface RowAPI {
|
||||
var toggleChildren: () -> Unit
|
||||
var updateData: (newData: RowData) -> Unit
|
||||
}
|
||||
|
||||
external class Row(model: RowModel, api: RowAPI) : RowModel, RowAPI {
|
||||
override var toggleChildren: () -> Unit
|
||||
override var updateData: (newData: RowData) -> Unit
|
||||
}
|
||||
|
||||
external class Column : Component<ColumnProps, dynamic> {
|
||||
override fun render(): dynamic
|
||||
|
||||
companion object {
|
||||
var displayName: String
|
||||
}
|
||||
}
|
||||
|
||||
external interface TreeNode {
|
||||
val data: Any
|
||||
val children: Array<TreeNode>?
|
||||
val height: Number?
|
||||
}
|
||||
|
||||
external class TreeState(data: Array<RowModel>) {
|
||||
var data: Array<RowModel>
|
||||
var height: Number
|
||||
var hasData: Boolean
|
||||
fun findRowModel(node: TreeNode): RowModel?
|
||||
fun indexAtYPos(yPos: Number): Number
|
||||
fun yPosAtIndex(index: Number): Number
|
||||
|
||||
companion object {
|
||||
fun create(data: Array<TreeNode>): TreeState
|
||||
fun createEmpty(): TreeState
|
||||
fun sliceRows(source: TreeState, from: Number, to: Number): Array<RowModel>
|
||||
var _hideRowsInRange: Any
|
||||
var _showRowsInRange: Any
|
||||
fun expandAll(source: TreeState, depthLimit: Number? = definedExternally /* null */): TreeState
|
||||
fun collapseAll(source: TreeState): TreeState
|
||||
fun expandAncestors(source: TreeState, model: RowModel): TreeState
|
||||
fun toggleChildren(source: TreeState, model: RowModel): TreeState
|
||||
fun updateData(source: TreeState, model: RowModel, newData: RowData): TreeState
|
||||
}
|
||||
}
|
||||
|
||||
external class TreeTable : Component<TreeTableProps, dynamic> {
|
||||
var vListRef: Any
|
||||
override fun render(): dynamic
|
||||
var handleChange: Any
|
||||
fun scrollTo(posY: Number): Unit
|
||||
|
||||
companion object {
|
||||
val Column: Column
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package hep.dataforge.vis.js.tree.demo
|
||||
|
||||
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,54 +0,0 @@
|
||||
package hep.dataforge.vis.js.tree.demo
|
||||
|
||||
import hep.dataforge.vis.js.tree.ApplicationBase
|
||||
import hep.dataforge.vis.js.tree.TreeTable
|
||||
import hep.dataforge.vis.js.tree.column
|
||||
import hep.dataforge.vis.js.tree.tree
|
||||
import react.dom.render
|
||||
import react.dom.span
|
||||
import kotlin.browser.document
|
||||
|
||||
|
||||
data class TableData(val name: String, val col1: String, val col2: Int)
|
||||
|
||||
class TreeDemoApp : ApplicationBase() {
|
||||
|
||||
override val stateKeys: List<String> = emptyList()
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
println("Starting application")
|
||||
val element = document.getElementById("demo")!!
|
||||
|
||||
println("Started application")
|
||||
|
||||
println("${TreeTable::class} is loaded")
|
||||
|
||||
|
||||
|
||||
render(element) {
|
||||
child(TreeTable::class) {
|
||||
attrs {
|
||||
tree<TableData> {
|
||||
child(TableData("aaa", "bbb", 2))
|
||||
child(TableData("ccc", "ddd", 66)) {
|
||||
child(TableData("ddd", "ggg", 22))
|
||||
}
|
||||
}
|
||||
onScroll = {}
|
||||
}
|
||||
column<TableData>("title"){
|
||||
span { +(it.data as TableData).name }
|
||||
}
|
||||
column<TableData>("col1"){
|
||||
span { +(it.data as TableData).col1 }
|
||||
}
|
||||
column<TableData>("col2"){
|
||||
span { +(it.data as TableData).col2 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() = emptyMap<String, Any>()//mapOf("lines" to presenter.dispose())
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package hep.dataforge.vis.js.tree
|
||||
|
||||
import hep.dataforge.vis.js.tree.demo.TreeDemoApp
|
||||
import hep.dataforge.vis.js.tree.demo.module
|
||||
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("testApp") == true) {
|
||||
val application = TreeDemoApp()
|
||||
|
||||
@Suppress("UnsafeCastFromDynamic")
|
||||
application.start(state?.appState ?: emptyMap<String, Any>())
|
||||
|
||||
application
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package hep.dataforge.vis.js.tree
|
||||
|
||||
import react.RProps
|
||||
import react.ReactElement
|
||||
|
||||
class ColumnProps(
|
||||
var renderCell: (row: Row) -> ReactElement,
|
||||
var renderHeaderCell: () -> ReactElement,
|
||||
var grow: Number? = null,
|
||||
var basis: String? = null // <CSS size> | auto
|
||||
) : RProps
|
||||
|
||||
class TreeTableProps(
|
||||
// Model properties
|
||||
var value: TreeState,
|
||||
var children: Array<ReactElement>,
|
||||
var onChange: ((TreeState) -> Unit)? = null,
|
||||
// View callbacks
|
||||
var onScroll: ((scrollTop: Number) -> Unit)? = null,
|
||||
|
||||
// View properties
|
||||
var height: Number? = null, // view height (px)
|
||||
var headerHeight: Number? = null, // header height (px)
|
||||
var className: String? = null
|
||||
) : RProps
|
@ -1,49 +0,0 @@
|
||||
package hep.dataforge.vis.js.tree
|
||||
|
||||
import react.RElementBuilder
|
||||
import react.ReactElement
|
||||
import react.createElement
|
||||
import react.dom.span
|
||||
|
||||
typealias RowData = Any
|
||||
|
||||
class TreeNodeBuilder<D : Any>(override val data: D, override var height: Number? = null) : TreeNode {
|
||||
private val _children = ArrayList<TreeNodeBuilder<D>>()
|
||||
override val children: Array<TreeNode> get() = _children.toTypedArray()
|
||||
|
||||
fun child(data: D, block: TreeNodeBuilder<D>.() -> Unit = {}) {
|
||||
val child = TreeNodeBuilder(data).apply(block)
|
||||
_children.add(child)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TreeTableBuilder<D : Any> {
|
||||
private val children = ArrayList<TreeNode>()
|
||||
|
||||
fun child(data: D, height: Number? = null, block: TreeNodeBuilder<D>.() -> Unit = {}) {
|
||||
this.children.add(TreeNodeBuilder(data, height).apply(block))
|
||||
}
|
||||
|
||||
fun build(): TreeState = TreeState.create(children.toTypedArray())
|
||||
}
|
||||
|
||||
fun <D : Any> TreeState.Companion.build(block: TreeTableBuilder<D>.() -> Unit): TreeState {
|
||||
return TreeTableBuilder<D>().apply(block).build()
|
||||
}
|
||||
|
||||
fun <D : Any> TreeTableProps.tree(block: TreeTableBuilder<D>.() -> Unit) {
|
||||
value = TreeTableBuilder<D>().apply(block).build()
|
||||
}
|
||||
|
||||
fun <D : Any> RElementBuilder<TreeTableProps>.column(
|
||||
name: String,
|
||||
renderer: (Row) -> ReactElement
|
||||
): ReactElement {
|
||||
val props = ColumnProps(
|
||||
renderHeaderCell = { span { +name } },
|
||||
renderCell = { row -> renderer.invoke(row) }
|
||||
)
|
||||
return createElement(TreeTable.Column, props = props)
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Three js demo for tree</title>
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
</head>
|
||||
<body class="testApp">
|
||||
<h1>Tree demo</h1>
|
||||
<div id="demo"></div>
|
||||
</body>
|
||||
</html>
|
@ -14,6 +14,7 @@ val kotlinVersion: String by rootProject.extra
|
||||
|
||||
dependencies {
|
||||
implementation(project(":dataforge-vis-spatial"))
|
||||
//implementation("ch.viseon.threejs:wrapper:105.0.0")
|
||||
implementation("info.laht.threekt:threejs-wrapper:0.88-npm-2")
|
||||
testCompile(kotlin("test-js"))
|
||||
}
|
||||
|
@ -5,14 +5,15 @@ import hep.dataforge.meta.double
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import info.laht.threekt.materials.Material
|
||||
import info.laht.threekt.materials.MeshPhongMaterial
|
||||
import info.laht.threekt.math.Color
|
||||
import info.laht.threekt.math.ColorConstants
|
||||
|
||||
|
||||
object Materials {
|
||||
val DEFAULT = MeshPhongMaterial().apply {
|
||||
this.color.set(ColorConstants.darkgreen)
|
||||
this.color.set(Colors.darkgreen)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,98 +0,0 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.number
|
||||
import hep.dataforge.vis.ApplicationBase
|
||||
import hep.dataforge.vis.common.DisplayGroup
|
||||
import hep.dataforge.vis.require
|
||||
import hep.dataforge.vis.spatial.jsroot.JSRootPlugin
|
||||
import hep.dataforge.vis.spatial.jsroot.jsRootGeometry
|
||||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.browser.document
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
class ThreeDemoApp : ApplicationBase() {
|
||||
|
||||
override val stateKeys: List<String> = emptyList()
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
require("JSRootGeoBase.js")
|
||||
|
||||
|
||||
//TODO remove after DI fix
|
||||
// Global.plugins.load(ThreePlugin())
|
||||
// Global.plugins.load(JSRootPlugin())
|
||||
|
||||
Global.plugins.load(JSRootPlugin)
|
||||
|
||||
val renderer = ThreeOutput(Global)
|
||||
renderer.start(document.getElementById("canvas")!!)
|
||||
println("started")
|
||||
|
||||
lateinit var group: DisplayGroup
|
||||
|
||||
renderer.render {
|
||||
group = group {
|
||||
box {
|
||||
z = 110.0
|
||||
xSize = 100.0
|
||||
ySize = 100.0
|
||||
zSize = 100.0
|
||||
}
|
||||
box {
|
||||
visible = false
|
||||
x = 110.0
|
||||
xSize = 100.0
|
||||
ySize = 100.0
|
||||
zSize = 100.0
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
visible = !visible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
convex {
|
||||
point(50, 50, 50)
|
||||
point(-50, -50, 50)
|
||||
point(-50, 50, -50)
|
||||
point(50, -50, -50)
|
||||
}
|
||||
jsRootGeometry {
|
||||
y = 110.0
|
||||
shape = box(50, 50, 50)
|
||||
color(12285)
|
||||
}
|
||||
}
|
||||
|
||||
var color by group.properties.number(1530).int
|
||||
|
||||
GlobalScope.launch {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
color = random.nextInt(0, Int.MAX_VALUE)
|
||||
}
|
||||
}
|
||||
|
||||
// view.animate()
|
||||
|
||||
// view = WebLinesView(document.getElementById("lines")!!, document.getElementById("addForm")!!)
|
||||
// presenter = LinesPresenter(view)
|
||||
//
|
||||
// state["lines"]?.let { linesState ->
|
||||
// @Suppress("UNCHECKED_CAST")
|
||||
// presenter.restore(linesState as Array<String>)
|
||||
// }
|
||||
}
|
||||
|
||||
override fun dispose() = emptyMap<String, Any>()//mapOf("lines" to presenter.dispose())
|
||||
}
|
@ -11,6 +11,7 @@ import hep.dataforge.vis.spatial.ThreeFactory.Companion.updateMesh
|
||||
import hep.dataforge.vis.spatial.three.ConvexBufferGeometry
|
||||
import hep.dataforge.vis.spatial.three.EdgesGeometry
|
||||
import hep.dataforge.vis.spatial.three.euler
|
||||
import hep.dataforge.vis.spatial.three.toBufferGeometry
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.geometries.BoxBufferGeometry
|
||||
@ -68,6 +69,14 @@ interface ThreeFactory<T : DisplayObject> {
|
||||
}
|
||||
}
|
||||
|
||||
operator fun <T : DisplayObject> ThreeFactory<T>.invoke(obj: Any): Object3D {
|
||||
if (type.isInstance(obj)) {
|
||||
return invoke(obj as T)
|
||||
} else {
|
||||
error("The object of type ${obj::class} could not be rendered by this factory")
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MeshThreeFactory<T : DisplayObject>(override val type: KClass<out T>) : ThreeFactory<T> {
|
||||
/**
|
||||
* Build an object
|
||||
@ -90,6 +99,17 @@ abstract class MeshThreeFactory<T : DisplayObject>(override val type: KClass<out
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic factory for elements which provide inside geometry builder
|
||||
*/
|
||||
object ThreeShapeFactory : MeshThreeFactory<Shape>(Shape::class) {
|
||||
override fun buildGeometry(obj: Shape): BufferGeometry {
|
||||
return obj.run {
|
||||
ThreeGeometryBuilder().apply { buildGeometry() }.build()
|
||||
}.toBufferGeometry()
|
||||
}
|
||||
}
|
||||
|
||||
object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
|
||||
override fun buildGeometry(obj: Box) =
|
||||
BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize)
|
||||
|
@ -0,0 +1,47 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.int
|
||||
import info.laht.threekt.core.Face3
|
||||
import info.laht.threekt.core.Geometry
|
||||
import info.laht.threekt.math.Color
|
||||
import info.laht.threekt.math.Vector3
|
||||
|
||||
class ThreeGeometryBuilder : GeometryBuilder<Geometry> {
|
||||
|
||||
private val vertices = ArrayList<Point3D>()
|
||||
private val faces = ArrayList<Face3>()
|
||||
|
||||
private fun append(vertex: Point3D): Int {
|
||||
val index = vertices.indexOf(vertex)
|
||||
return if (index > 0) {
|
||||
index
|
||||
} else {
|
||||
vertices.add(vertex)
|
||||
vertices.size - 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) {
|
||||
val materialIndex = meta["materialIndex"].int ?: 0
|
||||
val color = meta["color"]?.color() ?: Color()
|
||||
faces.add(
|
||||
Face3(
|
||||
append(vertex1),
|
||||
append(vertex2),
|
||||
append(vertex3),
|
||||
normal?.asVector() ?: Vector3(0, 0, 0),
|
||||
color,
|
||||
materialIndex
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun build(): Geometry {
|
||||
return Geometry().apply {
|
||||
vertices = this@ThreeGeometryBuilder.vertices.map { it.asVector() }.toTypedArray()
|
||||
faces = this@ThreeGeometryBuilder.faces.toTypedArray()
|
||||
}
|
||||
}
|
||||
}
|
@ -11,114 +11,89 @@ import info.laht.threekt.WebGLRenderer
|
||||
import info.laht.threekt.cameras.PerspectiveCamera
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.external.controls.OrbitControls
|
||||
import info.laht.threekt.helpers.AxesHelper
|
||||
import info.laht.threekt.lights.AmbientLight
|
||||
import info.laht.threekt.math.ColorConstants
|
||||
import info.laht.threekt.scenes.Scene
|
||||
import org.w3c.dom.Element
|
||||
import kotlin.browser.window
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class ThreeOutput(override val context: Context, val meta: Meta = EmptyMeta) : Output<DisplayObject> {
|
||||
|
||||
private val renderer = WebGLRenderer { antialias = true }.apply {
|
||||
setClearColor(ColorConstants.skyblue, 1)
|
||||
setSize(window.innerWidth, window.innerHeight)
|
||||
}
|
||||
private val aspectRatio by meta.number(1.0).double
|
||||
|
||||
val scene: Scene = Scene().apply {
|
||||
add(AmbientLight())
|
||||
if (meta["axis"] != null) {
|
||||
val axesHelper = AxesHelper(meta["axis.size"].int ?: 1)
|
||||
add(axesHelper)
|
||||
}
|
||||
}
|
||||
|
||||
val camera = PerspectiveCamera(
|
||||
meta["fov"].int ?: 75,
|
||||
window.innerWidth.toDouble() / window.innerHeight,
|
||||
meta["camera.nearClip"].double?: World.CAMERA_NEAR_CLIP,
|
||||
meta["camera.farClip"].double?: World.CAMERA_FAR_CLIP
|
||||
meta["camera.fov"].int ?: 75,
|
||||
aspectRatio,
|
||||
meta["camera.nearClip"].double ?: World.CAMERA_NEAR_CLIP,
|
||||
meta["camera.farClip"].double ?: World.CAMERA_FAR_CLIP
|
||||
).apply {
|
||||
position.setZ(World.CAMERA_INITIAL_DISTANCE)
|
||||
rotation.set(World.CAMERA_INITIAL_X_ANGLE, World.CAMERA_INITIAL_Y_ANGLE, World.CAMERA_INITIAL_Z_ANGLE)
|
||||
}
|
||||
|
||||
val controls: OrbitControls = OrbitControls(camera, renderer.domElement)
|
||||
fun attach(element: Element, computeWidth: Element.() -> Int = { element.clientWidth }) {
|
||||
|
||||
val root get() = renderer.domElement
|
||||
val width by meta.number(computeWidth(element)).int
|
||||
|
||||
private fun animate() {
|
||||
window.requestAnimationFrame {
|
||||
animate()
|
||||
val height: Int = (width / aspectRatio).toInt()
|
||||
|
||||
val renderer = WebGLRenderer { antialias = true }.apply {
|
||||
setClearColor(ColorConstants.skyblue, 1)
|
||||
setSize(width, height)
|
||||
}
|
||||
|
||||
val controls: OrbitControls = OrbitControls(camera, renderer.domElement)
|
||||
|
||||
fun animate() {
|
||||
window.requestAnimationFrame {
|
||||
animate()
|
||||
}
|
||||
renderer.render(scene, camera)
|
||||
}
|
||||
renderer.render(scene, camera)
|
||||
}
|
||||
|
||||
fun start(element: Element) {
|
||||
window.addEventListener("resize", {
|
||||
camera.aspect = window.innerWidth.toDouble() / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
camera.updateProjectionMatrix()
|
||||
|
||||
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||
val width by meta.number(computeWidth(element)).int
|
||||
|
||||
renderer.setSize(width, (width / aspectRatio).toInt())
|
||||
}, false)
|
||||
element.appendChild(root)
|
||||
|
||||
element.replaceWith(renderer.domElement)
|
||||
animate()
|
||||
}
|
||||
|
||||
|
||||
private fun buildNode(obj: DisplayObject): Object3D? {
|
||||
return when (obj) {
|
||||
is DisplayGroup -> Group(obj.mapNotNull { buildNode(it) }).apply {
|
||||
ThreeFactory.updatePosition(obj, this)
|
||||
return if (obj is DisplayGroup) Group(obj.mapNotNull { buildNode(it) }).apply {
|
||||
ThreeFactory.updatePosition(obj, this)
|
||||
} else {
|
||||
val factory = context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == obj::class }
|
||||
when {
|
||||
factory != null -> factory(obj)
|
||||
obj is Shape -> ThreeShapeFactory(obj)
|
||||
else -> error("Renderer for ${obj::class} not found")
|
||||
}
|
||||
//is Box -> ThreeBoxFactory(obj)
|
||||
//is JSRootObject -> ThreeJSRootFactory(obj)
|
||||
//is Convex -> ThreeConvexFactory(obj)
|
||||
else -> findFactory(obj::class)?.invoke(obj) ?: error("Factory not found")
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : DisplayObject> findFactory(type: KClass<out T>): ThreeFactory<T>? {
|
||||
return context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == type } as? ThreeFactory<T>?
|
||||
}
|
||||
|
||||
override fun render(obj: DisplayObject, meta: Meta) {
|
||||
buildNode(obj)?.let {
|
||||
scene.add(it)
|
||||
} ?: error("Renderer for ${obj::class} not found")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// init {
|
||||
// val cube: Mesh
|
||||
//
|
||||
// cube = Mesh(
|
||||
// BoxBufferGeometry(1, 1, 1),
|
||||
// MeshPhongMaterial().apply {
|
||||
// this.color.set(ColorConstants.darkgreen)
|
||||
// }
|
||||
// ).also(scene::add)
|
||||
//
|
||||
// Mesh(cube.geometry as BufferGeometry,
|
||||
// MeshBasicMaterial().apply {
|
||||
// this.wireframe = true
|
||||
// this.color.set(ColorConstants.black)
|
||||
// }
|
||||
// ).also(cube::add)
|
||||
//
|
||||
// val points = CatmullRomCurve3(
|
||||
// arrayOf(
|
||||
// Vector3(-10, 0, 10),
|
||||
// Vector3(-5, 5, 5),
|
||||
// Vector3(0, 0, 0),
|
||||
// Vector3(5, -5, 5),
|
||||
// Vector3(10, 0, 10)
|
||||
// )
|
||||
// ).getPoints(50)
|
||||
//
|
||||
// val geometry = BufferGeometry().setFromPoints(points)
|
||||
//
|
||||
// val material = LineBasicMaterial().apply {
|
||||
// color.set(0xff0000)
|
||||
// }
|
||||
//
|
||||
// // Create the final object to add to the scene
|
||||
// Line(geometry, material).apply(scene::add)
|
||||
// }
|
||||
companion object {
|
||||
fun build(context: Context, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit) =
|
||||
ThreeOutput(context, buildMeta(meta,override))
|
||||
}
|
||||
}
|
@ -13,12 +13,10 @@ class ThreePlugin : AbstractPlugin() {
|
||||
val factories = HashMap<Name, ThreeFactory<*>>()
|
||||
|
||||
init {
|
||||
factories["box".toName()] = ThreeBoxFactory
|
||||
//factories["box".toName()] = ThreeBoxFactory
|
||||
factories["convex".toName()] = ThreeConvexFactory
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun listNames(target: String): Sequence<Name> {
|
||||
return when (target) {
|
||||
ThreeFactory.TYPE -> factories.keys.asSequence()
|
||||
|
@ -1,4 +1,4 @@
|
||||
package hep.dataforge.vis
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
external val module: Module
|
||||
|
@ -0,0 +1,77 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.context.ContextBuilder
|
||||
import hep.dataforge.meta.number
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.jsroot.JSRootPlugin
|
||||
import hep.dataforge.vis.spatial.jsroot.jsRootGeometry
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
class ThreeDemoApp : ApplicationBase() {
|
||||
|
||||
override val stateKeys: List<String> = emptyList()
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
require("JSRootGeoBase.js")
|
||||
|
||||
//TODO replace by optimized builder after dataforge 0.1.3-dev-8
|
||||
val context = ContextBuilder("three-demo").apply {
|
||||
plugin(JSRootPlugin())
|
||||
}.build()
|
||||
|
||||
val grid = context.plugins.load(ThreeDemoGrid()).apply {
|
||||
demo("group", "Group demo") {
|
||||
val group = group {
|
||||
box {
|
||||
z = 110.0
|
||||
xSize = 100.0
|
||||
ySize = 100.0
|
||||
zSize = 100.0
|
||||
}
|
||||
box {
|
||||
visible = false
|
||||
x = 110.0
|
||||
xSize = 100.0
|
||||
ySize = 100.0
|
||||
zSize = 100.0
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
visible = !visible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var color by group.properties.number(1530).int
|
||||
|
||||
GlobalScope.launch {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
color = random.nextInt(0, Int.MAX_VALUE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("jsroot", "JSROOT cube"){
|
||||
jsRootGeometry {
|
||||
y = 110.0
|
||||
shape = box(50, 50, 50)
|
||||
color(12285)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun dispose() = emptyMap<String, Any>()//mapOf("lines" to presenter.dispose())
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.context.AbstractPlugin
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.context.PluginFactory
|
||||
import hep.dataforge.context.PluginTag
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.string
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.output.Output
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.vis.common.DisplayObject
|
||||
import hep.dataforge.vis.common.DisplayObjectList
|
||||
import hep.dataforge.vis.spatial.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.dom.create
|
||||
import kotlinx.html.h2
|
||||
import kotlinx.html.hr
|
||||
import kotlinx.html.id
|
||||
import kotlinx.html.js.div
|
||||
import kotlinx.html.span
|
||||
import kotlin.browser.document
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
private val gridRoot = document.create.div("row")
|
||||
private val outputs: MutableMap<Name, ThreeOutput> = HashMap()
|
||||
|
||||
override fun attach(context: Context) {
|
||||
super.attach(context)
|
||||
val elementId = meta["elementID"].string ?: "canvas"
|
||||
val element = document.getElementById(elementId) ?: error("Element with id $elementId not found on page")
|
||||
element.append(gridRoot)
|
||||
}
|
||||
|
||||
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Output<T> {
|
||||
return outputs.getOrPut(name) {
|
||||
if (type != DisplayObject::class) error("Supports only DisplayObject")
|
||||
val output = ThreeOutput.build(context, meta) {
|
||||
"axis" to {
|
||||
"size" to 500
|
||||
}
|
||||
}
|
||||
gridRoot.append {
|
||||
span("border") {
|
||||
div("col-4") {
|
||||
h2 { +(meta["title"].string ?: name.toString()) }
|
||||
hr()
|
||||
val id = "output-$name"
|
||||
output.attach(div { this.id = id }) { 300 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
} as Output<T>
|
||||
}
|
||||
|
||||
companion object : PluginFactory<ThreeDemoGrid> {
|
||||
override val tag: PluginTag = PluginTag(group = "hep.dataforge", name = "vis.js.spatial.demo")
|
||||
|
||||
override val type: KClass<out ThreeDemoGrid> = ThreeDemoGrid::class
|
||||
|
||||
override fun invoke(meta: Meta): ThreeDemoGrid = ThreeDemoGrid(meta)
|
||||
}
|
||||
}
|
||||
|
||||
fun ThreeDemoGrid.demo(name: String, title: String = name, block: DisplayObjectList.() -> Unit) {
|
||||
val meta = buildMeta {
|
||||
"title" to title
|
||||
}
|
||||
val output = get<DisplayObject>(DisplayObject::class, name.toName(), meta = meta)
|
||||
output.render(action = block)
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package hep.dataforge.vis
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.vis.spatial.ThreeDemoApp
|
||||
import kotlin.browser.document
|
||||
import kotlin.dom.hasClass
|
||||
|
@ -2,8 +2,8 @@ package hep.dataforge.vis.spatial.jsroot
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.vis.ApplicationBase
|
||||
import hep.dataforge.vis.spatial.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.demo.ApplicationBase
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
import org.w3c.dom.events.Event
|
||||
@ -62,7 +62,7 @@ class JSRootDemoApp : ApplicationBase() {
|
||||
val renderer = ThreeOutput(Global)
|
||||
val canvas = document.getElementById("canvas")!!
|
||||
canvas.clear()
|
||||
renderer.start(canvas)
|
||||
renderer.attach(canvas)
|
||||
println("started")
|
||||
|
||||
renderer.render {
|
||||
|
@ -1,29 +1,47 @@
|
||||
<!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>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<!-- <style>-->
|
||||
<!-- body {-->
|
||||
<!-- margin: 0;-->
|
||||
<!-- overflow: hidden;-->
|
||||
<!-- }-->
|
||||
<!-- </style>-->
|
||||
<!--<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>-->
|
||||
<!--<script type="text/javascript" src="js/OrbitControls.js"></script>-->
|
||||
<!--<script type="text/javascript" src="js/three.min.js"></script>-->
|
||||
<!--<script type="text/javascript" src="js/ThreeCSG.js"></script>-->
|
||||
<!--<script type="text/javascript" src="js/JSRootCore.js"></script>-->
|
||||
<!--<script type="text/javascript" src="js/JSRootGeoBase.js"></script>-->
|
||||
<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="main.bundle.js"></script>
|
||||
</head>
|
||||
<body class="testApp">
|
||||
<div id="drop_zone" data-toggle="tooltip" data-placement="right"
|
||||
<!--
|
||||
<div class="container" id="drop_zone" data-toggle="tooltip" data-placement="right"
|
||||
title="Для загрузки данных в текстовом формате, надо перетащить файл сюда">
|
||||
Загрузить данные
|
||||
<br/>
|
||||
(перетащить файл сюда)
|
||||
</div>
|
||||
<h1>Demo canvas</h1>
|
||||
<div id="canvas"></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>
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
`npm-multiplatform`
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
@ -7,13 +7,33 @@ import hep.dataforge.vis.common.DisplayObject
|
||||
import hep.dataforge.vis.common.DisplayObjectList
|
||||
import hep.dataforge.vis.common.double
|
||||
|
||||
class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta) {
|
||||
class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape {
|
||||
var xSize by double(1.0)
|
||||
var ySize by double(1.0)
|
||||
var zSize by double(1.0)
|
||||
|
||||
//TODO add helper for color configuration
|
||||
|
||||
override fun <T : Any> GeometryBuilder<T>.buildGeometry() {
|
||||
val dx = xSize / 2
|
||||
val dy = ySize / 2
|
||||
val dz = zSize / 2
|
||||
val node1 = Point3D(-dx, -dy, -dz)
|
||||
val node2 = Point3D(dx, -dy, -dz)
|
||||
val node3 = Point3D(dx, dy, -dz)
|
||||
val node4 = Point3D(-dx, dy, -dz)
|
||||
val node5 = Point3D(-dx, -dy, dz)
|
||||
val node6 = Point3D(dx, -dy, dz)
|
||||
val node7 = Point3D(dx, dy, dz)
|
||||
val node8 = Point3D(-dx, dy, dz)
|
||||
face4(node1, node4, node3, node2, Point3D(0, 0, -1))
|
||||
face4(node1, node2, node6, node5, Point3D(0, -1, 0))
|
||||
face4(node2, node3, node7, node6, Point3D(1, 0, 0))
|
||||
face4(node4, node8, node7, node3, Point3D(0, 1, 0))
|
||||
face4(node1, node5, node8, node4, Point3D(-1, 0, 0))
|
||||
face4(node8, node5, node6, node7, Point3D(0, 0, 1))
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE = "geometry.3d.box"
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaRepr
|
||||
import hep.dataforge.meta.buildMeta
|
||||
|
||||
|
||||
data class Point2D(val x: Number, val y: Number)
|
||||
|
||||
typealias Shape2D = List<Point2D>
|
||||
|
||||
data class Point3D(val x: Number, val y: Number, val z: Number): MetaRepr{
|
||||
override fun toMeta(): Meta = buildMeta {
|
||||
"x" to x
|
||||
"y" to y
|
||||
"z" to z
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaRepr
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.vis.common.DisplayObject
|
||||
|
||||
data class Point2D(val x: Number, val y: Number)
|
||||
|
||||
typealias Shape2D = List<Point2D>
|
||||
|
||||
data class Point3D(val x: Number, val y: Number, val z: Number) : MetaRepr {
|
||||
override fun toMeta(): Meta = buildMeta {
|
||||
"x" to x
|
||||
"y" to y
|
||||
"z" to z
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param T the type of resulting geometry
|
||||
*/
|
||||
interface GeometryBuilder<T: Any> {
|
||||
/**
|
||||
* Add a face to 3D model. If one of the vertices is not present in the current geometry model list of vertices,
|
||||
* it is added automatically.
|
||||
*
|
||||
* @param normal optional external normal to the face
|
||||
* @param meta optional additional platform-specific parameters like color or texture index
|
||||
*/
|
||||
fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = EmptyMeta)
|
||||
|
||||
fun build(): T
|
||||
|
||||
}
|
||||
|
||||
fun GeometryBuilder<*>.face4(
|
||||
vertex1: Point3D,
|
||||
vertex2: Point3D,
|
||||
vertex3: Point3D,
|
||||
vertex4: Point3D,
|
||||
normal: Point3D? = null,
|
||||
meta: Meta = EmptyMeta
|
||||
) {
|
||||
face(vertex1, vertex2, vertex3, normal, meta)
|
||||
face(vertex1, vertex3, vertex4, normal, meta)
|
||||
}
|
||||
|
||||
interface Shape: DisplayObject {
|
||||
fun <T: Any> GeometryBuilder<T>.buildGeometry()
|
||||
}
|
@ -3,14 +3,22 @@ pluginManagement {
|
||||
jcenter()
|
||||
gradlePluginPortal()
|
||||
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
maven("https://dl.bintray.com/mipt-npm/scientifik")
|
||||
}
|
||||
|
||||
val kotlinVersion = "1.3.40"
|
||||
resolutionStrategy {
|
||||
eachPlugin {
|
||||
when (requested.id.id) {
|
||||
"kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}")
|
||||
"kotlin-multiplatform" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
|
||||
"org.jetbrains.kotlin.jvm" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
|
||||
"org.jetbrains.kotlin.js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
|
||||
"kotlin-dce-js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
|
||||
"kotlin2js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
|
||||
"org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45")
|
||||
"org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:${requested.version}")
|
||||
"scientifik.mpp", "scientifik.publish" -> useModule("scientifik:gradle-tools:${requested.version}")
|
||||
"org.openjfx.javafxplugin" -> useModule("org.openjfx:javafx-plugin:${requested.version}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,8 +33,7 @@ include(
|
||||
":dataforge-vis-fx",
|
||||
":dataforge-vis-spatial",
|
||||
":dataforge-vis-spatial-fx",
|
||||
":dataforge-vis-spatial-js",
|
||||
":dataforge-vis-js-tree"
|
||||
":dataforge-vis-spatial-js"
|
||||
)
|
||||
|
||||
//if(file("../dataforge-core").exists()) {
|
||||
|
Loading…
Reference in New Issue
Block a user