From a725e577beee6a24b505d39e0e210d8646c1d4df Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 22 Nov 2019 18:28:16 -0800 Subject: [PATCH] refactored principal-axes calculation --- .../linear-algebra/matrix/principal-axes.ts | 127 +++++++++--------- .../structure/carbohydrates/compute.ts | 8 +- .../structure/structure/element/loci.ts | 3 +- src/mol-model/structure/util.ts | 14 +- src/mol-plugin/ui/structure/selection.tsx | 17 ++- .../util/structure-element-selection.ts | 3 +- src/mol-plugin/util/structure-orientation.ts | 87 ++++++------ 7 files changed, 128 insertions(+), 131 deletions(-) diff --git a/src/mol-math/linear-algebra/matrix/principal-axes.ts b/src/mol-math/linear-algebra/matrix/principal-axes.ts index 279067bb1..21a9d4d12 100644 --- a/src/mol-math/linear-algebra/matrix/principal-axes.ts +++ b/src/mol-math/linear-algebra/matrix/principal-axes.ts @@ -5,36 +5,31 @@ */ import Matrix from './matrix'; -import { Vec3, Mat4 } from '../3d'; +import { Vec3 } from '../3d'; import { svd } from './svd'; import { NumberArray } from '../../../mol-util/type-helpers'; +import { Axes3D } from '../../geometry'; export { PrincipalAxes } interface PrincipalAxes { - begA: Vec3 - endA: Vec3 - begB: Vec3 - endB: Vec3 - begC: Vec3 - endC: Vec3 - - center: Vec3 - - vecA: Vec3 - vecB: Vec3 - vecC: Vec3 - - normVecA: Vec3 - normVecB: Vec3 - normVecC: Vec3 + momentsAxes: Axes3D + boxAxes: Axes3D } namespace PrincipalAxes { - /** - * @param points 3xN matrix - */ - export function ofPoints(points: Matrix<3, number>): PrincipalAxes { + export function ofPositions(positions: NumberArray): PrincipalAxes { + const momentsAxes = calculateMomentsAxes(positions) + const boxAxes = calculateBoxAxes(positions, momentsAxes) + return { momentsAxes, boxAxes } + } + + export function calculateMomentsAxes(positions: NumberArray): Axes3D { + if (positions.length === 3) { + return Axes3D.create(Vec3.fromArray(Vec3(), positions, 0), Vec3(), Vec3(), Vec3()) + } + + const points = Matrix.fromArray(positions, 3, positions.length / 3) const n = points.rows const n3 = n / 3 const A = Matrix.create(3, 3) @@ -49,49 +44,33 @@ namespace PrincipalAxes { Matrix.multiplyABt(A, pointsT, pointsT) svd(A, W, U, V) - // center - const center = Vec3.create(mean[0], mean[1], mean[2]) - - // normalized - const normVecA = Vec3.create(U.data[0], U.data[3], U.data[6]) - const normVecB = Vec3.create(U.data[1], U.data[4], U.data[7]) - const normVecC = Vec3.create(U.data[2], U.data[5], U.data[8]) - - // scaled - const vecA = Vec3.scale(Vec3(), normVecA, Math.sqrt(W.data[0] / n3)) - const vecB = Vec3.scale(Vec3(), normVecB, Math.sqrt(W.data[1] / n3)) - const vecC = Vec3.scale(Vec3(), normVecC, Math.sqrt(W.data[2] / n3)) - - // points - const begA = Vec3.sub(Vec3.clone(center), center, vecA) - const endA = Vec3.add(Vec3.clone(center), center, vecA) - const begB = Vec3.sub(Vec3.clone(center), center, vecB) - const endB = Vec3.add(Vec3.clone(center), center, vecB) - const begC = Vec3.sub(Vec3.clone(center), center, vecC) - const endC = Vec3.add(Vec3.clone(center), center, vecC) - - return { - begA, endA, begB, endB, begC, endC, - center, - vecA, vecB, vecC, - normVecA, normVecB, normVecC - } - } + // origin + const origin = Vec3.create(mean[0], mean[1], mean[2]) - /** - * Set basis matrix for given axes - */ - export function setBasisMatrix(out: Mat4, principalAxes: PrincipalAxes) { - Mat4.setAxes(out, principalAxes.normVecB, principalAxes.normVecA, principalAxes.normVecC) - if (Mat4.determinant(out) < 0) Mat4.scaleUniformly(out, out, -1) - return out + // directions + const dirA = Vec3.create(U.data[0], U.data[3], U.data[6]) + const dirB = Vec3.create(U.data[1], U.data[4], U.data[7]) + const dirC = Vec3.create(U.data[2], U.data[5], U.data[8]) + Vec3.scale(dirA, dirA, Math.sqrt(W.data[0] / n3)) + Vec3.scale(dirB, dirB, Math.sqrt(W.data[1] / n3)) + Vec3.scale(dirC, dirC, Math.sqrt(W.data[2] / n3)) + + return Axes3D.create(origin, dirA, dirB, dirC) } + const tmpBoxVec = Vec3() + const tmpBoxVecA = Vec3() + const tmpBoxVecB = Vec3() + const tmpBoxVecC = Vec3() /** * Get the scale/length for each dimension for a box around the axes * to enclose the given positions */ - export function getProjectedScale(positions: NumberArray, principalAxes: PrincipalAxes) { + export function calculateBoxAxes(positions: NumberArray, momentsAxes: Axes3D): Axes3D { + if (positions.length === 3) { + return Axes3D.clone(momentsAxes) + } + let d1a = -Infinity let d1b = -Infinity let d2a = -Infinity @@ -102,7 +81,10 @@ namespace PrincipalAxes { const p = Vec3() const t = Vec3() - const { center, normVecA, normVecB, normVecC } = principalAxes + const center = momentsAxes.origin + const normVecA = Vec3.normalize(tmpBoxVecA, momentsAxes.dirA) + const normVecB = Vec3.normalize(tmpBoxVecB, momentsAxes.dirB) + const normVecC = Vec3.normalize(tmpBoxVecC, momentsAxes.dirC) for (let i = 0, il = positions.length; i < il; i += 3) { Vec3.projectPointOnVector(p, Vec3.fromArray(p, positions, i), normVecA, center) @@ -133,13 +115,28 @@ namespace PrincipalAxes { } } - return { - d1a: d1a, - d2a: d2a, - d3a: d3a, - d1b: -d1b, - d2b: -d2b, - d3b: -d3b + const dirA = Vec3.setMagnitude(Vec3(), normVecA, (d1a + d1b) / 2) + const dirB = Vec3.setMagnitude(Vec3(), normVecB, (d2a + d2b) / 2) + const dirC = Vec3.setMagnitude(Vec3(), normVecC, (d3a + d3b) / 2) + + const origin = Vec3() + const addCornerHelper = function (d1: number, d2: number, d3: number) { + Vec3.copy(tmpBoxVec, center) + Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, normVecA, d1) + Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, normVecB, d2) + Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, normVecC, d3) + Vec3.add(origin, origin, tmpBoxVec) } + addCornerHelper(d1a, d2a, d3a) + addCornerHelper(d1a, d2a, -d3b) + addCornerHelper(d1a, -d2b, -d3b) + addCornerHelper(d1a, -d2b, d3a) + addCornerHelper(-d1b, -d2b, -d3b) + addCornerHelper(-d1b, -d2b, d3a) + addCornerHelper(-d1b, d2a, d3a) + addCornerHelper(-d1b, d2a, -d3b) + Vec3.scale(origin, origin, 1 / 8) + + return Axes3D.create(origin, dirA, dirB, dirC) } } \ No newline at end of file diff --git a/src/mol-model/structure/structure/carbohydrates/compute.ts b/src/mol-model/structure/structure/carbohydrates/compute.ts index d0faefbf2..edf106ffd 100644 --- a/src/mol-model/structure/structure/carbohydrates/compute.ts +++ b/src/mol-model/structure/structure/carbohydrates/compute.ts @@ -13,7 +13,7 @@ import { PrincipalAxes } from '../../../../mol-math/linear-algebra/matrix/princi import { fillSerial } from '../../../../mol-util/array'; import { ResidueIndex, Model } from '../../model'; import { ElementSymbol } from '../../model/types'; -import { getPositionMatrix } from '../../util'; +import { getPositions } from '../../util'; import StructureElement from '../element'; import Structure from '../structure'; import Unit from '../unit'; @@ -195,9 +195,9 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { const ringAtoms = rings.all[sugarRings[j]]; const anomericCarbon = getAnomericCarbon(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 ma = PrincipalAxes.calculateMomentsAxes(getPositions(unit, ringAtoms)) + const center = Vec3.copy(Vec3.zero(), ma.origin) + const normal = Vec3.copy(Vec3.zero(), ma.dirC) const direction = getDirection(Vec3.zero(), unit, anomericCarbon, center) Vec3.orthogonalize(direction, normal, direction) diff --git a/src/mol-model/structure/structure/element/loci.ts b/src/mol-model/structure/structure/element/loci.ts index 7a04eeed8..5a4ffac71 100644 --- a/src/mol-model/structure/structure/element/loci.ts +++ b/src/mol-model/structure/structure/element/loci.ts @@ -20,7 +20,6 @@ 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 structure element index locations */ @@ -430,7 +429,7 @@ export namespace Loci { 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)) + return PrincipalAxes.ofPositions(positions) } function sourceIndex(unit: Unit, element: ElementIndex) { diff --git a/src/mol-model/structure/util.ts b/src/mol-model/structure/util.ts index 0ba5a6e3c..ea455e01b 100644 --- a/src/mol-model/structure/util.ts +++ b/src/mol-model/structure/util.ts @@ -8,7 +8,7 @@ import { Model, ResidueIndex, ElementIndex } from './model'; import { MoleculeType, AtomRole, PolymerTypeAtomRoleId, getMoleculeType, PolymerType } from './model/types'; import { Vec3 } from '../../mol-math/linear-algebra'; import { Unit } from './structure'; -import Matrix from '../../mol-math/linear-algebra/matrix/matrix'; +import { NumberArray } from '../../mol-util/type-helpers'; export function getCoarseBegCompId(unit: Unit.Spheres | Unit.Gaussians, element: ElementIndex) { const entityKey = unit.coarseElements.entityKey[element] @@ -80,14 +80,14 @@ export function elementLabel(model: Model, index: ElementIndex) { // return Vec3.distance(centerMin, centroid) // } -const matrixPos = Vec3.zero() -export function getPositionMatrix(unit: Unit, indices: ArrayLike<number>): Matrix<3, number> { +const tmpPositionsVec = Vec3.zero() +export function getPositions(unit: Unit, indices: ArrayLike<number>): NumberArray { const pos = unit.conformation.position - const mat = Matrix.create(3, indices.length) + const positions = new Float32Array(indices.length * 3) const { elements } = unit for (let i = 0, il = indices.length; i < il; ++i) { - pos(elements[indices[i]], matrixPos) - Vec3.toArray(matrixPos, mat.data, i * 3) + pos(elements[indices[i]], tmpPositionsVec) + Vec3.toArray(tmpPositionsVec, positions, i * 3) } - return mat + return positions } \ No newline at end of file diff --git a/src/mol-plugin/ui/structure/selection.tsx b/src/mol-plugin/ui/structure/selection.tsx index bfc003605..030f86068 100644 --- a/src/mol-plugin/ui/structure/selection.tsx +++ b/src/mol-plugin/ui/structure/selection.tsx @@ -14,6 +14,8 @@ import { Interactivity } from '../../util/interactivity'; import { ParameterControls } from '../controls/parameters'; import { stripTags } from '../../../mol-util/string'; import { StructureElement } from '../../../mol-model/structure'; +import { Vec3 } from '../../../mol-math/linear-algebra'; +import { Sphere3D } from '../../../mol-math/geometry'; const SSQ = StructureSelectionQueries const DefaultQueries: (keyof typeof SSQ)[] = [ @@ -58,21 +60,18 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS focus = () => { const { extraRadius, minRadius, durationMs } = this.state if (this.plugin.helpers.structureSelectionManager.stats.elementCount === 0) return - const { sphere } = this.plugin.helpers.structureSelectionManager.getBoundary(); - const radius = Math.max(sphere.radius + extraRadius, minRadius); const principalAxes = this.plugin.helpers.structureSelectionManager.getPrincipalAxes(); - const { center, normVecA, normVecC } = principalAxes - this.plugin.canvas3d.camera.focus(center, radius, durationMs, normVecA, normVecC); + const { origin, dirA, dirC } = principalAxes.boxAxes + const radius = Math.max(Vec3.magnitude(dirA) + extraRadius, minRadius); + this.plugin.canvas3d.camera.focus(origin, radius, durationMs, dirA, dirC); } - focusSingle(loci: StructureElement.Loci) { + focusLoci(loci: StructureElement.Loci) { return () => { const { extraRadius, minRadius, durationMs } = this.state if (this.plugin.helpers.structureSelectionManager.stats.elementCount === 0) return - const { sphere } = StructureElement.Loci.getBoundary(loci); + const sphere = Sphere3D.fromAxes3D(Sphere3D(), StructureElement.Loci.getPrincipalAxes(loci).boxAxes) const radius = Math.max(sphere.radius + extraRadius, minRadius); - // const principalAxes = this.plugin.helpers.structureSelectionManager.getPrincipalAxes(); - // const { center, normVecA, normVecC } = principalAxes this.plugin.canvas3d.camera.focus(sphere.center, radius, durationMs); } } @@ -151,7 +150,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS const e = mng.latestLoci[i]; latest.push(<li key={e!.label}> <button className='msp-btn msp-btn-block msp-form-control' style={{ borderRight: '6px solid transparent', overflow: 'hidden' }} - title='Click to focus.' onClick={this.focusSingle(e.loci)}> + title='Click to focus.' onClick={this.focusLoci(e.loci)}> <span dangerouslySetInnerHTML={{ __html: e.label }} /> </button> {/* <div> diff --git a/src/mol-plugin/util/structure-element-selection.ts b/src/mol-plugin/util/structure-element-selection.ts index 2a11a99b9..3311638ce 100644 --- a/src/mol-plugin/util/structure-element-selection.ts +++ b/src/mol-plugin/util/structure-element-selection.ts @@ -16,7 +16,6 @@ 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'; import { arrayRemoveAtInPlace } from '../../mol-util/array'; const boundaryHelper = new BoundaryHelper(); @@ -92,7 +91,7 @@ class StructureElementSelectionManager { StructureElement.Loci.toPositionsArray(v.selection, positions, offset) offset += StructureElement.Loci.size(v.selection) * 3 }) - return PrincipalAxes.ofPoints(Matrix.fromArray(positions, 3, elementCount)) + return PrincipalAxes.ofPositions(positions) } get stats() { diff --git a/src/mol-plugin/util/structure-orientation.ts b/src/mol-plugin/util/structure-orientation.ts index e445aec4c..c009b6663 100644 --- a/src/mol-plugin/util/structure-orientation.ts +++ b/src/mol-plugin/util/structure-orientation.ts @@ -13,11 +13,11 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { Mesh } from '../../mol-geo/geometry/mesh/mesh'; import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder'; import { Vec3, Mat4 } from '../../mol-math/linear-algebra'; -import Matrix from '../../mol-math/linear-algebra/matrix/matrix'; import { PrincipalAxes } from '../../mol-math/linear-algebra/matrix/principal-axes'; import { createCage } from '../../mol-geo/primitive/cage'; import { stringToWords } from '../../mol-util/string'; import { structureElementStatsLabel } from '../../mol-theme/label'; +import { Axes3D } from '../../mol-math/geometry'; const tmpMatrixPos = Vec3.zero() function getPositions(structure: Structure) { @@ -38,7 +38,6 @@ function getPositions(structure: Structure) { interface OrientationData { label: string principalAxes: PrincipalAxes - projectedScale: { d1a: number, d2a: number, d3a: number, d1b: number, d2b: number, d3b: number } } const OrientationVisuals = { 'principal-axes': '', 'oriented-box': '' } @@ -48,8 +47,8 @@ const OrientationVisualOptions = Object.keys(OrientationVisuals).map(name => [na export const OrientationParams = { ...Mesh.Params, visuals: PD.MultiSelect<OrientationVisualName>(['oriented-box'], OrientationVisualOptions), - orientationColor: PD.Color(ColorNames.orange), - orientationScale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }) + color: PD.Color(ColorNames.orange), + scale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }) } export type OrientationParams = typeof OrientationParams export type OrientationProps = PD.Values<OrientationParams> @@ -59,33 +58,38 @@ enum VisualGroup { OrientedBox = 2 } -function getVolume(data: OrientationData) { - const { d1a, d2a, d3a, d1b, d2b, d3b } = data.projectedScale - return (d1a - d1b) * (d2a - d2b) * (d3a - d3b) -} +const tmpAxesVec = Vec3() +function buildMomentsAxes(state: MeshBuilder.State, data: OrientationData, props: OrientationProps) { + const { origin, dirA, dirB, dirC } = data.principalAxes.momentsAxes -function buildPrincipalAxes(state: MeshBuilder.State, data: OrientationData, props: OrientationProps) { const vertices = new Float32Array(6 * 3) const edges = new Uint8Array([0, 1, 2, 3, 4, 5]) - Vec3.toArray(data.principalAxes.begA, vertices, 0) - Vec3.toArray(data.principalAxes.endA, vertices, 3) - Vec3.toArray(data.principalAxes.begB, vertices, 6) - Vec3.toArray(data.principalAxes.endB, vertices, 9) - Vec3.toArray(data.principalAxes.begC, vertices, 12) - Vec3.toArray(data.principalAxes.endC, vertices, 15) + Vec3.add(tmpAxesVec, origin, dirA) + Vec3.toArray(Vec3.add(tmpAxesVec, origin, dirA), vertices, 0) + Vec3.toArray(Vec3.sub(tmpAxesVec, origin, dirA), vertices, 3) + Vec3.toArray(Vec3.add(tmpAxesVec, origin, dirB), vertices, 6) + Vec3.toArray(Vec3.sub(tmpAxesVec, origin, dirB), vertices, 9) + Vec3.toArray(Vec3.add(tmpAxesVec, origin, dirC), vertices, 12) + Vec3.toArray(Vec3.sub(tmpAxesVec, origin, dirC), vertices, 15) - const matrix = Mat4.fromTranslation(Mat4(), Vec3.inverse(Vec3(), data.principalAxes.center)) + const matrix = Mat4.fromTranslation(Mat4(), Vec3.inverse(Vec3(), origin)) const cage = createCage(vertices, edges) - const radius = (Math.cbrt(getVolume(data)) / 300) * props.orientationScale + const volume = Axes3D.volume(data.principalAxes.boxAxes) + const radius = (Math.cbrt(volume) / 300) * props.scale state.currentGroup = VisualGroup.PrincipalAxes MeshBuilder.addCage(state, matrix, cage, radius, 2, 20) } -const tmpBoxVec = Vec3() +const tmpBoxVecCorner = Vec3() +const tmpBoxVecA = Vec3() +const tmpBoxVecB = Vec3() +const tmpBoxVecC = Vec3() function buildOrientedBox(state: MeshBuilder.State, data: OrientationData, props: OrientationProps) { - const { center, normVecA, normVecB, normVecC } = data.principalAxes - const { d1a, d2a, d3a, d1b, d2b, d3b } = data.projectedScale + const { origin, dirA, dirB, dirC } = data.principalAxes.boxAxes + const negDirA = Vec3.negate(tmpBoxVecA, dirA) + const negDirB = Vec3.negate(tmpBoxVecB, dirB) + const negDirC = Vec3.negate(tmpBoxVecC, dirC) const vertices = new Float32Array(8 * 3) const edges = new Uint8Array([ @@ -94,27 +98,28 @@ function buildOrientedBox(state: MeshBuilder.State, data: OrientationData, props ]) let offset = 0 - const addCornerHelper = function (d1: number, d2: number, d3: number) { - Vec3.copy(tmpBoxVec, center) - Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, normVecA, d1) - Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, normVecB, d2) - Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, normVecC, d3) - Vec3.toArray(tmpBoxVec, vertices, offset) + const addCornerHelper = function (v1: Vec3, v2: Vec3, v3: Vec3) { + Vec3.copy(tmpBoxVecCorner, origin) + Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v1) + Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v2) + Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v3) + Vec3.toArray(tmpBoxVecCorner, vertices, offset) offset += 3 } - addCornerHelper(d1a, d2a, d3a) - addCornerHelper(d1a, d2a, d3b) - addCornerHelper(d1a, d2b, d3b) - addCornerHelper(d1a, d2b, d3a) - addCornerHelper(d1b, d2b, d3b) - addCornerHelper(d1b, d2b, d3a) - addCornerHelper(d1b, d2a, d3a) - addCornerHelper(d1b, d2a, d3b) + addCornerHelper(dirA, dirB, dirC) + addCornerHelper(dirA, dirB, negDirC) + addCornerHelper(dirA, negDirB, negDirC) + addCornerHelper(dirA, negDirB, dirC) + addCornerHelper(negDirA, negDirB, negDirC) + addCornerHelper(negDirA, negDirB, dirC) + addCornerHelper(negDirA, dirB, dirC) + addCornerHelper(negDirA, dirB, negDirC) - const matrix = Mat4.fromTranslation(Mat4(), Vec3.inverse(Vec3(), data.principalAxes.center)) + const matrix = Mat4.fromTranslation(Mat4(), Vec3.inverse(Vec3(), origin)) const cage = createCage(vertices, edges) - const radius = (Math.cbrt(getVolume(data)) / 300) * props.orientationScale + const volume = Axes3D.volume(data.principalAxes.boxAxes) + const radius = (Math.cbrt(volume) / 300) * props.scale state.currentGroup = VisualGroup.OrientedBox MeshBuilder.addCage(state, matrix, cage, radius, 2, 20) } @@ -122,7 +127,7 @@ function buildOrientedBox(state: MeshBuilder.State, data: OrientationData, props function getOrientationMesh(data: OrientationData, props: OrientationProps, mesh?: Mesh) { const state = MeshBuilder.createState(256, 128, mesh) - if (props.visuals.includes('principal-axes')) buildPrincipalAxes(state, data, props) + if (props.visuals.includes('principal-axes')) buildMomentsAxes(state, data, props) if (props.visuals.includes('oriented-box')) buildOrientedBox(state, data, props) return MeshBuilder.getMesh(state) @@ -137,10 +142,8 @@ function getLabel(structure: Structure) { export async function getStructureOrientationRepresentation(ctx: RuntimeContext, structure: Structure, params: OrientationProps, prev?: ShapeRepresentation<OrientationData, Mesh, Mesh.Params>) { const repr = prev || ShapeRepresentation(getOrientationShape, Mesh.Utils); const label = getLabel(structure) - const positions = getPositions(structure) - const principalAxes = PrincipalAxes.ofPoints(Matrix.fromArray(positions, 3, structure.elementCount)) - const projectedScale = PrincipalAxes.getProjectedScale(positions, principalAxes) - const data: OrientationData = { label, principalAxes, projectedScale } + const principalAxes = PrincipalAxes.ofPositions(getPositions(structure)) + const data: OrientationData = { label, principalAxes } await repr.createOrUpdate(params, data).runInContext(ctx); return repr; } @@ -158,5 +161,5 @@ function getOrientationShape(ctx: RuntimeContext, data: OrientationData, props: const getLabel = function (groupId: number ) { return `${getOrientationLabel(data, groupId)} of ${data.label}` } - return Shape.create('Principal Axes', data, geo, () => props.orientationColor, () => 1, getLabel) + return Shape.create('Principal Axes', data, geo, () => props.color, () => 1, getLabel) } \ No newline at end of file -- GitLab