|
@@ -0,0 +1,108 @@
|
|
|
+/**
|
|
|
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
+ *
|
|
|
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
|
+ */
|
|
|
+
|
|
|
+import { Structure } from '../../mol-model/structure';
|
|
|
+import { ShapeRepresentation } from '../../mol-repr/shape/representation';
|
|
|
+import { Shape } from '../../mol-model/shape';
|
|
|
+import { ColorNames } from '../../mol-util/color/names';
|
|
|
+import { RuntimeContext } from '../../mol-task';
|
|
|
+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';
|
|
|
+
|
|
|
+const tmpMatrixPos = Vec3.zero()
|
|
|
+export function getPositionMatrix(structure: Structure) {
|
|
|
+ const mat = Matrix.create(3, structure.elementCount)
|
|
|
+ for (let i = 0, m = 0, il = structure.units.length; i < il; ++i) {
|
|
|
+ const unit = structure.units[i]
|
|
|
+ const { elements } = unit
|
|
|
+ const pos = unit.conformation.position
|
|
|
+ for (let j = 0, jl = elements.length; j < jl; ++j) {
|
|
|
+ pos(elements[j], tmpMatrixPos)
|
|
|
+ Vec3.toArray(tmpMatrixPos, mat.data, m + j * 3)
|
|
|
+ }
|
|
|
+ m += elements.length * 3
|
|
|
+ }
|
|
|
+ return mat
|
|
|
+}
|
|
|
+
|
|
|
+interface OrientationData {
|
|
|
+ principalAxes: PrincipalAxes
|
|
|
+}
|
|
|
+
|
|
|
+const OrientationVisuals = { 'principal-axes': '', 'oriented-box': '' }
|
|
|
+type OrientationVisualName = keyof typeof OrientationVisuals
|
|
|
+const OrientationVisualOptions = Object.keys(OrientationVisuals).map(name => [name, stringToWords(name)] as [OrientationVisualName, string])
|
|
|
+
|
|
|
+export const OrientationParams = {
|
|
|
+ ...Mesh.Params,
|
|
|
+ visuals: PD.MultiSelect<OrientationVisualName>(['principal-axes'], OrientationVisualOptions),
|
|
|
+ orientationColor: PD.Color(ColorNames.orange),
|
|
|
+ orientationScale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 })
|
|
|
+}
|
|
|
+export type OrientationParams = typeof OrientationParams
|
|
|
+export type OrientationProps = PD.Values<OrientationParams>
|
|
|
+
|
|
|
+enum VisualGroup {
|
|
|
+ PrincipalAxes = 1,
|
|
|
+ OrientedBox = 2
|
|
|
+}
|
|
|
+
|
|
|
+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)
|
|
|
+
|
|
|
+ const matrix = Mat4.fromTranslation(Mat4(), Vec3.inverse(Vec3(), data.principalAxes.center))
|
|
|
+
|
|
|
+ const cage = createCage(vertices, edges)
|
|
|
+ const radius = 0.3 * props.orientationScale
|
|
|
+ state.currentGroup = VisualGroup.PrincipalAxes
|
|
|
+ MeshBuilder.addCage(state, matrix, cage, radius, 2, 20)
|
|
|
+}
|
|
|
+
|
|
|
+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)
|
|
|
+
|
|
|
+ return MeshBuilder.getMesh(state)
|
|
|
+}
|
|
|
+
|
|
|
+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))
|
|
|
+ }
|
|
|
+ await repr.createOrUpdate(params, data).runInContext(ctx);
|
|
|
+ return repr;
|
|
|
+}
|
|
|
+
|
|
|
+function getOrientationLabel(data: OrientationData, groupId: number): string {
|
|
|
+ switch (groupId) {
|
|
|
+ case VisualGroup.PrincipalAxes: return 'Principal Axes'
|
|
|
+ case VisualGroup.OrientedBox: return 'Oriented Box'
|
|
|
+ }
|
|
|
+ return 'Unknown Orientation Visual'
|
|
|
+}
|
|
|
+
|
|
|
+function getOrientationShape(ctx: RuntimeContext, data: OrientationData, props: OrientationProps, shape?: Shape<Mesh>) {
|
|
|
+ const geo = getOrientationMesh(data, props, shape && shape.geometry);
|
|
|
+ const getLabel = function (groupId: number ) {
|
|
|
+ return getOrientationLabel(data, groupId)
|
|
|
+ }
|
|
|
+ return Shape.create('Principal Axes', data, geo, () => props.orientationColor, () => 1, getLabel)
|
|
|
+}
|