From e7a0ba8db09187703507bfc69b10d34011e501fb Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Wed, 29 Aug 2018 18:10:36 -0700 Subject: [PATCH] wip, better boundary calculation, making use of transformations --- src/apps/canvas/structure-view.ts | 64 ++++++++-- .../structure/structure/util/boundary.ts | 110 +++++++++++++++++- 2 files changed, 159 insertions(+), 15 deletions(-) diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index 13f8b3f16..912f0090b 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -12,6 +12,13 @@ import { AssemblySymmetry } from 'mol-model-props/rcsb/symmetry'; import { ShapeRepresentation, ShapeProps } from 'mol-geo/representation/shape'; import { getAxesShape } from './assembly-symmetry'; import Viewer from 'mol-view/viewer'; +import { CarbohydrateRepresentation } from 'mol-geo/representation/structure/representation/carbohydrate'; +import { MeshBuilder } from 'mol-geo/mesh/mesh-builder'; +import { addSphere } from 'mol-geo/mesh/builder/sphere'; +import { Shape } from 'mol-model/shape'; +import { Color } from 'mol-util/color'; +import { computeUnitBoundary } from 'mol-model/structure/structure/util/boundary'; +import { addBoundingBox } from 'mol-geo/mesh/builder/bounding-box'; export interface StructureView { readonly label: string @@ -21,7 +28,8 @@ export interface StructureView { readonly cartoon: CartoonRepresentation readonly ballAndStick: BallAndStickRepresentation - readonly axes: ShapeRepresentation<ShapeProps> + readonly carbohydrate: CarbohydrateRepresentation + readonly symmetryAxes: ShapeRepresentation<ShapeProps> readonly modelId: number readonly assemblyId: string @@ -45,7 +53,9 @@ interface StructureViewProps { export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model>, props: StructureViewProps = {}): Promise<StructureView> { const cartoon = CartoonRepresentation() const ballAndStick = BallAndStickRepresentation() - const axes = ShapeRepresentation() + const carbohydrate = CarbohydrateRepresentation() + const symmetryAxes = ShapeRepresentation() + const polymerSphere = ShapeRepresentation() let label: string let model: Model | undefined @@ -156,14 +166,46 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> useFog: false // TODO fog not working properly }, structure).run() + await carbohydrate.createOrUpdate({ + colorTheme: { name: 'carbohydrate-symbol' }, + sizeTheme: { name: 'uniform', value: 1, factor: 1 }, + useFog: false // TODO fog not working properly + }, structure).run() + viewer.center(structure.boundary.sphere.center) + + const mb = MeshBuilder.create() + mb.setGroup(0) + addSphere(mb, structure.boundary.sphere.center, structure.boundary.sphere.radius, 3) + addBoundingBox(mb, structure.boundary.box, 1, 2, 8) + for (let i = 0, il = structure.units.length; i < il; ++i) { + mb.setGroup(1) + const u = structure.units[i] + const ci = u.model.atomicHierarchy.chainAtomSegments.index[u.elements[0]] + const ek = u.model.atomicHierarchy.getEntityKey(ci) + if (u.model.entities.data.type.value(ek) === 'water') continue + const boundary = computeUnitBoundary(u) + addSphere(mb, boundary.sphere.center, boundary.sphere.radius, 3) + addBoundingBox(mb, boundary.box, 0.5, 2, 8) + } + const shape = Shape.create('boundary', mb.getMesh(), [Color(0xCC6633), Color(0x3366CC)], ['sphere boundary']) + await polymerSphere.createOrUpdate({ + alpha: 0.5, + doubleSided: false, + depthMask: false, + useFog: false // TODO fog not working properly + }, shape).run() } else { cartoon.destroy() ballAndStick.destroy() + carbohydrate.destroy() + polymerSphere.destroy() } viewer.add(cartoon) viewer.add(ballAndStick) + viewer.add(carbohydrate) + viewer.add(polymerSphere) } async function createSymmetryRepr() { @@ -173,21 +215,21 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> const axesShape = getAxesShape(symmetryFeatureId, assemblySymmetry) if (axesShape) { // getClusterColorTheme(symmetryFeatureId, assemblySymmetry) - await axes.createOrUpdate({ + await symmetryAxes.createOrUpdate({ colorTheme: { name: 'shape-group' }, // colorTheme: { name: 'uniform', value: Color(0xFFCC22) }, useFog: false // TODO fog not working properly }, axesShape).run() } else { - axes.destroy() + symmetryAxes.destroy() } } else { - axes.destroy() + symmetryAxes.destroy() } } else { - axes.destroy() + symmetryAxes.destroy() } - viewer.add(axes) + viewer.add(symmetryAxes) viewer.requestDraw() } @@ -201,7 +243,8 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> cartoon, ballAndStick, - axes, + carbohydrate, + symmetryAxes, get modelId() { return modelId }, get assemblyId() { return assemblyId }, @@ -217,12 +260,13 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> destroy: () => { viewer.remove(cartoon) viewer.remove(ballAndStick) - viewer.remove(axes) + viewer.remove(carbohydrate) + viewer.remove(symmetryAxes) viewer.requestDraw() cartoon.destroy() ballAndStick.destroy() - axes.destroy() + symmetryAxes.destroy() } } } diff --git a/src/mol-model/structure/structure/util/boundary.ts b/src/mol-model/structure/structure/util/boundary.ts index 2a595f8cd..ec3821263 100644 --- a/src/mol-model/structure/structure/util/boundary.ts +++ b/src/mol-model/structure/structure/util/boundary.ts @@ -2,13 +2,115 @@ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import Structure from '../structure' -import { Box3D, Sphere3D } from 'mol-math/geometry'; +import Unit from '../unit'; +import { Box3D, Sphere3D, SymmetryOperator } from 'mol-math/geometry'; import { Vec3 } from 'mol-math/linear-algebra'; +import { SortedArray } from 'mol-data/int'; +import { ElementIndex } from '../../model/indexing'; -function computeStructureBoundary(s: Structure): { box: Box3D, sphere: Sphere3D } { +export type Boundary = { box: Box3D, sphere: Sphere3D } + +function computeElementsPositionBoundary(elements: SortedArray<ElementIndex>, position: SymmetryOperator.CoordinateMapper): Boundary { + 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) + const center = Vec3.zero() + + let radiusSq = 0 + let size = 0 + + const p = Vec3.zero() + + size += elements.length + for (let j = 0, _j = elements.length; j < _j; j++) { + position(elements[j], p) + Vec3.min(min, min, p) + Vec3.max(max, max, p) + Vec3.add(center, center, p) + } + + if (size > 0) Vec3.scale(center, center, 1/size) + + for (let j = 0, _j = elements.length; j < _j; j++) { + position(elements[j], p) + const d = Vec3.squaredDistance(p, center) + if (d > radiusSq) radiusSq = d; + } + + return { + box: { min, max }, + sphere: { center, radius: Math.sqrt(radiusSq) } + }; +} + +function computeInvariantUnitBoundary(u: Unit): Boundary { + return computeElementsPositionBoundary(u.elements, u.conformation.invariantPosition) +} + +export function computeUnitBoundary(u: Unit): Boundary { + return computeElementsPositionBoundary(u.elements, u.conformation.position) +} + +const tmpBox = Box3D.empty() +const tmpSphere = Sphere3D.zero() + +export function computeStructureBoundary(s: Structure): Boundary { + 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) + const center = Vec3.zero() + + const { units } = s; + + const boundaryMap: Map<number, Boundary> = new Map() + function getInvariantBoundary(u: Unit) { + let boundary: Boundary + if (boundaryMap.has(u.invariantId)) { + boundary = boundaryMap.get(u.invariantId)! + } else { + boundary = computeInvariantUnitBoundary(u) + boundaryMap.set(u.invariantId, boundary) + } + return boundary + } + + let radiusSq = 0; + let size = 0; + + for (let i = 0, _i = units.length; i < _i; i++) { + size += 1 + const u = units[i] + const invariantBoundary = getInvariantBoundary(u) + const m = u.conformation.operator.matrix + Box3D.transform(tmpBox, invariantBoundary.box, m) + Vec3.min(min, min, tmpBox.min) + Vec3.max(max, max, tmpBox.max) + Sphere3D.transform(tmpSphere, invariantBoundary.sphere, m) + Vec3.add(center, center, tmpSphere.center) + } + + if (size > 0) Vec3.scale(center, center, 1/size) + + for (let i = 0, _i = units.length; i < _i; i++) { + const u = units[i] + const invariantBoundary = getInvariantBoundary(u) + const m = u.conformation.operator.matrix + Sphere3D.transform(tmpSphere, invariantBoundary.sphere, m) + const d = Vec3.squaredDistance(tmpSphere.center, center) + (tmpSphere.radius * tmpSphere.radius) * 4 + if (d > radiusSq) radiusSq = d + } + + const b = { + box: { min, max }, + sphere: { center, radius: Math.sqrt(radiusSq) } + }; + console.log(b, computeStructureBoundary2(s)) + return b +} + +export function computeStructureBoundary2(s: Structure): Boundary { const min = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE]; const max = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE]; @@ -62,6 +164,4 @@ function computeStructureBoundary(s: Structure): { box: Box3D, sphere: Sphere3D box: { min: Vec3.ofArray(min), max: Vec3.ofArray(max) }, sphere: { center: Vec3.create(cx, cy, cz), radius: Math.sqrt(radiusSq) } }; -} - -export { computeStructureBoundary } \ No newline at end of file +} \ No newline at end of file -- GitLab