diff --git a/zndraw/package-lock.json b/zndraw/package-lock.json new file mode 100644 index 000000000..06217e302 --- /dev/null +++ b/zndraw/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "zndraw", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/zndraw/static/World/World.js b/zndraw/static/World/World.js index 1522dcb4d..2a69eec7c 100644 --- a/zndraw/static/World/World.js +++ b/zndraw/static/World/World.js @@ -14,6 +14,7 @@ import { import { Selection } from "./systems/select.js"; import { Line3D, Canvas3D } from "./components/draw.js"; +import { centerCamera } from "./systems/events.js"; // These variables are module-scoped: we cannot access them // from outside the module @@ -232,6 +233,11 @@ class World { */ start() { loop.start(); + // center camera if there are particles + const particles = this.particles.cache.get(0); + if (!(particles === undefined || particles.length === 0)) { + centerCamera(this.selection.controls, this.particles); + } } /** diff --git a/zndraw/static/World/components/particles.js b/zndraw/static/World/components/particles.js index 17305def2..9f31c9306 100644 --- a/zndraw/static/World/components/particles.js +++ b/zndraw/static/World/components/particles.js @@ -252,6 +252,16 @@ class ParticlesGroup extends THREE.Group { } } } + + get_center(selection) { + let selectedParticles = this.particle_cache; + if (selection !== undefined && selection.length > 0) { + selectedParticles = this.particle_cache.select(selection); + } + const center = new THREE.Vector3(); + center.copy(selectedParticles.getCenter()); + return center; + } } class CellGroup extends THREE.Group { diff --git a/zndraw/static/World/systems/events.js b/zndraw/static/World/systems/events.js new file mode 100644 index 000000000..b12470e81 --- /dev/null +++ b/zndraw/static/World/systems/events.js @@ -0,0 +1,15 @@ +import * as THREE from "three"; + +export function centerCamera(controls, particlesGroup) { + if (controls.enablePan) { + // get the first object that is selected + const dummy = new THREE.Vector3(); + dummy.copy(controls.getCenter(particlesGroup.selection)); + controls.target.copy(dummy); + } else { + // follow is currently not working due to instancing + // document.getElementById("alertBoxCamera").style.display = "none"; + // controls.target = controls.target.clone(); + // controls.enablePan = true; + } +} diff --git a/zndraw/static/World/systems/select.js b/zndraw/static/World/systems/select.js index 8a07a0203..2824bb914 100644 --- a/zndraw/static/World/systems/select.js +++ b/zndraw/static/World/systems/select.js @@ -1,5 +1,6 @@ import * as THREE from "three"; import { TransformControls } from "three/examples/jsm/controls/TransformControls.js"; +import { centerCamera } from "./events.js"; let scroll_timer = null; @@ -49,9 +50,9 @@ class Selection { this._setupKeyboardEvents(); - this.controls.getCenter = () => { + this.controls.getCenter = (selection) => { const particlesGroup = this.scene.getObjectByName("particlesGroup"); - return particlesGroup.get_center(); + return particlesGroup.get_center(selection); }; this.socket.on("selection:run", (data) => { @@ -228,26 +229,7 @@ class Selection { if (document.activeElement === document.body) { const particlesGroup = this.scene.getObjectByName("particlesGroup"); if (event.key === "c") { - if (this.controls.enablePan) { - // get the first object that is selected - if (particlesGroup.selection.length > 0) { - const matrix = new THREE.Matrix4(); - const dummy = new THREE.Object3D(); - particlesGroup.particles_mesh.getMatrixAt( - particlesGroup.selection[0], - matrix, - ); - matrix.decompose(dummy.position, dummy.quaternion, dummy.scale); - this.controls.target.copy(dummy.position); - // this.controls.enablePan = false; - // document.getElementById("alertBoxCamera").style.display = "block"; - } - } else { - // follow is currently not working due to instancing - // document.getElementById("alertBoxCamera").style.display = "none"; - // this.controls.target = this.controls.target.clone(); - // this.controls.enablePan = true; - } + centerCamera(this.controls, particlesGroup); } if (event.key === "x") { if (this._drawing) { diff --git a/zndraw/static/pycom/Cache.js b/zndraw/static/pycom/Cache.js index b5b2371d5..59f6ac0d2 100644 --- a/zndraw/static/pycom/Cache.js +++ b/zndraw/static/pycom/Cache.js @@ -1,5 +1,5 @@ // Interface for the communication with Python to retrieve atoms - +import * as THREE from "three"; class Atom { constructor({ position, number, color, id, radius }) { this.position = position; @@ -53,6 +53,34 @@ class Atoms { }, }; } + select(indices) { + const selectedPositions = indices.map((index) => this.positions[index]); + const selectedNumbers = indices.map((index) => this.numbers[index]); + const selectedColors = indices.map((index) => this.colors[index]); + const selectedRadii = indices.map((index) => this.radii[index]); + const selectedAtoms = new Atoms({ + positions: selectedPositions, + cell: this.cell, + numbers: selectedNumbers, + colors: selectedColors, + radii: selectedRadii, + connectivity: this.connectivity, + calc: this.calc, + pbc: this.pbc, + }); + return selectedAtoms; + } + + getCenter() { + const sum = this.positions.reduce((acc, position) => { + const vec = new THREE.Vector3().fromArray(position); + return acc.add(vec); + }, new THREE.Vector3()); + + const mean = sum.divideScalar(this.positions.length); + + return mean; + } } class Cache {