Bladeren bron

principal axes for loci and orientation for Camera.focus

Alexander Rose 5 jaren geleden
bovenliggende
commit
2a2261beaf

+ 6 - 6
src/mol-canvas3d/camera.ts

@@ -79,12 +79,10 @@ class Camera {
     }
 
     getSnapshot() {
-        const ret = Camera.createDefaultSnapshot();
-        Camera.copySnapshot(ret, this.state);
-        return ret;
+        return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
     }
 
-    getFocus(target: Vec3, radius: number): Partial<Camera.Snapshot> {
+    getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
         const fov = this.state.fov
         const { width, height } = this.viewport
         const aspect = width / height
@@ -92,6 +90,7 @@ class Camera {
         const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
 
         Vec3.sub(this.deltaDirection, this.target, this.position)
+        if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection)
         Vec3.setMagnitude(this.deltaDirection, this.deltaDirection, targetDistance)
         Vec3.sub(this.newPosition, target, this.deltaDirection)
 
@@ -99,12 +98,13 @@ class Camera {
         state.target = Vec3.clone(target)
         state.radius = radius
         state.position = Vec3.clone(this.newPosition)
+        if (up) Vec3.matchDirection(state.up, up, state.up)
 
         return state
     }
 
-    focus(target: Vec3, radius: number, durationMs?: number) {
-        if (radius > 0) this.setState(this.getFocus(target, radius), durationMs);
+    focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
+        if (radius > 0) this.setState(this.getFocus(target, radius, up, dir), durationMs);
     }
 
     project(out: Vec4, point: Vec3) {

+ 3 - 3
src/mol-math/linear-algebra/matrix/matrix.ts

@@ -30,11 +30,11 @@ namespace Matrix {
         for (let i = 0, _l = m.data.length; i < _l; i++) m.data[i] = 0.0;
     }
 
-    export function fromArray(data: NumberArray, cols: number, rows: number): Matrix {
+    export function fromArray<N extends number, M extends number>(data: NumberArray, cols: N, rows: M): Matrix<N, M> {
         return { data, size: cols * rows, cols, rows }
     }
 
-    export function transpose(out: Matrix, mat: Matrix): Matrix {
+    export function transpose<N extends number, M extends number>(out: Matrix<M, N>, mat: Matrix<N, M>): Matrix<M, N> {
         const nrows = mat.rows, ncols = mat.cols
         const md = mat.data, mtd = out.data
 
@@ -42,7 +42,7 @@ namespace Matrix {
             let ri = mti
             for (let j = 0; j < ncols; ri += nrows, j++) mtd[ri] = md[mi + j]
         }
-        return mat
+        return out
     }
 
     /** out = matA * matB' */

+ 1 - 1
src/mol-math/linear-algebra/matrix/principal-axes.ts

@@ -33,7 +33,7 @@ namespace PrincipalAxes {
     /**
      * @param points 3xN matrix
      */
-    export function fromPoints(points: Matrix<3, number>): PrincipalAxes {
+    export function ofPoints(points: Matrix<3, number>): PrincipalAxes {
         const n = points.rows
         const n3 = n / 3
         const pointsT = Matrix.create(n, 3)

+ 1 - 1
src/mol-model/structure/structure/carbohydrates/compute.ts

@@ -195,7 +195,7 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
                     const ringAtoms = rings.all[sugarRings[j]];
                     const anomericCarbon = getAnomericCarbon(unit, ringAtoms)
 
-                    const pa = PrincipalAxes.fromPoints(getPositionMatrix(unit, ringAtoms))
+                    const pa = PrincipalAxes.ofPoints(getPositionMatrix(unit, ringAtoms))
                     const center = Vec3.copy(Vec3.zero(), pa.center)
                     const normal = Vec3.copy(Vec3.zero(), pa.normVecC)
                     const direction = getDirection(Vec3.zero(), unit, anomericCarbon, center)

+ 33 - 5
src/mol-model/structure/structure/element/loci.ts

@@ -19,6 +19,9 @@ import { ElementIndex } from '../../model';
 import { UnitIndex } from './element';
 import { Location } from './location';
 import { ChainIndex } from '../../model/indexing';
+import { PrincipalAxes } from '../../../../mol-math/linear-algebra/matrix/principal-axes';
+import Matrix from '../../../../mol-math/linear-algebra/matrix/matrix';
+import { NumberArray } from '../../../../mol-util/type-helpers';
 
 /** Represents multiple element index locations */
 export interface Loci {
@@ -376,7 +379,8 @@ export namespace Loci {
         return Loci(loci.structure, elements);
     }
 
-    const boundaryHelper = new BoundaryHelper(), tempPos = Vec3.zero();
+    const boundaryHelper = new BoundaryHelper();
+    const tempPosBoundary = Vec3.zero();
     export function getBoundary(loci: Loci): Boundary {
         boundaryHelper.reset(0);
 
@@ -386,8 +390,8 @@ export namespace Loci {
             const { elements } = e.unit;
             for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
                 const eI = elements[OrderedSet.getAt(indices, i)];
-                pos(eI, tempPos);
-                boundaryHelper.boundaryStep(tempPos, r(eI));
+                pos(eI, tempPosBoundary);
+                boundaryHelper.boundaryStep(tempPosBoundary, r(eI));
             }
         }
         boundaryHelper.finishBoundaryStep();
@@ -397,14 +401,38 @@ export namespace Loci {
             const { elements } = e.unit;
             for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
                 const eI = elements[OrderedSet.getAt(indices, i)];
-                pos(eI, tempPos);
-                boundaryHelper.extendStep(tempPos, r(eI));
+                pos(eI, tempPosBoundary);
+                boundaryHelper.extendStep(tempPosBoundary, r(eI));
             }
         }
 
         return { box: boundaryHelper.getBox(), sphere: boundaryHelper.getSphere() };
     }
 
+    const tempPos = Vec3.zero();
+    export function toPositionsArray(loci: Loci, positions: NumberArray, offset = 0) {
+        let m = offset
+        for (const e of loci.elements) {
+            const { indices } = e
+            const pos = e.unit.conformation.position
+            const { elements } = e.unit
+            const indexCount = OrderedSet.size(indices)
+            for (let i = 0; i < indexCount; i++) {
+                const eI = elements[OrderedSet.getAt(indices, i)]
+                pos(eI, tempPos)
+                Vec3.toArray(tempPos, positions, m + i * 3)
+            }
+            m += indexCount * 3
+        }
+        return positions
+    }
+
+    export function getPrincipalAxes(loci: Loci): PrincipalAxes {
+        const elementCount = size(loci)
+        const positions = toPositionsArray(loci, new Float32Array(3 * elementCount))
+        return PrincipalAxes.ofPoints(Matrix.fromArray(positions, 3, elementCount))
+    }
+
     function sourceIndex(unit: Unit, element: ElementIndex) {
         return Unit.isAtomic(unit)
             ? unit.model.atomicHierarchy.atoms.sourceIndex.value(element)

+ 3 - 1
src/mol-plugin/ui/structure/selection.tsx

@@ -59,7 +59,9 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
         if (this.plugin.helpers.structureSelectionManager.stats.elementCount === 0) return
         const { sphere } = this.plugin.helpers.structureSelectionManager.getBoundary();
         const radius = Math.max(sphere.radius + extraRadius, minRadius);
-        this.plugin.canvas3d.camera.focus(sphere.center, radius, durationMs);
+        const principalAxes = this.plugin.helpers.structureSelectionManager.getPrincipalAxes();
+        const { center, normVecA, normVecC } = principalAxes
+        this.plugin.canvas3d.camera.focus(center, radius, durationMs, normVecA, normVecC);
     }
 
     setProps = (p: { param: PD.Base<any>, name: string, value: any }) => {

+ 22 - 0
src/mol-plugin/util/structure-element-selection.ts

@@ -15,6 +15,8 @@ import { structureElementStatsLabel } from '../../mol-theme/label';
 import { Vec3 } from '../../mol-math/linear-algebra';
 import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
 import { Boundary } from '../../mol-model/structure/structure/util/boundary';
+import { PrincipalAxes } from '../../mol-math/linear-algebra/matrix/principal-axes';
+import Matrix from '../../mol-math/linear-algebra/matrix/matrix';
 
 const boundaryHelper = new BoundaryHelper();
 
@@ -35,6 +37,15 @@ class StructureElementSelectionManager {
         return this.entries.get(ref)!;
     }
 
+    /** Count of all selected elements */
+    size() {
+        let count = 0
+        this.entries.forEach(v => {
+            count += StructureElement.Loci.size(v.selection)
+        })
+        return count
+    }
+
     getBoundary() {
         const min = Vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE)
         const max = Vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE)
@@ -66,6 +77,17 @@ class StructureElementSelectionManager {
         return { box: { min, max }, sphere: boundaryHelper.getSphere() };
     }
 
+    getPrincipalAxes(): PrincipalAxes {
+        const elementCount = this.size()
+        const positions = new Float32Array(3 * elementCount)
+        let offset = 0
+        this.entries.forEach(v => {
+            StructureElement.Loci.toPositionsArray(v.selection, positions, offset)
+            offset += StructureElement.Loci.size(v.selection)
+        })
+        return PrincipalAxes.ofPoints(Matrix.fromArray(positions, 3, elementCount))
+    }
+
     get stats() {
         let structureCount = 0
         let elementCount = 0

+ 1 - 1
src/mol-plugin/util/structure-orientation.ts

@@ -85,7 +85,7 @@ function getOrientationMesh(data: OrientationData, props: OrientationProps, mesh
 export async function getStructureOrientationRepresentation(ctx: RuntimeContext, structure: Structure, params: OrientationProps, prev?: ShapeRepresentation<OrientationData, Mesh, Mesh.Params>) {
     const repr = prev || ShapeRepresentation(getOrientationShape, Mesh.Utils);
     const data = {
-        principalAxes: PrincipalAxes.fromPoints(getPositionMatrix(structure))
+        principalAxes: PrincipalAxes.ofPoints(getPositionMatrix(structure))
     }
     await repr.createOrUpdate(params, data).runInContext(ctx);
     return repr;