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

Last change on this file since 944 was 911, checked in by Maciej Komosinski, 5 years ago

Added the actual functionality of the app in place of previous draft

File size: 13.4 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        this.framstick1.mesh.position.set( 0, -2, 0 );
163        this.framstick2.mesh.position.set( 0, 2, 0 );
164
165        this.framstick1.setPositions();
166        this.framstick2.setPositions();
167
168        this.framstick1.setColor('red');
169        this.framstick2.setColor('blue');
170
171        this.camera.zoomAll(this.meshes);
172        this.addText();
173    }
174
175    /**
176     * Draw lines between selected parts of both framsticks
177     * @param {Array} selected1 array of values that user selected in fitview for first genotype
178     * @param {Array} selected2 array of values that user selected in fitview for second genotype
179     */
180    addLines(selected1, selected2) {
181        for (let i = 0; i < this.lines.length; i++) {
182            this.scene.remove(this.lines[i]);
183        }
184
185        this.lines = [];
186
187        let pairs = (selected1.length + selected2.length) / 2;
188
189        for (let i = 0; i < pairs; i++) {
190            if (selected1[i] != ' ' && selected2[i] != ' ') {
191                let index1 = parseInt(selected1[i]) - 1;
192                let index2 = selected2[i].charCodeAt(0) - 65;
193
194                let tempV1 = new THREE.Vector3().copy(this.framstick1.positions[index1]).applyEuler(this.framstick1.mesh.rotation).add(this.framstick1.mesh.position);
195                let tempV2 = new THREE.Vector3().copy(this.framstick2.positions[index2]).applyEuler(this.framstick2.mesh.rotation).add(this.framstick2.mesh.position);
196
197                let geometry = new THREE.Geometry();
198                geometry.vertices.push( tempV1 );
199                geometry.vertices.push( tempV2 );
200
201                let line = new THREE.Line( geometry, this.lineMaterial );
202                this.lines.push(line);
203            }
204        }
205
206        for (let i = 0; i < this.lines.length; i++) {
207            this.scene.add(this.lines[i]);
208        }
209    }
210
211    addText() {
212
213        while (this.container.children.length > 3){
214            this.container.removeChild(this.container.lastChild)
215        }
216
217        this.textLabels = [];
218
219        if (this.framstick1 != null && this.framstick2 != null) {
220            this.framstick1.addText(1);
221            this.framstick2.addText(2);
222        }
223
224    }
225
226    /**
227     * Allow to change mode of TransformControls
228     * @param {string} mode Selected mode of transformcontrols
229     */
230    setMode(mode) {
231        this.control.setMode(mode);
232    }
233
234    /**
235     * Selects object from scene according to current mouse position. The position
236     * of mouse should be transformed to screen space, which has values from -1 to 1
237     * for each dimension.
238     * @param {{x: number, y: number}} position position of mouse in canvas normalized to [-1,1]x[-1,1] space
239     */
240    handleMouseDown(position) {
241        this.raycaster.setFromCamera( position, this.camera.getPerspectiveCamera() ); 
242        let intersectsC1 = this.raycaster.intersectObjects( this.framstick1.mesh.children );
243        let intersectsC2 = this.raycaster.intersectObjects( this.framstick2.mesh.children );
244
245        if ( intersectsC1.length > 0 || intersectsC2.length > 0 ) {
246           
247            let obj = intersectsC1.length > 0 ? this.framstick1 : this.framstick2;
248
249            if ( this.SELECTED ) {
250                if (this.SELECTED == obj) {
251                    if (this.SELECTED == this.framstick1) {
252                        obj.setColor('red');
253                    } else {
254                        obj.setColor('blue');
255                    }
256                    this.control.detach();
257                    this.SELECTED = null;
258                } else {
259                    if (this.SELECTED == this.framstick1) {
260                        this.SELECTED.setColor('red');
261                    } else {
262                        this.SELECTED.setColor('blue');
263                    }
264                    this.control.detach();
265                    obj.setColor('white');   
266                    this.SELECTED = obj;     
267                    this.control.attach( obj.mesh );     
268                }
269            } else {
270                this.control.detach();
271                obj.setColor('white');
272                this.SELECTED = obj; 
273                this.control.attach( obj.mesh );
274            }
275        } else {
276            if (this.SELECTED) {
277                if (this.SELECTED == this.framstick1) {
278                    this.SELECTED.setColor('red');
279                } else {
280                    this.SELECTED.setColor('blue');
281                }
282                this.control.detach();
283                this.SELECTED = null;
284            }
285        }
286    }
287
288    handleMouseUp() {
289    }
290
291    handleMouseMove(position) {
292    }
293
294    /**
295     * Method for rendering Scene. It should be passed as drawing element in
296     * owners method.
297     *
298     * Example of usage is visible in GenoViewer Component.
299     * @returns {number} frameId of animation
300     */
301    renderScene() {
302        this.camera.getCameraControl().update();
303
304        let frameId = window.requestAnimationFrame(this.renderScene);
305        this.scene.render(this.renderer, this.camera.getPerspectiveCamera());
306        return frameId;
307    }
308
309    /**
310     * Resizes scene. Use with caution - this is performance-consuming function,
311     * so it should only be used when necessary. To perform resize, the canvas
312     * should have absolute position within parent, otherwise it
313     * will change size on its own.
314     * @param {number} width new width of canvas
315     * @param {number} height new height of canvas
316     */
317    resizeView(width, height) {
318        this.renderer.context.canvas.width = width;
319        this.renderer.context.canvas.height = height;
320        this.camera.getPerspectiveCamera().aspect = width / height;
321        this.camera.getPerspectiveCamera().updateProjectionMatrix();
322        this.renderer.setSize(width, height);
323        this.renderer.setPixelRatio(window.devicePixelRatio || 1);
324    }
325
326    /**
327     * Determines whether camera should change position in order to show all body
328     * or not.
329     * @param {boolean} rezoom true if should rezoom, false otherwise
330     */
331    setRezoom(rezoom) {
332        this.rezoom = rezoom;
333        if (this.rezoom) {
334            this.camera.zoomAll(this.meshes);
335        }
336    }
337
338    /**
339     * Determines whether camera should rotate automatically.
340     * @param {boolean} autorotate true if should autorotate, false otherwise
341     */
342    setAutoRotate(autorotate) {
343        this.camera.setAutoRotate(autorotate);
344    }
345
346    /**
347     * Clears scene from meshes.
348     */
349    clearView() {
350        if (this.framstick1 != null && this.framstick2 != null) {
351            this.scene.remove(this.framstick1.mesh);
352            this.scene.remove(this.framstick2.mesh);
353        }
354        this.control.detach();
355        while (this.container.children.length > 3) {
356            this.container.removeChild(this.container.lastChild)
357        }
358
359        this.SELECTED = null;
360    }
361
362    /**
363     * Returns reference to renderer DOM Element.
364     * @returns {Element} DOM Element of Viewer.
365     */
366    getDOM() {
367        return this.renderer.domElement;
368    }
369
370    /**
371     * Highlights all parts and joints presented in selectedelements argument.
372     * @param {SelectedElements} selectedelements object of class SelectedElements defined in mappingresolver.js, determines which parts and joints should be highlighted
373     */
374    highlightElements(selectedelements) {
375        this.selectedelements = [];
376        for (let i = 0; i < selectedelements.parts.length; i++) {
377            this.parts[selectedelements.parts[i]].material.transparent = false;
378            this.parts[selectedelements.parts[i]].material.emissive.set('green');
379            this.selectedelements.push(this.parts[selectedelements.parts[i]]);
380        }
381        for (let i = 0; i < selectedelements.joints.length; i++) {
382            this.joints[selectedelements.joints[i]].material.transparent = false;
383            this.joints[selectedelements.joints[i]].material.emissive.set('green');
384            this.selectedelements.push(this.joints[selectedelements.joints[i]]);
385        }
386    }
387
388    /**
389     * Removes all highlights from objects.
390     */
391    clearHighlights() {
392        for (let i = 0; i < this.selectedelements.length; i++) {
393            if (this.selectedelements[i] && this.selectedelements[i].material) {
394                this.selectedelements[i].material.transparent = this.selectedelements[i].userData.showTransparent;
395                this.selectedelements[i].material.emissive.set('black');
396            }
397        }
398        this.selectedelements = [];
399    }
400}
401
402export default Viewer;
Note: See TracBrowser for help on using the repository browser.