|
@@ -13,11 +13,11 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
|
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
|
|
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
|
|
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
|
|
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
|
|
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
|
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 { PrincipalAxes } from '../../mol-math/linear-algebra/matrix/principal-axes';
|
|
import { createCage } from '../../mol-geo/primitive/cage';
|
|
import { createCage } from '../../mol-geo/primitive/cage';
|
|
import { stringToWords } from '../../mol-util/string';
|
|
import { stringToWords } from '../../mol-util/string';
|
|
import { structureElementStatsLabel } from '../../mol-theme/label';
|
|
import { structureElementStatsLabel } from '../../mol-theme/label';
|
|
|
|
+import { Axes3D } from '../../mol-math/geometry';
|
|
|
|
|
|
const tmpMatrixPos = Vec3.zero()
|
|
const tmpMatrixPos = Vec3.zero()
|
|
function getPositions(structure: Structure) {
|
|
function getPositions(structure: Structure) {
|
|
@@ -38,7 +38,6 @@ function getPositions(structure: Structure) {
|
|
interface OrientationData {
|
|
interface OrientationData {
|
|
label: string
|
|
label: string
|
|
principalAxes: PrincipalAxes
|
|
principalAxes: PrincipalAxes
|
|
- projectedScale: { d1a: number, d2a: number, d3a: number, d1b: number, d2b: number, d3b: number }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
const OrientationVisuals = { 'principal-axes': '', 'oriented-box': '' }
|
|
const OrientationVisuals = { 'principal-axes': '', 'oriented-box': '' }
|
|
@@ -48,8 +47,8 @@ const OrientationVisualOptions = Object.keys(OrientationVisuals).map(name => [na
|
|
export const OrientationParams = {
|
|
export const OrientationParams = {
|
|
...Mesh.Params,
|
|
...Mesh.Params,
|
|
visuals: PD.MultiSelect<OrientationVisualName>(['oriented-box'], OrientationVisualOptions),
|
|
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 OrientationParams = typeof OrientationParams
|
|
export type OrientationProps = PD.Values<OrientationParams>
|
|
export type OrientationProps = PD.Values<OrientationParams>
|
|
@@ -59,33 +58,38 @@ enum VisualGroup {
|
|
OrientedBox = 2
|
|
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 vertices = new Float32Array(6 * 3)
|
|
const edges = new Uint8Array([0, 1, 2, 3, 4, 5])
|
|
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 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
|
|
state.currentGroup = VisualGroup.PrincipalAxes
|
|
MeshBuilder.addCage(state, matrix, cage, radius, 2, 20)
|
|
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) {
|
|
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 vertices = new Float32Array(8 * 3)
|
|
const edges = new Uint8Array([
|
|
const edges = new Uint8Array([
|
|
@@ -94,27 +98,28 @@ function buildOrientedBox(state: MeshBuilder.State, data: OrientationData, props
|
|
])
|
|
])
|
|
|
|
|
|
let offset = 0
|
|
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
|
|
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 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
|
|
state.currentGroup = VisualGroup.OrientedBox
|
|
MeshBuilder.addCage(state, matrix, cage, radius, 2, 20)
|
|
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) {
|
|
function getOrientationMesh(data: OrientationData, props: OrientationProps, mesh?: Mesh) {
|
|
const state = MeshBuilder.createState(256, 128, 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)
|
|
if (props.visuals.includes('oriented-box')) buildOrientedBox(state, data, props)
|
|
|
|
|
|
return MeshBuilder.getMesh(state)
|
|
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>) {
|
|
export async function getStructureOrientationRepresentation(ctx: RuntimeContext, structure: Structure, params: OrientationProps, prev?: ShapeRepresentation<OrientationData, Mesh, Mesh.Params>) {
|
|
const repr = prev || ShapeRepresentation(getOrientationShape, Mesh.Utils);
|
|
const repr = prev || ShapeRepresentation(getOrientationShape, Mesh.Utils);
|
|
const label = getLabel(structure)
|
|
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);
|
|
await repr.createOrUpdate(params, data).runInContext(ctx);
|
|
return repr;
|
|
return repr;
|
|
}
|
|
}
|
|
@@ -158,5 +161,5 @@ function getOrientationShape(ctx: RuntimeContext, data: OrientationData, props:
|
|
const getLabel = function (groupId: number ) {
|
|
const getLabel = function (groupId: number ) {
|
|
return `${getOrientationLabel(data, groupId)} of ${data.label}`
|
|
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)
|
|
}
|
|
}
|