diff --git a/README.md b/README.md
index 267c909..765d867 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-## Web Environment For Atomistic Structure (WEAS)
+## Web Environment For Atomic Structure (WEAS)
[![npm version](https://img.shields.io/npm/v/weas.svg?style=flat-square)](https://www.npmjs.com/package/weas)
[![Docs status](https://readthedocs.org/projects/weas/badge)](http://weas.readthedocs.io/)
[![Unit test](https://github.com/superstar54/weas/actions/workflows/ci.yml/badge.svg)](https://github.com/superstar54/weas/actions/workflows/ci.yml)
-The WEAS package is a JavaScript library designed to visualize and edit atomistic structures (molecule, crystal, nanoparticle) in the web environments.
+The WEAS package is a JavaScript library designed to visualize and edit atomic structures (molecule, crystal, nanoparticle) in the web environments.
Features:
@@ -60,6 +60,12 @@ npm run build
npx playwright test
```
+Run the test with the title
+
+```
+npx playwright test -g "Animation"
+```
+
If the snapshots need to be updated:
```
diff --git a/demo/demo.js b/demo/demo.js
index 96f77bc..66814e6 100644
--- a/demo/demo.js
+++ b/demo/demo.js
@@ -1,5 +1,7 @@
import { WEAS, Atoms, Species, parseXYZ, parseCIF, parseCube } from "../src/index.js"; // Adjust the path as necessary
+import * as THREE from "three";
+window.THREE = THREE;
window.WEAS = WEAS;
window.Atoms = Atoms;
window.Species = Species;
diff --git a/examples/h2o.html b/examples/h2o.html
index 4e539df..9635a9e 100644
--- a/examples/h2o.html
+++ b/examples/h2o.html
@@ -2,6 +2,7 @@
+
WEAS Molecule
diff --git a/package-lock.json b/package-lock.json
index f1ed756..55338ec 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "weas",
- "version": "0.0.8-b",
+ "version": "0.1.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "weas",
- "version": "0.0.8-b",
+ "version": "0.1.1",
"license": "MIT",
"dependencies": {
"dat.gui": "^0.7.9",
diff --git a/package.json b/package.json
index 42baf73..9bf953b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "weas",
- "version": "0.1.0",
+ "version": "0.1.1",
"description": "WEAS (Web Environment for Atomic Structures) is a JavaScript library to visualize and manipulate the atomistic structures directly in the web browser",
"main": "src/index.js",
"scripts": {
diff --git a/src/atoms/AtomsViewer.js b/src/atoms/AtomsViewer.js
index d55faa7..e2e9f50 100644
--- a/src/atoms/AtomsViewer.js
+++ b/src/atoms/AtomsViewer.js
@@ -445,8 +445,10 @@ class AtomsViewer {
}
this.atomColors = getAtomColors(this.atoms, this.colorBy, { colorType: this.colorType, colorRamp: this._colorRamp });
this.drawBalls();
- this.bondManager.drawBonds();
- this.polyhedraManager.drawPolyhedras();
+ const bondMesh = this.bondManager.drawBonds();
+ this.atomsMesh.add(bondMesh);
+ const polyhedraMesh = this.polyhedraManager.drawPolyhedras();
+ this.atomsMesh.add(polyhedraMesh);
this.isosurfaceManager.drawIsosurfaces();
if (this.showVectorField) {
this.VFManager.drawVectorFields();
@@ -459,6 +461,7 @@ class AtomsViewer {
drawBalls() {
// draw atoms
this.atomsMesh = drawAtoms({ scene: this.tjs.scene, atoms: this.atoms, atomScales: this.atomScales, colors: this.atomColors, radiusType: this.radiusType, materialType: this._materialType });
+ this.tjs.scene.add(this.atomsMesh);
// atoms to be drawn, boundary atoms, and the bonded atoms
// merge the boundaryList and the bondedAtoms
this.imageAtomsList = this.bondedAtoms["atoms"].concat(this.boundaryList);
@@ -473,7 +476,16 @@ class AtomsViewer {
atomScales[i] = this.atomScales[this.imageAtomsList[i][0]];
}
const atomColors = getAtomColors(imageAtomsList, this.colorBy, { colorType: this.colorType, defaultColor: "#0xffffff", colorRamp: this._colorRamp });
- this.boundaryAtomsMesh = drawAtoms({ scene: this.tjs.scene, atoms: imageAtomsList, atomScales: atomScales, colors: atomColors, radiusType: this.radiusType, materialType: this._materialType });
+ this.boundaryAtomsMesh = drawAtoms({
+ scene: this.tjs.scene,
+ atoms: imageAtomsList,
+ atomScales: atomScales,
+ colors: atomColors,
+ radiusType: this.radiusType,
+ materialType: this._materialType,
+ data_type: "boundary",
+ });
+ this.atomsMesh.add(this.boundaryAtomsMesh);
}
}
@@ -482,8 +494,16 @@ class AtomsViewer {
const atomScales = new Array(this.atoms.getAtomsCount()).fill(0);
// use yellow color to highlight the selected atoms
const atomColors = new Array(this.atoms.getAtomsCount()).fill(new THREE.Color(0xffff00));
- this.highlightAtomsMesh = drawAtoms({ scene: this.tjs.scene, atoms: this.atoms, atomScales: atomScales, colors: atomColors, radiusType: this.radiusType, materialType: "Basic" });
- this.tjs.scene.add(this.highlightAtomsMesh);
+ this.highlightAtomsMesh = drawAtoms({
+ scene: this.tjs.scene,
+ atoms: this.atoms,
+ atomScales: atomScales,
+ colors: atomColors,
+ radiusType: this.radiusType,
+ materialType: "Basic",
+ data_type: "highlight",
+ });
+ this.atomsMesh.add(this.highlightAtomsMesh);
this.highlightAtomsMesh.material.opacity = 0.6;
this.updateHighlightAtomsMesh(this.selectedAtomsIndices);
}
diff --git a/src/atoms/draw_atoms.js b/src/atoms/draw_atoms.js
index accee9e..599b385 100644
--- a/src/atoms/draw_atoms.js
+++ b/src/atoms/draw_atoms.js
@@ -4,7 +4,7 @@ import { materials } from "../tools/materials.js";
const Radii = { Covalent: covalentRadii, VDW: vdwRadii };
-export function drawAtoms({ scene, atoms, atomScales, colors, radiusType = "Covalent", materialType = "Standard" }) {
+export function drawAtoms({ atoms, atomScales, colors, radiusType = "Covalent", materialType = "Standard", data_type = "atom" }) {
console.time("drawAtoms Time");
// console.log("atomScales: ", atomScales);
console.log("Draw Atoms: ", +atoms.symbols.length, " atoms");
@@ -45,7 +45,7 @@ export function drawAtoms({ scene, atoms, atomScales, colors, radiusType = "Cova
// Set color
instancedMesh.setColorAt(globalIndex, colors[globalIndex]);
});
- instancedMesh.userData.type = "atom";
+ instancedMesh.userData.type = data_type;
instancedMesh.userData.uuid = atoms.uuid;
// the default objectMode for atoms is "edit"
instancedMesh.userData.objectMode = "edit";
@@ -57,7 +57,6 @@ export function drawAtoms({ scene, atoms, atomScales, colors, radiusType = "Cova
instancedMesh.instanceColor.needsUpdate = true;
}
- scene.add(instancedMesh);
console.timeEnd("drawAtoms Time");
return instancedMesh;
}
diff --git a/src/atoms/plugins/bond.js b/src/atoms/plugins/bond.js
index c71a34f..ce710fd 100644
--- a/src/atoms/plugins/bond.js
+++ b/src/atoms/plugins/bond.js
@@ -130,7 +130,7 @@ export class BondManager {
atomColors = this.viewer.atomColors;
}
this.bondMesh = drawStick(this.viewer.atoms, this.bondList, this.buildBondDict(), this.viewer.bondRadius, this.viewer._materialType, atomColors);
- this.scene.add(this.bondMesh);
+ return this.bondMesh;
}
updateBondMesh(atomIndex = null, atoms = null) {
diff --git a/src/atoms/plugins/isosurface.js b/src/atoms/plugins/isosurface.js
index 7b7742d..a490c13 100644
--- a/src/atoms/plugins/isosurface.js
+++ b/src/atoms/plugins/isosurface.js
@@ -94,6 +94,7 @@ export class Isosurface {
drawIsosurfaces() {
/* Draw isosurfaces */
if (this.volumetricData === null) {
+ console.log("No volumetric data is set");
return;
}
console.log("drawIsosurfaces");
diff --git a/src/atoms/plugins/polyhedra.js b/src/atoms/plugins/polyhedra.js
index 1a72518..9fac6e7 100644
--- a/src/atoms/plugins/polyhedra.js
+++ b/src/atoms/plugins/polyhedra.js
@@ -27,7 +27,8 @@ export class PolyhedraManager {
this.viewer = viewer;
this.scene = this.viewer.tjs.scene;
this.settings = [];
- this.meshes = [];
+ // create a group to store the polyhedra meshes
+ this.meshes = new THREE.Group();
this.init();
}
@@ -81,9 +82,7 @@ export class PolyhedraManager {
clearMeshes() {
/* Remove highlighted atom meshes from the selectedAtomsMesh group */
- this.meshes.forEach((mesh) => {
- clearObject(this.scene, mesh);
- });
+ clearObject(this.scene, this.meshes);
}
drawPolyhedras() {
@@ -92,10 +91,11 @@ export class PolyhedraManager {
if (this.viewer.debug) {
console.log("polyhedras: ", polyhedras);
}
- this.meshes = drawPolyhedras(this.viewer.atoms, polyhedras, this.viewer.bondManager.bondList, this.viewer._colorType, this.viewer._materialType);
- this.meshes.forEach((mesh) => {
- this.scene.add(mesh);
+ const meshes = drawPolyhedras(this.viewer.atoms, polyhedras, this.viewer.bondManager.bondList, this.viewer._colorType, this.viewer._materialType);
+ meshes.forEach((mesh) => {
+ this.meshes.add(mesh);
});
+ return this.meshes;
}
updatePolyhedraMesh(atomIndex = null, atoms = null) {
diff --git a/src/controls/TransformControls.js b/src/controls/TransformControls.js
index c298323..20a6b1e 100644
--- a/src/controls/TransformControls.js
+++ b/src/controls/TransformControls.js
@@ -27,7 +27,6 @@ export class TransformControls {
this.centroidNDC = new THREE.Vector2();
this.initialAtomPositions = new Map(); // To store initial positions of selected atoms
this.initialObjectState = new Map(); // To store initial state of selected objects
- this.viewerRect = this.tjs.containerElement.getBoundingClientRect();
// Get the camera's forward direction (negative z-axis in world space)
this.cameraDirection = new THREE.Vector3(0, 0, -1);
}
@@ -43,6 +42,7 @@ export class TransformControls {
enterMode(mode, mousePosition) {
this.mode = mode;
console.log("Enter mode: ", this.mode);
+ this.cameraDirection = new THREE.Vector3(0, 0, -1);
this.cameraDirection.applyQuaternion(this.tjs.camera.quaternion);
if (this.mode === "translate") {
// Get the camera's forward direction (negative z-axis in world space)
@@ -74,17 +74,15 @@ export class TransformControls {
// Create a translate operation
if (this.mode === "translate") {
const translateVector = this.getTranslateVector(this.eventHandler.currentMousePosition, this.initialMousePosition);
- console.log("Translate vector: ", translateVector);
- const translateOperation = new TranslateOperation(this.weas, translateVector);
+ const translateOperation = new TranslateOperation({ weas: this.weas, vector: translateVector });
this.weas.ops.execute(translateOperation, false);
} else if (this.mode === "rotate") {
const rotationAngle = this.getRotationAngle(this.eventHandler.currentMousePosition, this.initialMousePosition);
- const rotateOperation = new RotateOperation(this.weas, this.cameraDirection, rotationAngle);
+ const rotateOperation = new RotateOperation({ weas: this.weas, axis: this.cameraDirection, angle: rotationAngle });
this.weas.ops.execute(rotateOperation, false);
} else if (this.mode === "scale") {
const scaleVector = this.getScaleVector(this.eventHandler.currentMousePosition, this.initialMousePosition);
- console.log("Scale vector: ", scaleVector);
- const scaleOperation = new ScaleOperation(this.weas, scaleVector);
+ const scaleOperation = new ScaleOperation({ weas: this.weas, scale: scaleVector });
this.weas.ops.execute(scaleOperation, false);
} else {
console.log("Invalid mode");
@@ -175,9 +173,15 @@ export class TransformControls {
this.weas.objectManager.translateSelectedObjects(translateVector);
}
+ getNDC(mousePosition) {
+ return new THREE.Vector2(((mousePosition.x - this.tjs.viewerRect.left) / this.tjs.viewerRect.width) * 2 - 1, -((mousePosition.y - this.tjs.viewerRect.top) / this.tjs.viewerRect.height) * 2 + 1);
+ }
+
getTranslateVector(currentMousePosition, previousMousePosition) {
- const currentWorldPosition = getWorldPositionFromScreen(currentMousePosition.x, currentMousePosition.y, this.tjs.camera, this.translatePlane);
- const previousWorldPosition = getWorldPositionFromScreen(previousMousePosition.x, previousMousePosition.y, this.tjs.camera, this.translatePlane);
+ const newNDC = this.getNDC(currentMousePosition);
+ const currentWorldPosition = getWorldPositionFromScreen(this.tjs.camera, newNDC, this.translatePlane);
+ const initialNDC = this.getNDC(previousMousePosition);
+ const previousWorldPosition = getWorldPositionFromScreen(this.tjs.camera, initialNDC, this.translatePlane);
return currentWorldPosition.sub(previousWorldPosition);
}
@@ -200,15 +204,8 @@ export class TransformControls {
}
getScaleVector(currentMousePosition, previousMousePosition) {
- const initialNDC = new THREE.Vector2(
- ((previousMousePosition.x - this.viewerRect.left) / this.viewerRect.width) * 2 - 1,
- -((previousMousePosition.y - this.viewerRect.top) / this.viewerRect.height) * 2 + 1,
- );
-
- const newNDC = new THREE.Vector2(
- ((currentMousePosition.x - this.viewerRect.left) / this.viewerRect.width) * 2 - 1,
- -((currentMousePosition.y - this.viewerRect.top) / this.viewerRect.height) * 2 + 1,
- );
+ const initialNDC = this.getNDC(previousMousePosition);
+ const newNDC = this.getNDC(currentMousePosition);
if (initialNDC.equals(newNDC)) {
return; // Skip further processing
}
@@ -221,15 +218,8 @@ export class TransformControls {
}
getRotationAngle(currentMousePosition, previousMousePosition) {
- const initialNDC = new THREE.Vector2(
- ((previousMousePosition.x - this.viewerRect.left) / this.viewerRect.width) * 2 - 1,
- -((previousMousePosition.y - this.viewerRect.top) / this.viewerRect.height) * 2 + 1,
- );
-
- const newNDC = new THREE.Vector2(
- ((currentMousePosition.x - this.viewerRect.left) / this.viewerRect.width) * 2 - 1,
- -((currentMousePosition.y - this.viewerRect.top) / this.viewerRect.height) * 2 + 1,
- );
+ const initialNDC = this.getNDC(previousMousePosition);
+ const newNDC = this.getNDC(currentMousePosition);
if (initialNDC.equals(newNDC)) {
console.log("No mouse movement detected, skipping rotation.");
return; // Skip further processing
diff --git a/src/core/Camera.js b/src/core/Camera.js
new file mode 100644
index 0000000..eb8c9af
--- /dev/null
+++ b/src/core/Camera.js
@@ -0,0 +1,39 @@
+import * as THREE from "three";
+
+export class OrthographicCamera extends THREE.OrthographicCamera {
+ constructor(left, right, top, bottom, near, far, tjs = null) {
+ super(left, right, top, bottom, near, far);
+ this.tjs = tjs;
+ }
+
+ // Custom method to update zoom
+ updateZoom(value) {
+ if (this.zoom !== value) {
+ this.zoom = value;
+ this.updateProjectionMatrix(); // Required to apply the zoom change
+ this.dispatchObjectEvent({
+ data: value,
+ action: "zoom",
+ catalog: "camera",
+ });
+ }
+ }
+
+ // Custom method to update position
+ updatePosition(x, y, z) {
+ const newPos = new THREE.Vector3(x, y, z);
+ if (!this.position.equals(newPos)) {
+ this.position.copy(newPos);
+ this.dispatchObjectEvent({
+ data: [x, y, z],
+ action: "position",
+ catalog: "camera",
+ });
+ }
+ }
+
+ dispatchObjectEvent(data) {
+ const event = new CustomEvent("weas", { detail: data });
+ this.tjs.containerElement.dispatchEvent(event);
+ }
+}
diff --git a/src/core/ObjectManager.js b/src/core/ObjectManager.js
index df4f195..b595ddb 100644
--- a/src/core/ObjectManager.js
+++ b/src/core/ObjectManager.js
@@ -5,33 +5,42 @@ export class ObjectManager {
constructor(weas) {
this.weas = weas;
this.selectionManager = weas.selectionManager;
- this.sceneManager = weas.tjs.sceneManager;
+ this.scene = weas.tjs.scene;
}
- translateSelectedObjects(translateVector) {
- this.selectionManager.selectedObjects.forEach((object) => {
+ translateSelectedObjects(translateVector, selectedObjects = null) {
+ if (selectedObjects === null) {
+ selectedObjects = this.selectionManager.selectedObjects;
+ }
+ selectedObjects.forEach((object) => {
const initialPosition = object.position.clone();
object.position.copy(initialPosition.add(translateVector));
});
}
- rotateSelectedObjects(rotationAxis, rotationAngle) {
+ rotateSelectedObjects(rotationAxis, rotationAngle, selectedObjects = null) {
+ if (selectedObjects === null) {
+ selectedObjects = this.selectionManager.selectedObjects;
+ }
rotationAxis = rotationAxis.normalize();
rotationAngle = THREE.MathUtils.degToRad(rotationAngle);
- this.selectionManager.selectedObjects.forEach((object) => {
+ selectedObjects.forEach((object) => {
object.rotateOnAxis(rotationAxis, -rotationAngle);
});
}
deleteSelectedObjects() {
this.selectionManager.selectedObjects.forEach((object) => {
- clearObject(this.sceneManager.scene, object);
+ clearObject(this.scene, object);
});
this.selectionManager.clearSelection();
}
- scaleSelectedObjects(scale) {
- this.selectionManager.selectedObjects.forEach((object) => {
+ scaleSelectedObjects(scale, selectedObjects = null) {
+ if (selectedObjects === null) {
+ selectedObjects = this.selectionManager.selectedObjects;
+ }
+ selectedObjects.forEach((object) => {
object.scale.multiply(scale);
});
}
@@ -41,7 +50,7 @@ export class ObjectManager {
this.selectionManager.selectedObjects.forEach((object) => {
const clone = object.clone();
clone.position.add(new THREE.Vector3(1, 1, 1));
- this.sceneManager.scene.add(clone);
+ this.scene.add(clone);
newObjects.push(clone);
});
this.selectionManager.selectedObjects = newObjects;
diff --git a/src/core/SceneManager.js b/src/core/SceneManager.js
index 2515ee0..30378b6 100644
--- a/src/core/SceneManager.js
+++ b/src/core/SceneManager.js
@@ -3,28 +3,46 @@
import * as THREE from "three";
import { clearObjects } from "../utils.js";
-export class SceneManager {
- constructor() {
- this.scene = new THREE.Scene();
+export class WeasScene extends THREE.Scene {
+ constructor(tjs) {
+ super();
+ this.tjs = tjs;
}
+
add(object) {
- this.scene.add(object);
+ console.log("add object", object);
+ super.add(object);
+ this.dispatchObjectEvent({
+ data: object.toJSON(),
+ action: "add",
+ catalog: "object",
+ });
}
+
remove(object) {
- this.scene.remove(object);
+ console.log("remove object", object);
+ // if object is a string, find it by uuid
+ if (typeof object === "string") {
+ object = this.getObjectByProperty("uuid", object);
+ if (!object) {
+ console.warn("Object not found");
+ return;
+ }
+ }
+ super.remove(object);
+ this.dispatchObjectEvent({
+ data: object.toJSON(),
+ action: "remove",
+ catalog: "object",
+ });
}
- get children() {
- return this.scene.children;
- }
- // Call this method after updating atoms
- dispatchViewerUpdated(data) {
- // create a list of picked atoms from the selectedAtomsIndices set
- const event = new CustomEvent("viewerUpdated", { detail: data });
+
+ dispatchObjectEvent(data) {
+ const event = new CustomEvent("weas", { detail: data });
this.tjs.containerElement.dispatchEvent(event);
- console.log("Dispatch viewerUpdated");
}
clear() {
- clearObjects(this.scene);
+ clearObjects(this);
}
}
diff --git a/src/core/SelectionManager.js b/src/core/SelectionManager.js
index 7025763..9dfa269 100644
--- a/src/core/SelectionManager.js
+++ b/src/core/SelectionManager.js
@@ -21,7 +21,6 @@ export class SelectionManager {
}
init() {
- this.viewerRect = this.tjs.containerElement.getBoundingClientRect();
this.selectionBox = new SelectionBox(this.tjs.camera, this.tjs.scene);
this.helper = new SelectionHelper(this.tjs.renderers["MainRenderer"].renderer, "selectBox");
window.addEventListener("pointerdown", this.onMouseDown.bind(this), false);
@@ -42,16 +41,16 @@ export class SelectionManager {
}
onMouseDown(event) {
- let x = ((event.clientX - this.viewerRect.left) / this.viewerRect.width) * 2 - 1;
- let y = -((event.clientY - this.viewerRect.top) / this.viewerRect.height) * 2 + 1;
+ let x = ((event.clientX - this.tjs.viewerRect.left) / this.tjs.viewerRect.width) * 2 - 1;
+ let y = -((event.clientY - this.tjs.viewerRect.top) / this.tjs.viewerRect.height) * 2 + 1;
this.selectionBox.startPoint.set(x, y, 0.5);
this.oldSelectedAtomsIndices = this.weas.avr.selectedAtomsIndices;
this.oldSelectedObjects = this.selectedObjects;
}
pickSelection(event) {
- this.mouse.x = ((event.clientX - this.viewerRect.left) / this.viewerRect.width) * 2 - 1;
- this.mouse.y = -((event.clientY - this.viewerRect.top) / this.viewerRect.height) * 2 + 1;
+ this.mouse.x = ((event.clientX - this.tjs.viewerRect.left) / this.tjs.viewerRect.width) * 2 - 1;
+ this.mouse.y = -((event.clientY - this.tjs.viewerRect.top) / this.tjs.viewerRect.height) * 2 + 1;
// Update the picking ray
this.raycaster.setFromCamera(this.mouse, this.tjs.camera);
@@ -135,8 +134,8 @@ export class SelectionManager {
}
dragSelection(event) {
- let x = ((event.clientX - this.viewerRect.left) / this.viewerRect.width) * 2 - 1;
- let y = -((event.clientY - this.viewerRect.top) / this.viewerRect.height) * 2 + 1;
+ let x = ((event.clientX - this.tjs.viewerRect.left) / this.tjs.viewerRect.width) * 2 - 1;
+ let y = -((event.clientY - this.tjs.viewerRect.top) / this.tjs.viewerRect.height) * 2 + 1;
this.selectionBox.endPoint.set(x, y, 0.5);
this.selectionBox.select();
diff --git a/src/core/blendjs.js b/src/core/blendjs.js
index 4d63584..2d0259d 100644
--- a/src/core/blendjs.js
+++ b/src/core/blendjs.js
@@ -2,7 +2,8 @@
import * as THREE from "three";
import { OrbitControls } from "../three/OrbitControls.js";
import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js";
-import { SceneManager } from "./SceneManager.js";
+import { WeasScene } from "./SceneManager.js";
+import { OrthographicCamera } from "./Camera.js";
class BlendJSObject {
constructor(name, geometry, material) {
@@ -44,8 +45,7 @@ class BlendJSRenderer {
export class BlendJS {
constructor(containerElement) {
this.containerElement = containerElement;
- this.sceneManager = new SceneManager();
- this.scene = this.sceneManager.scene;
+ this.scene = new WeasScene(this);
this.objects = {};
this.materials = {};
this.meshes = {};
@@ -54,7 +54,7 @@ export class BlendJS {
this.init();
}
init() {
- this.sceneManager.scene.background = new THREE.Color(0xffffff); // Set the scene's background to white
+ this.scene.background = new THREE.Color(0xffffff); // Set the scene's background to white
// Create a renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(this.containerElement.clientWidth, this.containerElement.clientHeight);
@@ -76,13 +76,14 @@ export class BlendJS {
const frustumHalfHeight = frustumSize / 2;
const frustumHalfWidth = frustumHalfHeight * aspect;
- this.camera = new THREE.OrthographicCamera(
+ this.camera = new OrthographicCamera(
-frustumHalfWidth, // left
frustumHalfWidth, // right
frustumHalfHeight, // top
-frustumHalfHeight, // bottom
1, // near clipping plane
2000, // far clipping plane
+ this,
);
// Set initial camera position
this.camera.position.set(0, -100, 0);
@@ -90,7 +91,7 @@ export class BlendJS {
// Enable layer 1 for the camera
// this layer will be used for vertex indicators
this.camera.layers.enable(1);
- this.sceneManager.add(this.camera);
+ this.scene.add(this.camera);
// Create a light
const light = new THREE.DirectionalLight(0xffffff, 2.0);
light.position.set(50, 50, 100);
@@ -108,13 +109,14 @@ export class BlendJS {
// this.controls.enablePan = true; // This line disables panning
// this.controls.enableDamping = true; // Enable smooth camera movements
// Add event listener for window resize
+ this.viewerRect = this.containerElement.getBoundingClientRect();
window.addEventListener("resize", this.onWindowResize.bind(this), false);
}
addObject(name, geometry, material) {
const object = new BlendJSObject(name, geometry, material);
this.objects[name] = object;
- this.sceneManager.add(object.object3D);
+ this.scene.add(object.object3D);
return object;
}
@@ -134,7 +136,7 @@ export class BlendJS {
addLight(name, light) {
const lgt = new BlendJSLight(name, light);
this.lights[name] = lgt;
- this.sceneManager.add(lgt.light);
+ this.scene.add(lgt.light);
return lgt;
}
@@ -161,21 +163,23 @@ export class BlendJS {
Object.values(this.renderers).forEach((rndr) => {
rndr.renderer.setSize(this.containerElement.clientWidth, this.containerElement.clientHeight);
});
+ this.viewerRect = this.containerElement.getBoundingClientRect();
}
//
- updateCameraAndControls({ center = null, direction = [0, 0, 1], distance = null, zoom = 1 }) {
+ updateCameraAndControls({ lookAt = null, direction = [0, 0, 1], distance = null, zoom = 1 }) {
/*
Calculate the camera parameters based on the bounding box of the scene and the camera direction
- The camera to look at the center, and rotate around the center of the atoms
+ The camera to look at the lookAt, and rotate around the lookAt of the atoms.
+ Position of the camera is defined by the look_at, direction, and distance attributes.
*/
// normalize the camera direction
direction = new THREE.Vector3(...direction).normalize();
const sceneBoundingBox = this.getSceneBoundingBox();
- // center of the bounding box
- if (center === null) {
- center = sceneBoundingBox.getCenter(new THREE.Vector3());
+ // lookAt of the bounding box
+ if (lookAt === null) {
+ lookAt = sceneBoundingBox.getCenter(new THREE.Vector3());
} else {
- center = new THREE.Vector3(...center);
+ lookAt = new THREE.Vector3(...lookAt);
}
const size = calculateBoundingBox(sceneBoundingBox, direction);
@@ -198,25 +202,25 @@ export class BlendJS {
this.camera.top = cameraHeight / 2;
this.camera.bottom = -cameraHeight / 2;
- // Adjust camera position based on the center of the bounding box and the camera direction
+ // Adjust camera position based on the lookAt of the bounding box and the camera direction
if (distance === null) {
distance = size.z + padding;
}
- let cameraPosition = center.clone().add(direction.multiplyScalar(distance));
- this.camera.position.copy(cameraPosition);
+ let cameraPosition = lookAt.clone().add(direction.multiplyScalar(distance));
+ this.camera.updatePosition(cameraPosition.x, cameraPosition.y, cameraPosition.z);
- this.camera.lookAt(center);
+ this.camera.lookAt(lookAt);
this.camera.updateProjectionMatrix();
- this.camera.zoom = zoom;
- // Set the camera target to the center of the atoms
- this.controls.target.set(center.x, center.y, center.z);
+ this.camera.updateZoom(zoom);
+ // Set the camera target to the lookAt of the atoms
+ this.controls.target.set(lookAt.x, lookAt.y, lookAt.z);
}
getSceneBoundingBox() {
// Create a bounding box that will include all objects
let sceneBoundingBox = new THREE.Box3();
// For each object in the scene, expand the bounding box to include it
- this.sceneManager.scene.traverse(function (object) {
+ this.scene.traverse(function (object) {
if (object.isMesh || object.isLineSegments || object.isInstancedMesh) {
let objectBoundingBox;
// if it is a instancedMesh
@@ -251,7 +255,7 @@ export class BlendJS {
// loop through renderers to render the scene
Object.values(this.renderers).forEach((rndr) => {
- rndr.renderer.render(this.sceneManager, this.camera);
+ rndr.renderer.render(this.scene, this.camera);
});
};
@@ -269,7 +273,7 @@ export class BlendJS {
renderer.setPixelRatio(highResPixelRatio);
// Render the scene for high-res output
- renderer.render(this.sceneManager.scene, this.camera);
+ renderer.render(this.scene, this.camera);
// Get the image data URL
var imgData = renderer.domElement.toDataURL("image/png");
diff --git a/src/operation/atoms.js b/src/operation/atoms.js
index 8d326af..aaa0e7e 100644
--- a/src/operation/atoms.js
+++ b/src/operation/atoms.js
@@ -6,16 +6,16 @@ class ReplaceOperation extends BaseOperation {
static description = "Replace atoms";
static category = "Edit";
- constructor({ weas, element = "C", indices = null }) {
+ constructor({ weas, symbol = "C", indices = null }) {
super(weas);
this.indices = indices ? indices : Array.from(this.weas.avr.selectedAtomsIndices);
- this.element = element;
+ this.symbol = symbol;
// .copy() provides a fresh instance for restoration
this.initialAtoms = weas.avr.atoms.copy();
}
execute() {
- this.weas.avr.replaceSelectedAtoms(this.element, this.indices);
+ this.weas.avr.replaceSelectedAtoms(this.symbol, this.indices);
}
undo() {
@@ -23,12 +23,13 @@ class ReplaceOperation extends BaseOperation {
this.weas.avr.atoms = this.initialAtoms.copy();
}
- adjust(newElement) {
- if (!(newElement in elementAtomicNumbers)) {
+ adjust(newSymbol) {
+ // if newSymbol not in elementAtomicNumbers, and newSymbol not in this.weas.avr.atoms.species, ship the adjustment
+ if (!(newSymbol in elementAtomicNumbers || newSymbol in this.weas.avr.atoms.species)) {
return;
}
- this.element = newElement;
- this.execute(); // Re-execute with the new element
+ this.symbol = newSymbol;
+ this.execute(); // Re-execute with the new symbol
}
setupGUI(guiFolder) {
@@ -36,10 +37,10 @@ class ReplaceOperation extends BaseOperation {
renameFolder(guiFolder, "Replace");
guiFolder
- .add(this, "element", "H")
- .name("Element")
+ .add(this, "symbol", "H")
+ .name("Symbol")
.onChange((value) => {
- this.adjust(this.element);
+ this.adjust(this.symbol);
});
}
}
@@ -48,16 +49,16 @@ class AddAtomOperation extends BaseOperation {
static description = "Add atom";
static category = "Edit";
- constructor({ weas, element = "C", position = { x: 0, y: 0, z: 0 } }) {
+ constructor({ weas, symbol = "C", position = { x: 0, y: 0, z: 0 } }) {
super(weas);
// this.weas.avr.selectedAtomsIndices is a set
this.position = position;
- this.element = element;
+ this.symbol = symbol;
this.initialAtoms = weas.avr.atoms.copy();
}
execute() {
- this.weas.avr.addAtom(this.element, this.position);
+ this.weas.avr.addAtom(this.symbol, this.position);
}
undo() {
@@ -65,15 +66,15 @@ class AddAtomOperation extends BaseOperation {
this.weas.avr.atoms = this.initialAtoms.copy();
}
- adjust(newElement, newPosition) {
- // if newElement in elementAtomicNumbers, ship the adjustment
- if (!(newElement in elementAtomicNumbers)) {
+ adjust(newSymbol, newPosition) {
+ // if newSymbol not in elementAtomicNumbers, ship the adjustment
+ if (!(newSymbol in elementAtomicNumbers || newSymbol in this.weas.avr.atoms.species)) {
return;
}
this.weas.avr.atoms = this.initialAtoms.copy();
- this.element = newElement;
+ this.symbol = newSymbol;
this.position = newPosition;
- this.execute(); // Re-execute with the new element
+ this.execute(); // Re-execute with the new symbol
}
setupGUI(guiFolder) {
@@ -81,8 +82,8 @@ class AddAtomOperation extends BaseOperation {
renameFolder(guiFolder, "Add");
guiFolder
- .add(this, "element", "C")
- .name("Element")
+ .add(this, "symbol", "C")
+ .name("Symbol")
.onChange((value) => {
this.adjust(value, this.position);
});
@@ -90,19 +91,19 @@ class AddAtomOperation extends BaseOperation {
.add(this.position, "x", -10, 10)
.name("X-axis")
.onChange((value) => {
- this.adjust(this.element, { ...this.position, x: value });
+ this.adjust(this.symbol, { ...this.position, x: value });
});
guiFolder
.add(this.position, "y", -10, 10)
.name("Y-axis")
.onChange((value) => {
- this.adjust(this.element, { ...this.position, y: value });
+ this.adjust(this.symbol, { ...this.position, y: value });
});
guiFolder
.add(this.position, "z", -10, 10)
.name("Z-axis")
.onChange((value) => {
- this.adjust(this.element, { ...this.position, z: value });
+ this.adjust(this.symbol, { ...this.position, z: value });
});
}
}
@@ -111,7 +112,7 @@ class ColorByAttribute extends BaseOperation {
static description = "Color by attribute";
static category = "Color";
- constructor({ weas, attribute = "Element", color1 = "#ff0000", color2 = "#0000ff" }) {
+ constructor({ weas, attribute = "Symbol", color1 = "#ff0000", color2 = "#0000ff" }) {
super(weas);
// weas.meshPrimitive.settings is a array of objects
// deep copy it to avoid modifying the original settings
diff --git a/src/operation/mesh.js b/src/operation/mesh.js
index 6fc145b..69db225 100644
--- a/src/operation/mesh.js
+++ b/src/operation/mesh.js
@@ -12,9 +12,7 @@ class BaseMeshOperation extends BaseOperation {
execute() {
const data = vector3ToArray(this.data);
- console.log("data: ", data);
this.object = this.drawFunction(data);
- console.log("object: ", this.object);
this.weas.tjs.scene.add(this.object);
}
diff --git a/src/operation/object.js b/src/operation/object.js
index 1bf2ef9..7b7439c 100644
--- a/src/operation/object.js
+++ b/src/operation/object.js
@@ -17,13 +17,17 @@ class DeleteOperation extends BaseOperation {
}
execute() {
- this.weas.avr.deleteSelectedAtoms(this.indices);
+ if (this.indices.length > 0) {
+ this.weas.avr.deleteSelectedAtoms(this.indices);
+ }
this.weas.objectManager.deleteSelectedObjects();
}
undo() {
console.log("undo delete");
- this.weas.avr.atoms = this.initialAtoms.copy();
+ if (this.indices.length > 0) {
+ this.weas.avr.atoms = this.initialAtoms.copy();
+ }
// Restore the deleted objects
const selectedObjects = [];
this.initialObjectsState.forEach(({ object, parent }) => {
@@ -32,7 +36,7 @@ class DeleteOperation extends BaseOperation {
parent.add(object);
} else {
// If no parent was recorded, add it directly to the scene
- this.weas.tjs.sceneManager.scene.add(object);
+ this.weas.tjs.scene.add(object);
}
selectedObjects.push(object);
});
@@ -54,16 +58,20 @@ class CopyOperation extends BaseOperation {
}
execute() {
- this.weas.avr.copyAtoms(this.indices);
+ if (this.indices.length > 0) {
+ this.weas.avr.copyAtoms(this.indices);
+ }
this.newObjects = this.weas.objectManager.copySelectedObjects();
}
undo() {
console.log("Undo copy operation.");
- this.weas.avr.atoms = this.initialAtoms.copy();
+ if (this.indices.length > 0) {
+ this.weas.avr.atoms = this.initialAtoms.copy();
+ }
// Remove the new objects
this.newObjects.forEach((object) => {
- clearObject(this.weas.tjs.sceneManager.scene, object);
+ clearObject(this.weas.tjs.scene, object);
});
}
diff --git a/src/operation/transform.js b/src/operation/transform.js
index 2af5bee..972f16a 100644
--- a/src/operation/transform.js
+++ b/src/operation/transform.js
@@ -5,40 +5,42 @@ class TranslateOperation extends BaseOperation {
static description = "Translate";
static category = "Edit";
- constructor(weas, translateVector = new THREE.Vector3()) {
+ constructor({ weas, vector = new THREE.Vector3() }) {
super(weas);
// currentFrame
- this.currentFrame = weas.currentFrame;
+ this.currentFrame = weas.avr.currentFrame;
// store the selected atoms and the translate vector
this.selectedAtomsIndices = Array.from(weas.avr.selectedAtomsIndices);
- this.selectedObjects = weas.selectedObjects;
- // if translateVector is a normal array [x, y, z], convert it to a THREE.Vector3
- if (Array.isArray(translateVector)) {
- translateVector = new THREE.Vector3(translateVector[0], translateVector[1], translateVector[2]);
+ this.selectedObjects = weas.selectionManager.selectedObjects;
+ // if vector is a normal array [x, y, z], convert it to a THREE.Vector3
+ if (Array.isArray(vector)) {
+ vector = new THREE.Vector3(vector[0], vector[1], vector[2]);
}
- this.translateVector = translateVector.clone();
- this.translateVectorGui = { x: translateVector.x, y: translateVector.y, z: translateVector.z };
+ this.vector = vector.clone();
+ this.vectorGui = { x: vector.x, y: vector.y, z: vector.z };
}
execute() {
console.log("execute translate");
- this.weas.currentFrame = this.currentFrame;
- this.weas.avr.translateSelectedAtoms(this.translateVector, this.selectedAtomsIndices);
- this.weas.objectManager.translateSelectedObjects(this.translateVector);
+ this.weas.avr.currentFrame = this.currentFrame;
+ this.weas.selectionManager.selectedObjects = this.selectedObjects;
+ this.weas.avr.translateSelectedAtoms(this.vector, this.selectedAtomsIndices);
+ this.weas.objectManager.translateSelectedObjects(this.vector);
}
undo() {
console.log("undo translate");
- this.weas.currentFrame = this.currentFrame;
- // negative translateVector
- const negativeTranslateVector = this.translateVector.clone().negate();
- this.weas.avr.translateSelectedAtoms(negativeTranslateVector, this.selectedAtomsIndices);
- this.weas.objectManager.translateSelectedObjects(negativeTranslateVector);
+ this.weas.avr.currentFrame = this.currentFrame;
+ // negative vector
+ const negativevector = this.vector.clone().negate();
+ this.weas.avr.translateSelectedAtoms(negativevector, this.selectedAtomsIndices);
+ this.weas.selectionManager.selectedObjects = this.selectedObjects;
+ this.weas.objectManager.translateSelectedObjects(negativevector);
}
adjust() {
this.undo();
- this.translateVector = new THREE.Vector3(this.translateVectorGui.x, this.translateVectorGui.y, this.translateVectorGui.z);
+ this.vector = new THREE.Vector3(this.vectorGui.x, this.vectorGui.y, this.vectorGui.z);
this.execute(); // Re-execute with the new translate vector
}
@@ -47,19 +49,19 @@ class TranslateOperation extends BaseOperation {
renameFolder(guiFolder, "Translate");
guiFolder
- .add(this.translateVectorGui, "x", -10, 10)
+ .add(this.vectorGui, "x", -10, 10)
.name("X-axis")
.onChange((value) => {
this.adjust();
});
guiFolder
- .add(this.translateVectorGui, "y", -10, 10)
+ .add(this.vectorGui, "y", -10, 10)
.name("Y-axis")
.onChange((value) => {
this.adjust();
});
guiFolder
- .add(this.translateVectorGui, "z", -10, 10)
+ .add(this.vectorGui, "z", -10, 10)
.name("Z-axis")
.onChange((value) => {
this.adjust();
@@ -71,11 +73,14 @@ class RotateOperation extends BaseOperation {
static description = "Rotate";
static category = "Edit";
- constructor(weas, axis, angle) {
+ constructor({ weas, axis, angle }) {
super(weas);
- this.currentFrame = weas.currentFrame;
+ this.currentFrame = weas.avr.currentFrame;
this.selectedAtomsIndices = Array.from(weas.avr.selectedAtomsIndices);
- this.selectedObjects = weas.selectedObjects;
+ this.selectedObjects = weas.selectionManager.selectedObjects;
+ if (Array.isArray(axis)) {
+ axis = new THREE.Vector3(axis[0], axis[1], axis[2]);
+ }
this.axis = axis;
this.angle = angle;
this.axisGui = { x: axis.x, y: axis.y, z: axis.z };
@@ -84,7 +89,8 @@ class RotateOperation extends BaseOperation {
execute() {
// Implementation for rotating selected atoms
- this.weas.currentFrame = this.currentFrame;
+ this.weas.avr.currentFrame = this.currentFrame;
+ this.weas.selectionManager.selectedObjects = this.selectedObjects;
this.weas.avr.rotateSelectedAtoms(this.axis, this.angle, this.selectedAtomsIndices);
this.weas.objectManager.rotateSelectedObjects(this.axis, this.angle);
}
@@ -92,7 +98,8 @@ class RotateOperation extends BaseOperation {
undo() {
// Undo logic
console.log("undo rotate");
- this.weas.currentFrame = this.currentFrame;
+ this.weas.avr.currentFrame = this.currentFrame;
+ this.weas.selectionManager.selectedObjects = this.selectedObjects;
// rotate the atoms back
this.weas.avr.rotateSelectedAtoms(this.axis, -this.angle, this.selectedAtomsIndices);
// rotate the objects back
@@ -142,27 +149,32 @@ class ScaleOperation extends BaseOperation {
static description = "Scale";
static category = "Edit";
- constructor(weas, scale = new THREE.Vector3()) {
+ constructor({ weas, scale = new THREE.Vector3() }) {
super(weas);
// currentFrame
- this.currentFrame = weas.currentFrame;
+ this.currentFrame = weas.avr.currentFrame;
// store the selected atoms and the scale vector
this.selectedAtomsIndices = Array.from(weas.avr.selectedAtomsIndices);
- this.selectedObjects = weas.selectedObjects;
+ this.selectedObjects = weas.selectionManager.selectedObjects;
+ if (Array.isArray(scale)) {
+ scale = new THREE.Vector3(scale[0], scale[1], scale[2]);
+ }
this.scale = scale.clone();
this.scaleGui = { x: scale.x, y: scale.y, z: scale.z };
}
execute() {
console.log("execute scale");
- this.weas.currentFrame = this.currentFrame;
+ this.weas.avr.currentFrame = this.currentFrame;
+ this.weas.selectionManager.selectedObjects = this.selectedObjects;
// this.weas.avr.scaleSelectedAtoms(this.scale, this.selectedAtomsIndices);
this.weas.objectManager.scaleSelectedObjects(this.scale);
}
undo() {
console.log("undo scale");
- this.weas.currentFrame = this.currentFrame;
+ this.weas.avr.currentFrame = this.currentFrame;
+ this.weas.selectionManager.selectedObjects = this.selectedObjects;
// scale back, by 1/scale
const scale = new THREE.Vector3(1 / this.scale.x, 1 / this.scale.y, 1 / this.scale.z);
// this.weas.avr.scaleSelectedAtoms(scale, this.selectedAtomsIndices);
diff --git a/src/tools/camera.js b/src/tools/camera.js
index 8084245..d805b45 100644
--- a/src/tools/camera.js
+++ b/src/tools/camera.js
@@ -2,17 +2,31 @@ export function setupCameraGUI(gui, camera) {
// Create a folder for camera parameters
const cameraFolder = gui.addFolder("Camera");
- cameraFolder.add(camera.position, "x", -100, 100).name("X Position");
- cameraFolder.add(camera.position, "y", -100, 100).name("Y Position");
- cameraFolder.add(camera.position, "z", -100, 100).name("Z Position");
+ // Temp storage for position to use in onChange callbacks
+ const position = { x: camera.position.x, y: camera.position.y, z: camera.position.z };
- cameraFolder.add(camera.rotation, "x", -Math.PI, Math.PI).name("X Rotation");
- cameraFolder.add(camera.rotation, "y", -Math.PI, Math.PI).name("Y Rotation");
- cameraFolder.add(camera.rotation, "z", -Math.PI, Math.PI).name("Z Rotation");
-
- const cameraParams = {
- fov: camera.fov,
- near: camera.near,
- far: camera.far,
- };
+ cameraFolder
+ .add(position, "x", -100, 100)
+ .name("X Position")
+ .onChange((newValue) => {
+ camera.updatePosition(newValue, position.y, position.z);
+ // Update the temp storage to ensure consistency
+ position.x = newValue;
+ });
+ cameraFolder
+ .add(position, "y", -100, 100)
+ .name("Y Position")
+ .onChange((newValue) => {
+ camera.updatePosition(position.x, newValue, position.z);
+ // Update the temp storage to ensure consistency
+ position.y = newValue;
+ });
+ cameraFolder
+ .add(position, "z", -100, 100)
+ .name("Z Position")
+ .onChange((newValue) => {
+ camera.updatePosition(position.x, position.y, newValue);
+ // Update the temp storage to ensure consistency
+ position.z = newValue;
+ });
}
diff --git a/src/tools/viewpoint.js b/src/tools/viewpoint.js
index 3954315..720df5b 100644
--- a/src/tools/viewpoint.js
+++ b/src/tools/viewpoint.js
@@ -1,27 +1,27 @@
-export function createViewpointButtons(viewer, gui) {
+export function createViewpointButtons(weas, gui) {
// Create a folder for the viewpoint buttons
- console.log("viewer", viewer);
+ console.log("weas", weas);
const viewpointFolder = gui.addFolder("Viewpoint");
// Create buttons for different viewpoints
const viewpoints = {
Top: () => {
- viewer.tjs.updateCameraAndControls(viewer.atoms.getCenterOfGeometry(), [0, 0, 100]);
+ weas.tjs.updateCameraAndControls({ direction: [0, 0, 100] });
},
Bottom: () => {
- viewer.tjs.updateCameraAndControls(viewer.atoms.getCenterOfGeometry(), [0, 0, -100]);
+ weas.tjs.updateCameraAndControls({ direction: [0, 0, -100] });
},
Left: () => {
- viewer.tjs.updateCameraAndControls(viewer.atoms.getCenterOfGeometry(), [-100, 0, 0]);
+ weas.tjs.updateCameraAndControls({ direction: [-100, 0, 0] });
},
Right: () => {
- viewer.tjs.updateCameraAndControls(viewer.atoms.getCenterOfGeometry(), [100, 0, 0]);
+ weas.tjs.updateCameraAndControls({ direction: [100, 0, 0] });
},
Front: () => {
- viewer.tjs.updateCameraAndControls(viewer.atoms.getCenterOfGeometry(), [0, -100, 0]);
+ weas.tjs.updateCameraAndControls({ direction: [0, -100, 0] });
},
Back: () => {
- viewer.tjs.updateCameraAndControls(viewer.atoms.getCenterOfGeometry(), [0, 100, 0]);
+ weas.tjs.updateCameraAndControls({ direction: [0, 100, 0] });
},
};
diff --git a/src/utils.js b/src/utils.js
index 3050dfb..83d6adc 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -48,9 +48,7 @@ export function clearObject(scene, obj) {
}
}
-export function getWorldPositionFromScreen(screenX, screenY, camera, plane) {
- const ndc = new THREE.Vector2((screenX / window.innerWidth) * 2 - 1, -(screenY / window.innerHeight) * 2 + 1);
-
+export function getWorldPositionFromScreen(camera, ndc, plane) {
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(ndc, camera);
diff --git a/src/weas.js b/src/weas.js
index 3c2bdf9..630b867 100644
--- a/src/weas.js
+++ b/src/weas.js
@@ -47,7 +47,7 @@ class WEAS {
this.avr.animate();
Object.values(this.tjs.renderers).forEach((rndr) => {
- rndr.renderer.render(this.tjs.sceneManager.scene, this.tjs.camera);
+ rndr.renderer.render(this.tjs.scene, this.tjs.camera);
});
};
@@ -55,7 +55,7 @@ class WEAS {
}
clear() {
- this.tjs.sceneManager.clear();
+ this.tjs.scene.clear();
}
}
diff --git a/tests/e2e/gui.spec.js b/tests/e2e/gui.spec.js
index a9227d1..b28ee53 100644
--- a/tests/e2e/gui.spec.js
+++ b/tests/e2e/gui.spec.js
@@ -210,8 +210,72 @@ test.describe("Selection", () => {
// simulate keydown event
await page.keyboard.press("g");
// mouse move to the center of the canvas element
- await page.mouse.move(page.centerX - 200, page.centerY);
- await page.mouse.click(page.centerX - 200, page.centerY);
+ await page.mouse.move(page.centerX - 100, page.centerY);
+ await page.mouse.click(page.centerX - 100, page.centerY);
await expect(page).toHaveScreenshot();
});
});
+
+test.describe("Animation", () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto("http://127.0.0.1:8080/tests/e2e/testAnimation.html");
+
+ // focus the element
+ const element = await page.$("#viewer");
+ await element.focus();
+ const boundingBox = await element.boundingBox();
+ // Calculate the center of the element
+ const centerX = boundingBox.x + boundingBox.width / 2;
+ const centerY = boundingBox.y + boundingBox.height / 2;
+ page.centerX = centerX;
+ page.centerY = centerY;
+ // Move the mouse to the center of the element
+ await page.mouse.move(centerX, centerY);
+ });
+
+ test("Undo Redos", async ({ page }) => {
+ await expect(page).toHaveScreenshot("Animation-frame-0.png");
+ // simulate keydown event
+ await page.keyboard.press("g");
+ // mouse move to the center of the canvas element
+ await page.mouse.move(page.centerX + 100, page.centerY);
+ await page.mouse.click(page.centerX + 100, page.centerY);
+ // current frame is 0, add name toHaveScreenshot
+ await expect(page).toHaveScreenshot("Animation-frame-0-move.png");
+ // set frame 10
+ await page.evaluate(() => {
+ const timeline = document.getElementById("timeline");
+ timeline.value = 10;
+ // Creating and dispatching the event must happen within the page context
+ const event = new Event("input", {
+ bubbles: true,
+ cancelable: true,
+ });
+ timeline.dispatchEvent(event);
+ });
+ await expect(page).toHaveScreenshot("Animation-frame-10.png");
+ //move atoms
+ await page.keyboard.press("g");
+ // mouse move to the center of the canvas element
+ await page.mouse.move(page.centerX + 200, page.centerY);
+ await page.mouse.click(page.centerX + 200, page.centerY);
+ await expect(page).toHaveScreenshot("Animation-frame-10-move.png");
+ // undo
+ await page.keyboard.down("Control");
+ await page.keyboard.press("z");
+ await expect(page).toHaveScreenshot("Animation-frame-10-undo.png");
+ // undo, should go back to frame 0
+ const element = await page.$("#undo");
+ await element.click();
+ await expect(page).toHaveScreenshot("Animation-frame-0-undo.png");
+ // redo
+ await page.keyboard.down("Control");
+ await page.keyboard.press("y");
+ await expect(page).toHaveScreenshot("Animation-frame-0-redo.png");
+ // redo
+ await page.waitForSelector("#redo", { state: "attached" });
+ const element2 = await page.$("#redo");
+ await element2.click();
+ await expect(page).toHaveScreenshot("Animation-frame-10-redo.png");
+ });
+});
diff --git a/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-chromium-linux.png
new file mode 100644
index 0000000..b2db8ac
Binary files /dev/null and b/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-move-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-move-chromium-linux.png
new file mode 100644
index 0000000..35987ba
Binary files /dev/null and b/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-move-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-redo-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-redo-chromium-linux.png
new file mode 100644
index 0000000..0c95b95
Binary files /dev/null and b/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-redo-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-undo-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-undo-chromium-linux.png
new file mode 100644
index 0000000..34dd648
Binary files /dev/null and b/tests/e2e/gui.spec.js-snapshots/Animation-frame-0-undo-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-chromium-linux.png
new file mode 100644
index 0000000..80b069d
Binary files /dev/null and b/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-move-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-move-chromium-linux.png
new file mode 100644
index 0000000..3e35e2f
Binary files /dev/null and b/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-move-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-redo-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-redo-chromium-linux.png
new file mode 100644
index 0000000..ed819ed
Binary files /dev/null and b/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-redo-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-undo-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-undo-chromium-linux.png
new file mode 100644
index 0000000..3127809
Binary files /dev/null and b/tests/e2e/gui.spec.js-snapshots/Animation-frame-10-undo-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Duplicate-Atoms-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Duplicate-Atoms-1-chromium-linux.png
index a73f80c..dec4cf2 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Duplicate-Atoms-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Duplicate-Atoms-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Move-Atoms-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Move-Atoms-1-chromium-linux.png
index 43aff36..482fe28 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Move-Atoms-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Move-Atoms-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Delete-Object-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Delete-Object-1-chromium-linux.png
index bdb2796..aa658db 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Delete-Object-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Delete-Object-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Duplicate-Object-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Duplicate-Object-1-chromium-linux.png
index a58c611..de3388f 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Duplicate-Object-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Duplicate-Object-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Mesh-Primitive-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Mesh-Primitive-1-chromium-linux.png
index 7aabcb1..808b244 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Mesh-Primitive-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Mesh-Primitive-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Move-Object-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Move-Object-1-chromium-linux.png
index 5e588c4..0304837 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Move-Object-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Move-Object-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Rotate-Object-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Rotate-Object-1-chromium-linux.png
index a983521..d7c67ba 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Rotate-Object-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Rotate-Object-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Scale-Object-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Scale-Object-1-chromium-linux.png
index e5eedaa..6f0b97e 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Object-Scale-Object-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Object-Scale-Object-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-1-chromium-linux.png
index 43aff36..482fe28 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-1-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-3-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-3-chromium-linux.png
index 48df32a..dcbba0e 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-3-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-3-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-4-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-4-chromium-linux.png
index 27df862..9ca9a83 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-4-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-4-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-5-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-5-chromium-linux.png
index 5f12ad7..19d3c98 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-5-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-5-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-6-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-6-chromium-linux.png
index 80651af..69e3080 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-6-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Edit-Undo-Redos-6-chromium-linux.png differ
diff --git a/tests/e2e/gui.spec.js-snapshots/Selection-Move-Selected-1-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Selection-Move-Selected-1-chromium-linux.png
index da5b7ae..1c793f6 100644
Binary files a/tests/e2e/gui.spec.js-snapshots/Selection-Move-Selected-1-chromium-linux.png and b/tests/e2e/gui.spec.js-snapshots/Selection-Move-Selected-1-chromium-linux.png differ
diff --git a/tests/e2e/testAnimation.html b/tests/e2e/testAnimation.html
new file mode 100644
index 0000000..09049d8
--- /dev/null
+++ b/tests/e2e/testAnimation.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+ WEAS Test
+
+
+
+
+
+
+
diff --git a/tests/e2e/testPrimitive.html b/tests/e2e/testPrimitive.html
index 2032931..3faae5e 100644
--- a/tests/e2e/testPrimitive.html
+++ b/tests/e2e/testPrimitive.html
@@ -20,10 +20,11 @@
let domElement = document.getElementById("viewer");
let editor = new weas.WEAS({ domElement });
- editor.ops.mesh.AddCubeOperation({ position: [-5, 0, 0], size: 2 });
- editor.ops.mesh.AddSphereOperation({ position: [5, 2, 0], radius: 2, color: "#00FF00" });
+ editor.ops.mesh.AddCubeOperation({ position: [0, 0, 0], scale: [1, 1, 1], color: "#0000FF", opacity: 0.5 });
+ editor.ops.mesh.AddSphereOperation({ position: [-5, 0, 0], scale: [1, 1, 1], color: "#00FF00", opacity: 0.5 });
+ editor.ops.mesh.AddCylinderOperation({ position: [5, 0, 0], scale: [1, 1, 1], color: "#bd0d87", opacity: 0.5 });
editor.ops.hideGUI();
- editor.selectionManager.selectedObjects = [editor.tjs.scene.children[5]];
+ editor.selectionManager.selectedObjects = [editor.tjs.scene.children[3]];
editor.render();