diff --git a/src/mol-geo/primitive/box.ts b/src/mol-geo/primitive/box.ts index 45a35ebea43fef38f413f23132372e8b7bac3eef..9ba23149f07dfba0daea75f5656101119a272145 100644 --- a/src/mol-geo/primitive/box.ts +++ b/src/mol-geo/primitive/box.ts @@ -17,7 +17,7 @@ export type BoxProps = Partial<typeof DefaultBoxProps> const tmpVector = Vec3.zero(); -export default function Box(props?: BoxProps) { +export function Box(props?: BoxProps) { const { width, height, depth } = { ...DefaultBoxProps, ...props } // buffers diff --git a/src/mol-geo/primitive/cylinder.ts b/src/mol-geo/primitive/cylinder.ts index cb49550f6478b8db8b8667aac6b448f3f46352a3..a17cc3ff8d68539e0f19c4365b82ab6d967f7acf 100644 --- a/src/mol-geo/primitive/cylinder.ts +++ b/src/mol-geo/primitive/cylinder.ts @@ -21,7 +21,7 @@ export const DefaultCylinderProps = { } export type CylinderProps = Partial<typeof DefaultCylinderProps> -export default function Cylinder(props?: CylinderProps) { +export function Cylinder(props?: CylinderProps) { const { radiusTop, radiusBottom, height, radialSegments, heightSegments, topCap, bottomCap, thetaStart, thetaLength } = { ...DefaultCylinderProps, ...props }; // buffers diff --git a/src/mol-geo/primitive/icosahedron.ts b/src/mol-geo/primitive/icosahedron.ts index 07e544e07fcfdd7cddb36940173f2841c58da169..d9383aa0fe1dc119ec344dc351b1d80e098c6ea6 100644 --- a/src/mol-geo/primitive/icosahedron.ts +++ b/src/mol-geo/primitive/icosahedron.ts @@ -6,7 +6,7 @@ // adapted from three.js, MIT License Copyright 2010-2018 three.js authors -import Polyhedron from './polyhedron' +import { Polyhedron } from './polyhedron' const t = ( 1 + Math.sqrt( 5 ) ) / 2; @@ -33,6 +33,6 @@ export const DefaultIcosahedronProps = { } export type IcosahedronProps = Partial<typeof DefaultIcosahedronProps> -export default function Icosahedron(props?: IcosahedronProps) { +export function Icosahedron(props?: IcosahedronProps) { return Polyhedron(vertices, indices, { ...DefaultIcosahedronProps, ...props }) } \ No newline at end of file diff --git a/src/mol-geo/primitive/plane.ts b/src/mol-geo/primitive/plane.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6e02744074056da32d3627050132a0e9d1c9267 --- /dev/null +++ b/src/mol-geo/primitive/plane.ts @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +export const DefaultPlaneProps = { + width: 1, + height: 1 +} +export type PlaneProps = Partial<typeof DefaultPlaneProps> + +export function Plane(props?: PlaneProps) { + const { width, height } = { ...DefaultPlaneProps, ...props } + + return { + vertices: new Float32Array([ + -width / 2, height / 2, 0, + width / 2, height / 2, 0, + -width / 2, -height / 2, 0, + width / 2, -height / 2, 0 + ]), + normals: new Float32Array([ + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + 0, 0, 1 + ]), + indices: new Uint32Array([ + 0, 2, 1, + 1, 2, 3 + ]) + } +} \ No newline at end of file diff --git a/src/mol-geo/primitive/polyhedron.ts b/src/mol-geo/primitive/polyhedron.ts index 558cce08d59c2e61618855a2bfb5697e386e0eea..d5818b6a77ff30a2f9ae72d2e416cec4dc112655 100644 --- a/src/mol-geo/primitive/polyhedron.ts +++ b/src/mol-geo/primitive/polyhedron.ts @@ -15,7 +15,7 @@ export const DefaultPolyhedronProps = { } export type PolyhedronProps = Partial<typeof DefaultPolyhedronProps> -export default function Polyhedron(_vertices: Helpers.NumberArray, _indices: Helpers.NumberArray, props?: PolyhedronProps) { +export function Polyhedron(_vertices: Helpers.NumberArray, _indices: Helpers.NumberArray, props?: PolyhedronProps) { const { radius, detail } = { ...DefaultPolyhedronProps, ...props } const builder = createBuilder() const { vertices, indices } = builder diff --git a/src/mol-geo/primitive/wedge.ts b/src/mol-geo/primitive/wedge.ts new file mode 100644 index 0000000000000000000000000000000000000000..f15642e1e428e824d8efed00e51c7599112e5224 --- /dev/null +++ b/src/mol-geo/primitive/wedge.ts @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Vec3 } from 'mol-math/linear-algebra' + +export const DefaultWedgeProps = { + width: 1, + height: 1, + depth: 1 +} +export type WedgeProps = Partial<typeof DefaultWedgeProps> + +const _a = Vec3.create(0, 0.5, 0.5) +const _b = Vec3.create(0.5, -0.5, 0.5) +const _c = Vec3.create(-0.5, -0.5, 0.5) +const _d = Vec3.create(0, 0.5, -0.5) +const _e = Vec3.create(0.5, -0.5, -0.5) +const _f = Vec3.create(-0.5, -0.5, -0.5) + +const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero() +const d = Vec3.zero(), e = Vec3.zero(), f = Vec3.zero() + +const nabc = Vec3.create(0, 0, 1) +const ndef = Vec3.create(0, 0, -1) +const nabde = Vec3.zero() +const nbcef = Vec3.create(0, -1, 0) +const nacdf = Vec3.zero() + +const s = Vec3.zero() + +export function Wedge(props?: WedgeProps) { + const { width, height, depth } = { ...DefaultWedgeProps, ...props } + + const vertices = new Float32Array(54) + const normals = new Float32Array(54) + const indices = new Uint32Array(24) + + Vec3.set(s, width, height, depth) + Vec3.mul(a, _a, s); Vec3.mul(b, _b, s); Vec3.mul(c, _c, s) + Vec3.mul(d, _d, s); Vec3.mul(e, _e, s); Vec3.mul(f, _f, s) + + Vec3.sub(nabde, b, a) + Vec3.normalize(nabde, Vec3.set(nabde, -nabde[1], nabde[0], 0)) + Vec3.sub(nacdf, c, a) + Vec3.normalize(nacdf, Vec3.set(nacdf, nacdf[1], -nacdf[0], 0)) + + let vc = 0 + let ic = 0 + + // abc + Vec3.toArray(a, vertices, vc + 0) + Vec3.toArray(c, vertices, vc + 3) + Vec3.toArray(b, vertices, vc + 6) + for (let i = 0; i < 3; ++i) Vec3.toArray(nabc, normals, vc + i * 3) + indices[ic + 0] = vc / 3 + 0 + indices[ic + 1] = vc / 3 + 1 + indices[ic + 2] = vc / 3 + 2 + vc += 9 + ic += 3 + + // def + Vec3.toArray(d, vertices, vc + 0) + Vec3.toArray(e, vertices, vc + 3) + Vec3.toArray(f, vertices, vc + 6) + for (let i = 0; i < 3; ++i) Vec3.toArray(ndef, normals, vc + i * 3) + indices[ic + 0] = vc / 3 + 0 + indices[ic + 1] = vc / 3 + 1 + indices[ic + 2] = vc / 3 + 2 + vc += 9 + ic += 3 + + // abde + Vec3.toArray(a, vertices, vc + 0) + Vec3.toArray(d, vertices, vc + 3) + Vec3.toArray(e, vertices, vc + 6) + Vec3.toArray(b, vertices, vc + 9) + for (let i = 0; i < 4; ++i) Vec3.toArray(nabde, normals, vc + i * 3) + indices[ic + 0] = vc / 3 + 2 + indices[ic + 1] = vc / 3 + 1 + indices[ic + 2] = vc / 3 + 0 + indices[ic + 3] = vc / 3 + 0 + indices[ic + 4] = vc / 3 + 3 + indices[ic + 5] = vc / 3 + 2 + vc += 12 + ic += 6 + + // acdf + Vec3.toArray(d, vertices, vc + 0) + Vec3.toArray(a, vertices, vc + 3) + Vec3.toArray(c, vertices, vc + 6) + Vec3.toArray(f, vertices, vc + 9) + for (let i = 0; i < 4; ++i) Vec3.toArray(nacdf, normals, vc + i * 3) + indices[ic + 0] = vc / 3 + 2 + indices[ic + 1] = vc / 3 + 1 + indices[ic + 2] = vc / 3 + 0 + indices[ic + 3] = vc / 3 + 0 + indices[ic + 4] = vc / 3 + 3 + indices[ic + 5] = vc / 3 + 2 + vc += 12 + ic += 6 + + // bcef + Vec3.toArray(e, vertices, vc + 0) + Vec3.toArray(f, vertices, vc + 3) + Vec3.toArray(c, vertices, vc + 6) + Vec3.toArray(b, vertices, vc + 9) + for (let i = 0; i < 4; ++i) Vec3.toArray(nbcef, normals, vc + i * 3) + indices[ic + 0] = vc / 3 + 2 + indices[ic + 1] = vc / 3 + 1 + indices[ic + 2] = vc / 3 + 0 + indices[ic + 3] = vc / 3 + 0 + indices[ic + 4] = vc / 3 + 3 + indices[ic + 5] = vc / 3 + 2 + vc += 12 + ic += 6 + + return { vertices, normals, indices } +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts index 271782dbcc2ef96c39780c838e5af20c33691a6c..95292334e241d173939257de1e73a4624607591a 100644 --- a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts +++ b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts @@ -24,17 +24,24 @@ import { SizeTheme } from '../../../theme'; import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util'; import { MeshBuilder } from '../../../shape/mesh-builder'; import { getPolymerElementCount, PolymerTraceIterator } from './util/polymer'; -import { Vec3 } from 'mol-math/linear-algebra'; +import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types'; -// import { radToDeg } from 'mol-math/misc'; +import { degToRad } from 'mol-math/misc'; + +// TODO handle polymer ends properly +// TODO avoid allocating Vec3, use global temp vars const tmpNormal = Vec3.zero() const tangentVec = Vec3.zero() const normalVec = Vec3.zero() const binormalVec = Vec3.zero() - const prevNormal = Vec3.zero() +const t = Mat4.identity() +const rotX90 = Mat4.fromRotation(Mat4.identity(), degToRad(90), Vec3.create(1, 0, 0)) +const rotY90 = Mat4.fromRotation(Mat4.identity(), degToRad(90), Vec3.create(0, 1, 0)) +const rotXY90 = Mat4.mul(Mat4.identity(), rotX90, rotY90) + const orthogonalizeTmpVec = Vec3.zero() /** Get a vector that is similar to b but orthogonal to a */ function orthogonalize(out: Vec3, a: Vec3, b: Vec3) { @@ -45,13 +52,9 @@ function orthogonalize(out: Vec3, a: Vec3, b: Vec3) { function interpolateNormals(controlPoints: Helpers.NumberArray, tangentVectors: Helpers.NumberArray, normalVectors: Helpers.NumberArray, binormalVectors: Helpers.NumberArray, firstNormalVector: Vec3, lastNormalVector: Vec3) { const n = controlPoints.length / 3 - // const n1 = n - 1 - // const angle = radToDeg(Math.acos(Vec3.dot(firstNormalVector, lastNormalVector))) - // console.log('angle', angle) if (Vec3.dot(firstNormalVector, lastNormalVector) < 0) { Vec3.scale(lastNormalVector, lastNormalVector, -1) - // console.log('flipped last normal vector') } Vec3.copy(prevNormal, firstNormalVector) @@ -65,13 +68,6 @@ function interpolateNormals(controlPoints: Helpers.NumberArray, tangentVectors: orthogonalize(normalVec, tangentVec, tmpNormal) Vec3.toArray(normalVec, normalVectors, i * 3) - // const deltaAngle = radToDeg(Math.acos(Vec3.dot(prevNormal, normalVec))) - // if (deltaAngle > (angle / n1) * 5 && deltaAngle > 20) { - // console.warn(i, 'large delta angle', deltaAngle) - // } - // if (Vec3.dot(normalVec, prevNormal) < 0) { - // console.warn(i, 'flip compared to prev', radToDeg(Math.acos(Vec3.dot(prevNormal, normalVec)))) - // } Vec3.copy(prevNormal, normalVec) Vec3.normalize(binormalVec, Vec3.cross(binormalVec, tangentVec, normalVec)) @@ -107,11 +103,11 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me const polymerTraceIt = PolymerTraceIterator(unit) while (polymerTraceIt.hasNext) { const v = polymerTraceIt.move() - // builder.setId(elements[v.center.element]) builder.setId(v.center.element) const isNucleic = v.moleculeType === MoleculeType.DNA || v.moleculeType === MoleculeType.RNA const isSheet = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta) + const isHelix = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Helix) const tension = (isNucleic || isSheet) ? 0.5 : 0.9 for (let j = 0; j <= linearSegments; ++j) { @@ -198,7 +194,7 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me const arrowHeight = v.secStrucChange ? 1.7 : 0 builder.addSheet(controlPoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, true, true) } else { - if (SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Helix)) { + if (isHelix) { width = 0.2; height = 1.0 } else if (isNucleic) { width = 1.5; height = 0.3 @@ -206,6 +202,22 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me builder.addTube(controlPoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true) } + if ((isSheet && !v.secStrucChange) || !isSheet) { + const upVec = Vec3.zero() + let width = 0.5, height = 1.2, depth = 0.6 + if (isNucleic) { + Vec3.fromArray(upVec, binormalVectors, Math.round(linearSegments / 2) * 3) + depth = 0.9 + } else { + Vec3.fromArray(upVec, normalVectors, Math.round(linearSegments / 2) * 3) + } + + Mat4.targetTo(t, v.t3, v.t1, upVec) + Mat4.mul(t, t, rotXY90) + Mat4.setTranslation(t, v.t2) + builder.addWedge(t, { width, height, depth }) + } + if (i % 10000 === 0 && ctx.shouldUpdate) { await ctx.update({ message: 'Polymer trace mesh', current: i, max: polymerElementCount }); } diff --git a/src/mol-geo/shape/mesh-builder.ts b/src/mol-geo/shape/mesh-builder.ts index 306670182c020c003780b71cc021b0b3ed7d6bf3..42f5383b828a1d55647c0b18986a84c8f529bb11 100644 --- a/src/mol-geo/shape/mesh-builder.ts +++ b/src/mol-geo/shape/mesh-builder.ts @@ -8,9 +8,11 @@ import { ValueCell } from 'mol-util/value-cell' import { Vec3, Mat4, Mat3 } from 'mol-math/linear-algebra'; import { ChunkedArray } from 'mol-data/util'; -import Box, { BoxProps } from '../primitive/box'; -import Cylinder, { CylinderProps } from '../primitive/cylinder'; -import Icosahedron, { IcosahedronProps } from '../primitive/icosahedron'; +import { Box, BoxProps } from '../primitive/box'; +import { Plane, PlaneProps } from '../primitive/plane'; +import { Wedge, WedgeProps } from '../primitive/wedge'; +import { Cylinder, CylinderProps } from '../primitive/cylinder'; +import { Icosahedron, IcosahedronProps } from '../primitive/icosahedron'; import { Mesh } from './mesh'; import { getNormalMatrix } from '../util'; import { addSheet } from '../primitive/sheet'; @@ -31,6 +33,8 @@ export interface MeshBuilderState { export interface MeshBuilder { add(t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices?: Uint32Array): void addBox(t: Mat4, props?: BoxProps): void + addPlane(t: Mat4, props?: PlaneProps): void + addWedge(t: Mat4, props?: WedgeProps): void addCylinder(start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps): void addDoubleCylinder(start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps): void addFixedCountDashedCylinder(start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: CylinderProps): void @@ -128,8 +132,16 @@ export namespace MeshBuilder { return { add, addBox: (t: Mat4, props?: BoxProps) => { - const box = Box(props) - add(t, box.vertices, box.normals, box.indices) + const { vertices, normals, indices } = Box(props) + add(t, vertices, normals, indices) + }, + addPlane: (t: Mat4, props?: PlaneProps) => { + const { vertices, normals, indices } = Plane(props) + add(t, vertices, normals, indices) + }, + addWedge: (t: Mat4, props?: WedgeProps) => { + const { vertices, normals, indices } = Wedge(props) + add(t, vertices, normals, indices) }, addCylinder: (start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps) => { const d = Vec3.distance(start, end) * lengthScale diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts index 3f7e29c69e5b2ce52d280c52fdacb7870e4cfeef..ddd499c4b2d34ae291c606beacb9f196791a6b25 100644 --- a/src/mol-view/stage.ts +++ b/src/mol-view/stage.ts @@ -81,9 +81,9 @@ export class Stage { // this.loadPdbid('4v5a') // ribosome // this.loadPdbid('3j3q') // ... // this.loadPdbid('2np2') // dna - this.loadPdbid('1d66') // dna + // this.loadPdbid('1d66') // dna // this.loadPdbid('9dna') // A form dna - // this.loadPdbid('1bna') // B form dna + this.loadPdbid('1bna') // B form dna // this.loadPdbid('199d') // C form dna // this.loadPdbid('4lb6') // Z form dna // this.loadPdbid('1egk') // 4-way dna-rna junction