source: js/standard_expdef_demo/js/external/framsjs/visualization/transformations.js @ 880

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

A simple javascript demo/animation of the basic behavior of "standard.expdef" experiment definition in Framsticks

File size: 12.6 KB
Line 
1class ShapeTransformations {
2
3    /**
4     * Basic constructor for Transformations class.
5     * Initializes fields partShapes and jointShapes with getPartShapes and
6     * getJointsShapes results.
7     */
8    constructor() {
9        this.partShapes = this.getPartShapes();
10        this.jointShapes = this.getJointShapes();
11    }
12
13    /**
14     * Getter for Part shapes informations.
15     * @returns {ShapeInfo[]} basic info about shapes of parts
16     */
17    getPartShapes() {
18        let shapes = [];
19        shapes["SHAPE_BALL_AND_STICK"] = { name: "Ball & Stick", value: Module.Part["SHAPE_BALL_AND_STICK"] };
20        shapes["SHAPE_ELLIPSOID"] = { name: "Elipsoid", value: Module.Part["SHAPE_ELLIPSOID"] };
21        shapes["SHAPE_CUBOID"] = { name: "Cuboid", value: Module.Part["SHAPE_CUBOID"] };
22        shapes["SHAPE_CYLINDER"] = { name: "Cylinder", value: Module.Part["SHAPE_CYLINDER"] };
23        return shapes;
24    }
25
26    /**
27     * Getter for Joint shapes informations.
28     * @returns {ShapeInfo[]} basic info about shapes of joints
29     */
30    getJointShapes() {
31        let shapes = [];
32        shapes["SHAPE_BALL_AND_STICK"] = { name: "Ball & Stick", value: Module.Joint["SHAPE_BALL_AND_STICK"] };
33        shapes["SHAPE_FIXED"] = { name: "Fixed", value: Module.Joint["SHAPE_FIXED"] };
34        return shapes;
35    }
36
37    /**
38     * Recursive method for checking which elements are affected by transformations of object.
39     * This is internal method that should not be called.
40     * @param {BodyElement} bodyElement bodyelement, for which connected elements are checked for affection
41     * @param {object} result {partElements: PartMesh[], jointElements: JointMesh[]} that is result of recursive call
42     */
43    getAffectedElements_r(bodyElement, result) {
44        if (bodyElement.marked) {
45            return;
46        }
47
48        if (bodyElement.type == "p") {
49
50            bodyElement.marked = true;
51            result.partElements.push(bodyElement);
52
53            for (let i = 0; i < bodyElement.connectedJoints.length; ++i) {
54                let joint = bodyElement.connectedJoints[i];
55                if (!joint.marked) {
56                    joint.marked = true;
57                    result.jointElements.push(joint);
58                    if (joint.data.get_usedelta()) {
59                        this.getAffectedElements_r(joint.connectedParts[1], result);
60                    }
61                }
62            }
63
64        }
65        else if (bodyElement.type == "j") {
66            bodyElement.marked = true;
67            result.jointElements.push(bodyElement);
68
69            this.getAffectedElements_r(bodyElement.connectedParts[1], result);
70        }
71    }
72
73    /**
74     * Function that finds affected elements with function getAffectedElements_r
75     * and returns all affected parts and joints
76     * @param {BodyElement} bodyElement bodyelement, for which connected elements are checked for affection
77     * @returns {object} {partElements: PartMesh[], jointElements: JointMesh[]} that is result of recursive call
78     */
79    getAffectedElements(bodyElement) {
80        let result = {
81            partElements: [],
82            jointElements: []
83        };
84
85        this.getAffectedElements_r(bodyElement, result);
86
87        for (let j = 0; j < result.partElements.length; ++j) {
88            result.partElements[j].marked = false;
89        }
90        for (let k = 0; k < result.jointElements.length; ++k) {
91            result.jointElements[k].marked = false;
92        }
93        return result;
94    }
95
96    /**
97     * Helper for clamping colors from Framsticks-SDK to 0-1 values
98     * @param {number} value density of color
99     * @returns {number} clamped color
100     */
101    calcColorComponent(value) {
102        return THREE.Math.clamp(value, 0, 1);
103    }
104
105    /**
106     * Applies color for a given element
107     * @param {BodyElement} bodyElement body element to color
108     * @param {number} component which component should be changed
109     * @param {number} value new value of component
110     */
111    applyColor(bodyElement, component, value) {
112        bodyElement.mesh.material.color[component] = this.calcColorComponent(value);
113    }
114
115    /**
116     * Creates new phong material for Mesh
117     * @param {number} r color red
118     * @param {number} g color green
119     * @param {number} b color blue
120     * @returns {MeshPhongMaterial} new material for Mesh
121     */
122    getNewMaterial(r, g, b) {
123        let color = new THREE.Color(
124            this.calcColorComponent(r),
125            this.calcColorComponent(g),
126            this.calcColorComponent(b));
127        return new THREE.MeshPhongMaterial({ color: color });
128    }
129
130    /**
131     * Returns THREE.js object representing rotation matrix based on XYZ axis
132     * rotation angles.
133     * @param {number} rotAngleX angle of rotation for X axis
134     * @param {number} rotAngleY angle of rotation for Y axis
135     * @param {number} rotAngleZ angle of rotation for Z axis
136     * @returns {THREE.Euler} rotation matrix to be applied for model
137     */
138    getPartRotation(rotAngleX, rotAngleY, rotAngleZ) {
139        return new THREE.Euler(rotAngleX, -rotAngleY, rotAngleZ, "ZYX");
140    }
141
142    /**
143     * Creates rotation matrix for cylinder Part
144     * @param {number} rotAngleX rotate coordinate
145     * @param {number} rotAngleY rotate coordinate
146     * @param {number} rotAngleZ rotate coordinate
147     * @returns {Matrix4} rotation matrix for cylinder Part
148     */
149    getCylinderPartRotationMatrix(rotAngleX, rotAngleY, rotAngleZ) {
150        let rot = this.getPartRotation(rotAngleX, rotAngleY, rotAngleZ);
151
152        let m = new THREE.Matrix4();
153        m.makeRotationZ(THREE.Math.degToRad(90));
154
155        let m2 = new THREE.Matrix4();
156        m2.makeRotationFromEuler(rot);
157        m2.multiply(m);
158        return m2;
159    }
160
161    /**
162     * Applies rotation on a single part
163     * @param {BodyElement} bodyElement element on which rotation will be performed
164     */
165    applySinglePartRotation(bodyElement) {
166        let shape = bodyElement.data.get_shape();
167        let rot = bodyElement.data.get_o().getAngles();
168
169        if (this.partShapes['SHAPE_CYLINDER'].value == shape) {
170            let m = this.getCylinderPartRotationMatrix(
171                rot.get_x(),
172                rot.get_y(),
173                rot.get_z());
174            bodyElement.mesh.rotation.setFromRotationMatrix(m);
175        }
176        else {
177            let r = this.getPartRotation(rot.get_x(), rot.get_y(), rot.get_z());
178            bodyElement.mesh.rotation.copy(r);
179        }
180    }
181
182    /**
183     * Applies rotation on a bodyElement part and all other affected elements.
184     * @param {BodyElement} bodyElement element on which rotation will be performed
185     */
186    applyPartRotation(bodyElement) {
187
188        let affectedElements = this.getAffectedElements(bodyElement);
189
190        if (affectedElements.partElements.length > 1) {
191            for (let i = 0; i < affectedElements.partElements.length; ++i) {
192                let part = affectedElements.partElements[i];
193                part.mesh.position.x = part.data.get_p().get_x();
194                part.mesh.position.y = part.data.get_p().get_y();
195                part.mesh.position.z = part.data.get_p().get_z();
196                this.applySinglePartRotation(part);
197            }
198            let jointMeshFactory = new JointMeshFactory(this.jointShapes);
199            for (let j = 0; j < affectedElements.jointElements.length; ++j) {
200                let newJointMesh = jointMeshFactory.create(affectedElements.jointElements[j].data);
201                this.changeBodyElementMesh(affectedElements.jointElements[j], newJointMesh);
202            }
203        } else {
204            this.applySinglePartRotation(bodyElement);
205        }
206
207    }
208
209    /**
210     * Redefines all affected joints due to part changes
211     * @param {BodyElement} bodyElement starting element
212     */
213    applyJointDeltaChange(bodyElement) {
214
215        let affectedElements = this.getAffectedElements(bodyElement);
216
217        for (let i = 0; i < affectedElements.partElements.length; ++i) {
218            let part = affectedElements.partElements[i];
219            part.mesh.position.x = part.data.get_p().get_x();
220            part.mesh.position.y = part.data.get_p().get_y();
221            part.mesh.position.z = part.data.get_p().get_z();
222            this.applySinglePartRotation(part);
223        }
224        let jointMeshFactory = new JointMeshFactory(this.jointShapes);
225        for (let j = 0; j < affectedElements.jointElements.length; ++j) {
226            let newJointMesh = jointMeshFactory.create(affectedElements.jointElements[j].data);
227            this.changeBodyElementMesh(affectedElements.jointElements[j], newJointMesh);
228        }
229
230    }
231
232    /**
233     * Applies scale to a part
234     * @param {BodyElement} bodyElement element to be scaled
235     */
236    applyPartScale(bodyElement) {
237        let part = bodyElement.data;
238        let shape = part.get_shape();
239        if (this.partShapes['SHAPE_CYLINDER'].value == shape) {
240            bodyElement.mesh.scale.set(
241                part.get_scale().get_y(),
242                part.get_scale().get_x(),
243                part.get_scale().get_z());
244        } else if (this.partShapes['SHAPE_BALL_AND_STICK'].value != shape) {
245            bodyElement.mesh.scale.set(
246                part.get_scale().get_x(),
247                part.get_scale().get_y(),
248                part.get_scale().get_z());
249        }
250    }
251
252    /**
253     * Replaces old mesh of an element with a new mesh
254     * @param {BodyElement} element Element to be changed
255     * @param {Mesh} newMesh New mesh for element
256     */
257    changeBodyElementMesh(element, newMesh) {
258        let scene = element.mesh.parent;
259        let oldMesh = element.mesh;
260
261        let userData = oldMesh.userData;
262        userData.showTransparent = newMesh.userData.showTransparent;
263        userData.mesh = newMesh.userData.mesh;
264        newMesh.userData = userData;
265
266        scene.remove(oldMesh);
267        scene.add(newMesh);
268
269        oldMesh.geometry.dispose();
270        oldMesh.geometry = null;
271        oldMesh.material.dispose();
272        oldMesh.material = null;
273    }
274
275    /**
276     * Changes Part position.
277     * @param {BodyElement} bodyElement element to be changed
278     * @param {string} axis which axis should be changed
279     */
280    applyPartPosition(bodyElement, axis) {
281        let jointMeshFactory = new JointMeshFactory(this.jointShapes);
282
283        let affectedElements = this.getAffectedElements(bodyElement);
284        for (let i = 0; i < affectedElements.partElements.length; ++i) {
285            affectedElements.partElements[i].mesh.position[axis] =
286                affectedElements.partElements[i].data.get_p()["get_" + axis]();
287        }
288        for (let j = 0; j < affectedElements.jointElements.length; ++j) {
289            let newJointMesh = jointMeshFactory.create(affectedElements.jointElements[j].data);
290            this.changeBodyElementMesh(affectedElements.jointElements[j], newJointMesh);
291        }
292    }
293
294    /**
295     * Updates all affected elements positions.
296     * @param {BodyElement} bodyElement beginning element
297     */
298    updateAfterPartTranslation(bodyElement) {
299        let jointMeshFactory = new JointMeshFactory(this.jointShapes);
300
301        let affectedElements = this.getAffectedElements(bodyElement);
302        for (let i = 0; i < affectedElements.partElements.length; ++i) {
303            let currentElement = affectedElements.partElements[i];
304            if (currentElement !== bodyElement) {
305                affectedElements.partElements[i].mesh.position.x =
306                    affectedElements.partElements[i].data.get_p().get_x();
307                affectedElements.partElements[i].mesh.position.y =
308                    affectedElements.partElements[i].data.get_p().get_y();
309                affectedElements.partElements[i].mesh.position.z =
310                    affectedElements.partElements[i].data.get_p().get_z();
311            }
312        }
313        for (let j = 0; j < affectedElements.jointElements.length; ++j) {
314            let newJointMesh = jointMeshFactory.create(affectedElements.jointElements[j].data);
315            this.changeBodyElementMesh(affectedElements.jointElements[j], newJointMesh);
316        }
317    }
318
319    /**
320     * Creates Part shape and removes old shape.
321     * @param {PartMesh} bodyElement part to be applied
322     */
323    applyPartShape(bodyElement) {
324        let partMeshFactory = new PartMeshFactory(this.partShapes);
325        let newPartMesh = partMeshFactory.create(bodyElement.data);
326        this.changeBodyElementMesh(bodyElement, newPartMesh);
327    }
328
329    /**
330     * Creates Joint shape and removes old shape.
331     * @param {JointMesh} bodyElement joint to be applied
332     */
333    applyJointShape(bodyElement) {
334        let jointMeshFactory = new JointMeshFactory(this.jointShapes);
335        let newJointMesh = jointMeshFactory.create(bodyElement.data);
336        this.changeBodyElementMesh(bodyElement, newJointMesh);
337    }
338}
Note: See TracBrowser for help on using the repository browser.