source: js/human_3d_alignment/src/visualization/sviewer.js @ 976

Last change on this file since 976 was 963, checked in by Maciej Komosinski, 4 years ago

Initial random rotation and location

File size: 13.6 KB
Line 
1/*global Module*/
2"use strict";
3
4import * as THREE from 'three';
5import Framstick from './framstick';
6import Scene from './scene';
7import Camera from './camera';
8const TransformControls = require('three-transform-controls')(THREE);
9
10/** Camera class configuration for Viewer */
11export const cameraconfig = {
12    fieldOfView: 75,
13    near: 0.1,
14    far: 1000,
15    autoRotateSpeed: 2,
16    defaultSettings: {
17        target: {
18            x: 0,
19            y: 0,
20            z: 0
21        },
22        position: {
23            x: 0.56,
24            y: 0.56,
25            z: 0.56
26        },
27    }
28};
29
30/** Light configuration for Viewer */
31export const lightconfig = {
32    ambient: {
33        color: 0x303030
34    },
35    directional: {
36        top: {
37            color: 0x808080,
38            intensity: 1.0,
39            positionZ: 1000
40        },
41        bottom: {
42            color: 0x808080,
43            intensity: 1.0,
44            positionZ: -1000
45        }
46    }
47};
48
49/**
50 * Class for visualizing static Framsticks creatures. Connects all visualization
51 * classes and render scene in given HTML canvas. For resizing it is recommended
52 * to put canvas in resizable div and then compute new widths and heights from
53 * this parent.
54 */
55class Viewer {
56    /**
57     * Default constructor for Viewer. It initializes all 3D visualization objects
58     * in Scene  without creature Meshes. It needs canvas to be created in
59     * HTML - it does not create canvas on its own, like default WebGLRenderer.
60     * Width and height should be taken from canvas parent
61     * @param {number} width width of canvas
62     * @param {number} height height of canvas
63     * @param {Element} canvasdom DOM reference to canvas.
64     */
65    constructor(width, height, canvasdom) {
66        this.container = document.getElementById("simcontainer");
67
68        this.meshes = [];
69        this.parts = [];
70        this.lines = [];
71        this.framstick1 = null;
72        this.framstick2 = null;
73        this.selected1 = [];
74        this.selected2 = [];
75
76        this.renderer = new THREE.WebGLRenderer({
77            antialias: true,
78            devicePixelRatio: window.devicePixelRatio || 1,
79            canvas: canvasdom
80        });
81        this.renderer.setSize(width, height);
82        this.raycaster = new THREE.Raycaster();
83        this.scene = new Scene();
84        this.camera = new Camera(cameraconfig, this.renderer.domElement, false);
85        this.control = new TransformControls(this.camera.getPerspectiveCamera(), this.renderer.domElement);
86        this.control.addEventListener( 'change', (e) => {
87            this.addLines(this.selected1, this.selected2);
88            this.addText();
89        });
90        this.control.addEventListener( 'dragging-changed', function(e) {
91            this.camera.getCameraControl().enable = ! e.value;
92        });
93        this.scene.add(this.control);
94        this.control.setMode( "translate" );
95
96        let ambientLight = new THREE.AmbientLight(lightconfig.ambient.color);
97        this.scene.add(ambientLight);
98
99        let topDirectionalLight = new THREE.DirectionalLight(
100            lightconfig.directional.top.color,
101            lightconfig.directional.top.intensity);
102        topDirectionalLight.position.set(0, 0, lightconfig.directional.top.positionZ);
103        this.scene.add(topDirectionalLight);
104
105        let bottomDirectionalLight = new THREE.DirectionalLight(
106            lightconfig.directional.bottom.color,
107            lightconfig.directional.bottom.intensity);
108        bottomDirectionalLight.position.set(0, 0, lightconfig.directional.bottom.positionZ);
109        this.scene.add(bottomDirectionalLight);
110
111        this.renderer.setClearColor('gray');
112        this.rezoom = false;
113        this.autorotate = false;
114        this.renderScene = this.renderScene.bind(this);
115        this.selectedelements = [];
116
117        this.mouse = new THREE.Vector2();
118        this.SELECTED = null;
119        this.CLICKED = false;
120
121
122        this.lineMaterial = new THREE.LineBasicMaterial({
123            color: 0xffff00,
124            transparent: true,
125            opacity: 0.5
126        });
127
128        this.textLabels = [];
129        this.container = document.getElementById("simcontainer");
130    }
131
132    /**
133     * Allow to clear view and add new pair of genotypes
134     * @param {string} genotype1 Genotype of first framstick
135     * @param {string} genotype2 Genotype of second framstick
136     */
137    add2Genotypes(genotype1, genotype2) {
138        this.clearView();
139
140        this.framstick1 = new Framstick(genotype1, this);
141        this.framstick2 = new Framstick(genotype2, this);
142
143        this.meshes = [];
144        this.parts = [];
145
146        for (let i = 0; i < this.framstick1.mesh.children.length; i++) {
147            this.meshes.push(this.framstick1.mesh.children[i]);
148        }
149
150        for (let i = 0; i < this.framstick2.mesh.children.length; i++) {
151            this.meshes.push(this.framstick2.mesh.children[i]);
152        }
153
154        for (let i = 0; i < this.framstick1.parts.length; i++) {
155            this.parts.push(this.framstick1.parts[i]);
156        }
157
158        for (let i = 0; i < this.framstick2.parts.length; i++) {
159            this.parts.push(this.framstick2.parts[i]);
160        }
161       
162        let dist = 2;
163        let angle = Math.random()*Math.PI*2;
164        let x = Math.cos(angle)*dist;
165        let y = Math.sin(angle)*dist;
166
167        this.framstick1.mesh.position.set( x, y, 0 );
168        this.framstick2.mesh.position.set( -x, -y, 0 );
169
170        this.framstick1.setPositions();
171        this.framstick2.setPositions();
172
173        this.framstick1.setColor('red');
174        this.framstick2.setColor('blue');
175
176        this.camera.zoomAll(this.meshes);
177        this.addText();
178    }
179
180    /**
181     * Draw lines between selected parts of both framsticks
182     * @param {Array} selected1 array of values that user selected in fitview for first genotype
183     * @param {Array} selected2 array of values that user selected in fitview for second genotype
184     */
185    addLines(selected1, selected2) {
186        for (let i = 0; i < this.lines.length; i++) {
187            this.scene.remove(this.lines[i]);
188        }
189
190        this.lines = [];
191
192        let pairs = (selected1.length + selected2.length) / 2;
193
194        for (let i = 0; i < pairs; i++) {
195            if (selected1[i] != ' ' && selected2[i] != ' ') {
196                let index1 = parseInt(selected1[i]) - 1;
197                let index2 = selected2[i].charCodeAt(0) - 65;
198
199                let tempV1 = new THREE.Vector3().copy(this.framstick1.positions[index1]).applyEuler(this.framstick1.mesh.rotation).add(this.framstick1.mesh.position);
200                let tempV2 = new THREE.Vector3().copy(this.framstick2.positions[index2]).applyEuler(this.framstick2.mesh.rotation).add(this.framstick2.mesh.position);
201
202                let geometry = new THREE.Geometry();
203                geometry.vertices.push( tempV1 );
204                geometry.vertices.push( tempV2 );
205
206                let line = new THREE.Line( geometry, this.lineMaterial );
207                this.lines.push(line);
208            }
209        }
210
211        for (let i = 0; i < this.lines.length; i++) {
212            this.scene.add(this.lines[i]);
213        }
214    }
215
216    addText() {
217
218        while (this.container.children.length > 3){
219            this.container.removeChild(this.container.lastChild)
220        }
221
222        this.textLabels = [];
223
224        if (this.framstick1 != null && this.framstick2 != null) {
225            this.framstick1.addText(1);
226            this.framstick2.addText(2);
227        }
228
229    }
230
231    /**
232     * Allow to change mode of TransformControls
233     * @param {string} mode Selected mode of transformcontrols
234     */
235    setMode(mode) {
236        this.control.setMode(mode);
237    }
238
239    /**
240     * Selects object from scene according to current mouse position. The position
241     * of mouse should be transformed to screen space, which has values from -1 to 1
242     * for each dimension.
243     * @param {{x: number, y: number}} position position of mouse in canvas normalized to [-1,1]x[-1,1] space
244     */
245    handleMouseDown(position) {
246        this.raycaster.setFromCamera( position, this.camera.getPerspectiveCamera() ); 
247        let intersectsC1 = this.raycaster.intersectObjects( this.framstick1.mesh.children );
248        let intersectsC2 = this.raycaster.intersectObjects( this.framstick2.mesh.children );
249
250        if ( intersectsC1.length > 0 || intersectsC2.length > 0 ) {
251           
252            let obj = intersectsC1.length > 0 ? this.framstick1 : this.framstick2;
253
254            if ( this.SELECTED ) {
255                if (this.SELECTED == obj) {
256                    if (this.SELECTED == this.framstick1) {
257                        obj.setColor('red');
258                    } else {
259                        obj.setColor('blue');
260                    }
261                    this.control.detach();
262                    this.SELECTED = null;
263                } else {
264                    if (this.SELECTED == this.framstick1) {
265                        this.SELECTED.setColor('red');
266                    } else {
267                        this.SELECTED.setColor('blue');
268                    }
269                    this.control.detach();
270                    obj.setColor('white');   
271                    this.SELECTED = obj;     
272                    this.control.attach( obj.mesh );     
273                }
274            } else {
275                this.control.detach();
276                obj.setColor('white');
277                this.SELECTED = obj; 
278                this.control.attach( obj.mesh );
279            }
280        } else {
281            if (this.SELECTED) {
282                if (this.SELECTED == this.framstick1) {
283                    this.SELECTED.setColor('red');
284                } else {
285                    this.SELECTED.setColor('blue');
286                }
287                this.control.detach();
288                this.SELECTED = null;
289            }
290        }
291    }
292
293    handleMouseUp() {
294    }
295
296    handleMouseMove(position) {
297    }
298
299    /**
300     * Method for rendering Scene. It should be passed as drawing element in
301     * owners method.
302     *
303     * Example of usage is visible in GenoViewer Component.
304     * @returns {number} frameId of animation
305     */
306    renderScene() {
307        this.camera.getCameraControl().update();
308
309        let frameId = window.requestAnimationFrame(this.renderScene);
310        this.scene.render(this.renderer, this.camera.getPerspectiveCamera());
311        return frameId;
312    }
313
314    /**
315     * Resizes scene. Use with caution - this is performance-consuming function,
316     * so it should only be used when necessary. To perform resize, the canvas
317     * should have absolute position within parent, otherwise it
318     * will change size on its own.
319     * @param {number} width new width of canvas
320     * @param {number} height new height of canvas
321     */
322    resizeView(width, height) {
323        this.renderer.context.canvas.width = width;
324        this.renderer.context.canvas.height = height;
325        this.camera.getPerspectiveCamera().aspect = width / height;
326        this.camera.getPerspectiveCamera().updateProjectionMatrix();
327        this.renderer.setSize(width, height);
328        this.renderer.setPixelRatio(window.devicePixelRatio || 1);
329    }
330
331    /**
332     * Determines whether camera should change position in order to show all body
333     * or not.
334     * @param {boolean} rezoom true if should rezoom, false otherwise
335     */
336    setRezoom(rezoom) {
337        this.rezoom = rezoom;
338        if (this.rezoom) {
339            this.camera.zoomAll(this.meshes);
340        }
341    }
342
343    /**
344     * Determines whether camera should rotate automatically.
345     * @param {boolean} autorotate true if should autorotate, false otherwise
346     */
347    setAutoRotate(autorotate) {
348        this.camera.setAutoRotate(autorotate);
349    }
350
351    /**
352     * Clears scene from meshes.
353     */
354    clearView() {
355        if (this.framstick1 != null && this.framstick2 != null) {
356            this.scene.remove(this.framstick1.mesh);
357            this.scene.remove(this.framstick2.mesh);
358        }
359        this.control.detach();
360        while (this.container.children.length > 3) {
361            this.container.removeChild(this.container.lastChild)
362        }
363
364        this.SELECTED = null;
365    }
366
367    /**
368     * Returns reference to renderer DOM Element.
369     * @returns {Element} DOM Element of Viewer.
370     */
371    getDOM() {
372        return this.renderer.domElement;
373    }
374
375    /**
376     * Highlights all parts and joints presented in selectedelements argument.
377     * @param {SelectedElements} selectedelements object of class SelectedElements defined in mappingresolver.js, determines which parts and joints should be highlighted
378     */
379    highlightElements(selectedelements) {
380        this.selectedelements = [];
381        for (let i = 0; i < selectedelements.parts.length; i++) {
382            this.parts[selectedelements.parts[i]].material.transparent = false;
383            this.parts[selectedelements.parts[i]].material.emissive.set('green');
384            this.selectedelements.push(this.parts[selectedelements.parts[i]]);
385        }
386        for (let i = 0; i < selectedelements.joints.length; i++) {
387            this.joints[selectedelements.joints[i]].material.transparent = false;
388            this.joints[selectedelements.joints[i]].material.emissive.set('green');
389            this.selectedelements.push(this.joints[selectedelements.joints[i]]);
390        }
391    }
392
393    /**
394     * Removes all highlights from objects.
395     */
396    clearHighlights() {
397        for (let i = 0; i < this.selectedelements.length; i++) {
398            if (this.selectedelements[i] && this.selectedelements[i].material) {
399                this.selectedelements[i].material.transparent = this.selectedelements[i].userData.showTransparent;
400                this.selectedelements[i].material.emissive.set('black');
401            }
402        }
403        this.selectedelements = [];
404    }
405}
406
407export default Viewer;
Note: See TracBrowser for help on using the repository browser.