/*
* @Description:
* @Author: Hongpeng Ma
* @Github: gitlab.com/hongpengm
* @Date: 2019-03-29 02:24:30
* @LastEditTime: 2019-04-14 01:11:03
*/
import { createDOM, shiftDOM } from '../utils/DOM'
import { GenomeScene } from './mesh/genomeScene'
import { LineScene } from './mesh/lineScene'
import { ExtrudeScene } from './mesh/extrudeScene'
import { GeneticElement } from './mesh/geneticElement'
import { InteractionLine } from './mesh/interactionLine'
import { Chrom3DModel } from './model/genomeModel'
import * as dat from 'dat.gui'
'use strict'
const THREE = require('three')
const d3 = require('d3')
const uidv4 = require('uuid/v4')
const stats = require('stats-js')
const axios = require('axios')
import { CControls } from './control/customizedControl'
require('./control/orbitControl.js')
/**
* Graphics 3D Application
*
* @class Graphics3DApplication
*/
class Graphics3DApplication {
/**
*Creates an instance of Graphics3DApplication.
* @param {*} parentDOM Parent DOM elements
* @memberof Graphics3DApplication
*/
constructor (parentDOM) {
this.id = uidv4()
this.initBBox(parentDOM)
this.updateFunctions = []
this.init()
if (Graphics3DApplication.instances === undefined) {
Graphics3DApplication.instances = []
}
Graphics3DApplication.instances.push(this)
}
/// //////////////////////////////////////////////////////////////////////////
// Init //
/// //////////////////////////////////////////////////////////////////////////
/**
* Init 3D Application
*
* @memberof Graphics3DApplication
*/
init () {
this.initScene()
this.initCamera()
this.initLight()
this.initRenderer()
this.initController()
this.initGUIControl()
}
/**
* Set the base DOM box given parent DOM
*
* @param {*} parentDOM
* @memberof Graphics3DApplication
*/
initBBox (parentDOM) {
this.baseDOM = createDOM('div', uidv4(), 1000, 1000,
{
left: 700
})
parentDOM.appendChild(this.baseDOM)
let baseDOMBox = this.baseDOM.getBoundingClientRect()
this.width = baseDOMBox.width
this.height = baseDOMBox.height
}
/**
* init the THREE.scene element of the 3D Application
*
* @memberof Graphics3DApplication
*/
initScene () {
this.scene = new THREE.Scene()
this.axesHelper = new THREE.AxesHelper(20)
this.scene.add(this.axesHelper)
this.scene.background = new THREE.Color( 0xf0f0f0 );
}
/**
* Init the camera element of the 3D Application
*
* @memberof Graphics3DApplication
*/
initCamera () {
this.camera = new THREE.PerspectiveCamera(
50,
this.width / this.height,
1,
3000
)
this.camera.position.set(0, 0, 300)
this.camera.lookAt(0,0,0)
this.camera.layers.enable(0)
this.camera.layers.enable(1)
}
/**
* Init the light element of the 3D Application
*
* @memberof Graphics3DApplication
*/
initLight () {
this.light = new THREE.PointLight(0xffffff)
this.light.position.copy(this.camera.position)
this.scene.add(this.light)
const ctx = this
this.addUpdateFunctions(() => {
let x = ctx.camera
while(x.parent){
x = x.parent
}
//console.log(x.position)
ctx.light.position.copy(x.position)
// console.log('Hello', ctx.light, ctx.camera)
})
}
/**
* Init the renderer element
*
* @memberof Graphics3DApplication
*/
initRenderer () {
this.renderer = new THREE.WebGLRenderer()
const r = this.renderer
r.setPixelRatio(window.devicePixelRatio)
r.setSize(this.width, this.height)
r.gammaInput = true
r.gammaOutput = true
this.baseDOM.appendChild(r.domElement)
}
/**
* Init the scene controller
*
* @memberof Graphics3DApplication
*/
initController () {
let orbitController = new THREE.OrbitControls(this.camera, this.renderer.domElement)
const c = orbitController
c.minDistance = 5
c.maxDistance = 2000
c.enablePan = false
c.enableDamping = true
c.dampingFactor = 0.25
let fpsController = new CControls(this.camera, this.renderer.domElement)
this.controller = orbitController
this.getController = ()=>{return this.controller}
this.controllers ={
orbit: orbitController,
fps: fpsController
}
}
initGUIControl(){
this.gui = new dat.GUI()
const gui = this.gui
// this.baseDOM.appendChild(this.gui.domElement)
gui.addFolder('Chromosomes')
gui.addFolder('Chrom Colors')
gui.addFolder('Camera Types')
const app = this
this.toggleController = {
controller: 'orbit'
}
gui.__folders['Camera Types']
.add(this.toggleController,'controller', ['orbit','fps'])
.name('Toggle Control')
.onChange(()=>{
this.controller = this.controllers[this.toggleController.controller]
this.controller.enabled = true
Object.keys(this.controllers).forEach(k=>{
if (k!== this.toggleController.controller){
this.controllers[k].enabled = false
}
})
switch (this.toggleController.controller){
case 'fps':
this.controller.init()
}
})
}
/// //////////////////////////////////////////////////////////////////////////
// Add Scene Obj //
/// //////////////////////////////////////////////////////////////////////////
/**
* Add a test sphere at x,y,z
*
* @param {Number} x
* @param {Number} y
* @param {Number} z
* @memberof Graphics3DApplication
*/
addTestSphere (x, y, z) {
let testObject = new THREE.Object3D()
let testObjectOffset = new THREE.Vector3(x, y, z)
testObject.position.add(testObjectOffset)
let testSphere = (() => {
let geometry = new THREE.SphereBufferGeometry(3)
let material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
let mesh_ = new THREE.Mesh(geometry, material)
return mesh_
})()
testObject.add(testSphere)
this.scene.add(testObject)
}
/**
* Add a test line
*
* @memberof Graphics3DApplication
*/
async addTestLine () {
const data = await this.getGenomeDataByURL('http://localhost:8080/Human/liberman_MDS.txt')
let color = new THREE.Color()
color.setHSL(0.5, 1, 0.5)
let chr_ = new LineScene(data.getChromPositions('1'), color)
chr_.setResolution(this.width, this.height)
this.scene.add(chr_.mesh)
this.addUpdateFunctions(
chr_.updateFunctions()
)
let chr2 = new ExtrudeScene(data.getChromPositions('2'), color)
this.scene.add(chr2.mesh)
// this.addUpdateFunctions(()=>{
// chr_.matLine.resolution.set(this.width,this.height)
// })
}
addTestGeneticElement (){
//let ge = new GeneticElement()
//this.scene.add(ge.mesh)
}
addTestInteractionLine(){
let testIL = new InteractionLine()
console.log(testIL.mesh, this.width, this.height)
this.addUpdateFunctions(()=>{
if(testIL.mesh.material.resolution){
testIL.mesh.material.resolution.set(this.width, this.height)
}
})
this.genomeScene.baseObject.add(testIL.mesh)
testIL.scene = this.genomeScene.baseObject
testIL.loadText(this.genomeScene.baseObject)
}
/**
* Add a test genome
*
* @memberof Graphics3DApplication
*/
async addTestGenome () {
const data = await this.getGenomeDataByURL('http://localhost:8080/Human/liberman_MDS.txt')
const genomeScene = new GenomeScene(this, data, {
options: {
skeletonType: 'tube'
}
})
genomeScene.setResolution(this.width, this.height)
genomeScene.loadGenomeMesh()
genomeScene.loadTestGeneticElements()
let testColor = new THREE.Color()
testColor.setHSL(0.8, 1, 0.5)
genomeScene.setChromHighlight('1', 0.3, 0.7, testColor)
genomeScene.removeAllHighlightChroms()
genomeScene.highlightChroms({
'2': { start: 0.4, end: 0.5, color: testColor }
})
genomeScene.addToScene();
//['1','2','3','4','5','6','7'].forEach(k=>{
// genomeScene.chroms[k].line.mesh.visible = false
//})
this.addUpdateFunctions(genomeScene.updateFunctions())
this.genomeScene = genomeScene
}
async addGenome(dataURL){
const app = this
const data = await this.getGenomeDataByURL(dataURL)
const genomeScene = new GenomeScene(this, data)
genomeScene.setResolution(this.width, this.height)
genomeScene.loadGenomeMesh()
genomeScene.addToScene()
this.addUpdateFunctions(genomeScene.updateFunctions())
this.genomeScene = genomeScene
}
/**
* Adding a customized mesh
*
* @memberof Graphics3DApplication
*/
addCustomizedMesh () {
}
/// //////////////////////////////////////////////////////////////////////////
// Retrieve Genome Data //
/// //////////////////////////////////////////////////////////////////////////
/**
* Async get URL data
*
* @param {String} url
* @returns {response}
* @memberof Graphics3DApplication
*/
async getURLData (url) {
return await axios.get(url)
}
/**
* Convert a genome string file to Chrom3DModel
*
* @param {String} str
* @returns {Chrom3DModel}
* @memberof Graphics3DApplication
*/
parseGenomeStringData (str) {
return new Chrom3DModel(str)
}
/**
* Async retrieve the genome data given a url
*
* @param {String} url
* @returns {Chrom3DModel}
* @memberof Graphics3DApplication
*/
async getGenomeDataByURL (url) {
const response = await this.getURLData(url)
return this.parseGenomeStringData(response.data)
}
/// //////////////////////////////////////////////////////////////////////////
// Animate & Render //
/// //////////////////////////////////////////////////////////////////////////
/**
* Animate the application
*
* @param {*} app
* @memberof Graphics3DApplication
*/
animate (app) {
const ctx = this
window.requestAnimationFrame(app.animate)
this.render()
}
/**
* Render the scene
*
* @memberof Graphics3DApplication
*/
render (time) {
const r = this.renderer
r.setClearColor(0x000000, 0)
r.setViewport(0, 0, this.width, this.height)
// console.log(this.updateFunctions)
this.executeUpdateFunctions()
r.render(this.scene, this.camera)
if (this.controller.update){
this.getController().update(time)
this.getController().camera.updateMatrixWorld()
}
}
/**
* Add a function to execute every render loop
*
* @param {Function} f
* @memberof Graphics3DApplication
*/
addUpdateFunctions (f) {
if (Array.isArray(f)) {
this.updateFunctions = this.updateFunctions.concat(f)
} else {
this.updateFunctions.push(f)
}
}
/**
* Execute update functions in the updateFunctions queue
*
* @memberof Graphics3DApplication
*/
executeUpdateFunctions () {
this.updateFunctions.forEach(f => {
f()
})
}
}
Graphics3DApplication.each = function (f) {
Graphics3DApplication.instances.forEach(f)
}
export { Graphics3DApplication }