|
@@ -8,7 +8,7 @@ import { ValueCell } from 'mol-util/value-cell'
|
|
|
|
|
|
import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
|
|
|
import { Unit, Element } from 'mol-model/structure';
|
|
|
-import { DefaultStructureProps, UnitsVisual } from '../index';
|
|
|
+import { DefaultStructureProps, UnitsVisual } from '..';
|
|
|
import { RuntimeContext } from 'mol-task'
|
|
|
import { createTransforms, createColors } from './util/common';
|
|
|
import { markElement } from './util/element';
|
|
@@ -25,11 +25,58 @@ import { createMeshValues, updateMeshValues, updateRenderableState, createRender
|
|
|
import { MeshBuilder } from '../../../shape/mesh-builder';
|
|
|
import { getPolymerElementCount, PolymerTraceIterator } from './util/polymer';
|
|
|
import { Vec3 } from 'mol-math/linear-algebra';
|
|
|
+import { SecondaryStructureType } from 'mol-model/structure/model/types';
|
|
|
+// import { radToDeg } from 'mol-math/misc';
|
|
|
+
|
|
|
+const tmpNormal = Vec3.zero()
|
|
|
+const tangentVec = Vec3.zero()
|
|
|
+const normalVec = Vec3.zero()
|
|
|
+const binormalVec = Vec3.zero()
|
|
|
+
|
|
|
+const prevNormal = Vec3.zero()
|
|
|
+
|
|
|
+const orthogonalizeTmpVec = Vec3.zero()
|
|
|
+/** Get a vector that is similar to b but orthogonal to a */
|
|
|
+function orthogonalize(out: Vec3, a: Vec3, b: Vec3) {
|
|
|
+ Vec3.normalize(orthogonalizeTmpVec, Vec3.cross(orthogonalizeTmpVec, a, b))
|
|
|
+ Vec3.normalize(out, Vec3.cross(out, orthogonalizeTmpVec, a))
|
|
|
+ return out
|
|
|
+}
|
|
|
+
|
|
|
+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)
|
|
|
+
|
|
|
+ for (let i = 0; i < n; ++i) {
|
|
|
+ const t = i === 0 ? 0 : 1 / (n - i)
|
|
|
+ Vec3.normalize(tmpNormal, Vec3.slerp(tmpNormal, prevNormal, lastNormalVector, t))
|
|
|
+
|
|
|
+ Vec3.fromArray(tangentVec, tangentVectors, i * 3)
|
|
|
|
|
|
-export function reflect(target: Vec3, p1: Vec3, p2: Vec3, amount: number) {
|
|
|
- target[0] = p1[0] - amount * (p2[0] - p1[0])
|
|
|
- target[1] = p1[1] - amount * (p2[1] - p1[1])
|
|
|
- target[2] = p1[2] - amount * (p2[2] - p1[2])
|
|
|
+ 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))
|
|
|
+ Vec3.toArray(binormalVec, binormalVectors, i * 3)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
|
|
@@ -39,26 +86,23 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me
|
|
|
|
|
|
// TODO better vertex count estimates
|
|
|
const builder = MeshBuilder.create(polymerElementCount * 30, polymerElementCount * 30 / 2, mesh)
|
|
|
- const linearSegments = 5
|
|
|
- const radialSegments = 8
|
|
|
+ const linearSegments = 12
|
|
|
+ const radialSegments = 16
|
|
|
const tension = 0.9
|
|
|
|
|
|
- const tA = Vec3.zero()
|
|
|
+ const tanA = Vec3.zero()
|
|
|
+ const tanB = Vec3.zero()
|
|
|
+
|
|
|
const tB = Vec3.zero()
|
|
|
- const dA = Vec3.zero()
|
|
|
- const dB = Vec3.zero()
|
|
|
- const torsionVec = Vec3.zero()
|
|
|
- const initialTorsionVec = Vec3.zero()
|
|
|
const tangentVec = Vec3.zero()
|
|
|
- const normalVec = Vec3.zero()
|
|
|
|
|
|
const tmp = Vec3.zero()
|
|
|
- const reflectedControlPoint = Vec3.zero()
|
|
|
|
|
|
const pn = (linearSegments + 1) * 3
|
|
|
const controlPoints = new Float32Array(pn)
|
|
|
- const torsionVectors = new Float32Array(pn)
|
|
|
+ const tangentVectors = new Float32Array(pn)
|
|
|
const normalVectors = new Float32Array(pn)
|
|
|
+ const binormalVectors = new Float32Array(pn)
|
|
|
|
|
|
let i = 0
|
|
|
const polymerTraceIt = PolymerTraceIterator(unit)
|
|
@@ -66,120 +110,88 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me
|
|
|
const v = polymerTraceIt.move()
|
|
|
builder.setId(v.index)
|
|
|
|
|
|
- Vec3.spline(tB, v.t1, v.t2, v.t3, v.t4, 0.5, tension)
|
|
|
- Vec3.spline(dA, v.d12, v.d23, v.d34, v.d45, 0.5, tension)
|
|
|
-
|
|
|
- Vec3.normalize(initialTorsionVec, Vec3.sub(initialTorsionVec, tB, dB))
|
|
|
-
|
|
|
- Vec3.toArray(tB, controlPoints, 0)
|
|
|
- Vec3.normalize(torsionVec, Vec3.sub(torsionVec, tB, dB))
|
|
|
- Vec3.toArray(torsionVec, torsionVectors, 0)
|
|
|
- // approximate tangent as direction to previous control point
|
|
|
- Vec3.normalize(tangentVec, Vec3.sub(tangentVec, tB, tA))
|
|
|
- Vec3.normalize(normalVec, Vec3.cross(normalVec, tangentVec, torsionVec))
|
|
|
- Vec3.toArray(normalVec, normalVectors, 0)
|
|
|
-
|
|
|
- //
|
|
|
-
|
|
|
- const t12 = Vec3.zero()
|
|
|
- const t23 = Vec3.zero()
|
|
|
- const t34 = Vec3.zero()
|
|
|
- const t45 = Vec3.zero()
|
|
|
- Vec3.spline(t12, v.t0, v.t1, v.t2, v.t3, 0.5, tension)
|
|
|
- Vec3.spline(t23, v.t1, v.t2, v.t3, v.t4, 0.5, tension)
|
|
|
- Vec3.spline(t34, v.t2, v.t3, v.t4, v.t5, 0.5, tension)
|
|
|
- Vec3.spline(t45, v.t3, v.t4, v.t5, v.t6, 0.5, tension)
|
|
|
-
|
|
|
- // const dp12 = Vec3.zero()
|
|
|
- // const dp23 = Vec3.zero()
|
|
|
- // const dp34 = Vec3.zero()
|
|
|
- // const dp45 = Vec3.zero()
|
|
|
- // Vec3.projectPointOnVector(dp12, v.d12, t12, v.t1)
|
|
|
- // Vec3.projectPointOnVector(dp23, v.d23, t23, v.t2)
|
|
|
- // Vec3.projectPointOnVector(dp34, v.d34, t34, v.t3)
|
|
|
- // Vec3.projectPointOnVector(dp45, v.d45, t45, v.t4)
|
|
|
-
|
|
|
- const td12 = Vec3.zero()
|
|
|
- const td23 = Vec3.zero()
|
|
|
- const td34 = Vec3.zero()
|
|
|
- const td45 = Vec3.zero()
|
|
|
- Vec3.normalize(td12, Vec3.sub(td12, t12, v.d12))
|
|
|
- Vec3.scaleAndAdd(v.d12, t12, td12, 1)
|
|
|
- Vec3.normalize(td23, Vec3.sub(td23, t23, v.d23))
|
|
|
- if (Vec3.dot(td12, td23) < 0) {
|
|
|
- Vec3.scaleAndAdd(v.d23, t23, td23, -1)
|
|
|
- console.log('foo td0 td1')
|
|
|
- } else {
|
|
|
- Vec3.scaleAndAdd(v.d23, t23, td23, 1)
|
|
|
- }
|
|
|
- Vec3.normalize(td34, Vec3.sub(td34, t34, v.d34))
|
|
|
- if (Vec3.dot(td12, td34) < 0) {
|
|
|
- Vec3.scaleAndAdd(v.d34, t34, td34, -1)
|
|
|
- console.log('foo td1 td2')
|
|
|
- } else {
|
|
|
- Vec3.scaleAndAdd(v.d34, t34, td34, 1)
|
|
|
- }
|
|
|
- Vec3.normalize(td45, Vec3.sub(td45, t45, v.d45))
|
|
|
- if (Vec3.dot(td12, td45) < 0) {
|
|
|
- Vec3.scaleAndAdd(v.d45, t45, td45, -1)
|
|
|
- console.log('foo td2 td3')
|
|
|
- } else {
|
|
|
- Vec3.scaleAndAdd(v.d45, t45, td45, 1)
|
|
|
- }
|
|
|
-
|
|
|
- // console.log(td0, td1, td2, td3)
|
|
|
-
|
|
|
- builder.addIcosahedron(t12, 0.3, 1)
|
|
|
- builder.addIcosahedron(t23, 0.3, 1)
|
|
|
- builder.addIcosahedron(t34, 0.3, 1)
|
|
|
- builder.addIcosahedron(t45, 0.3, 1)
|
|
|
-
|
|
|
- // builder.addIcosahedron(dp12, 0.3, 1)
|
|
|
- // builder.addIcosahedron(dp23, 0.3, 1)
|
|
|
- // builder.addIcosahedron(dp34, 0.3, 1)
|
|
|
- // builder.addIcosahedron(dp45, 0.3, 1)
|
|
|
-
|
|
|
- builder.addIcosahedron(v.d12, 0.3, 1)
|
|
|
- builder.addIcosahedron(v.d23, 0.3, 1)
|
|
|
- builder.addIcosahedron(v.d34, 0.3, 1)
|
|
|
- builder.addIcosahedron(v.d45, 0.3, 1)
|
|
|
-
|
|
|
- for (let j = 1; j <= linearSegments; ++j) {
|
|
|
+ for (let j = 0; j <= linearSegments; ++j) {
|
|
|
const t = j * 1.0 / linearSegments;
|
|
|
- Vec3.copy(tA, tB)
|
|
|
// if ((v.last && t > 0.5) || (v.first && t < 0.5)) break
|
|
|
|
|
|
if (t < 0.5) {
|
|
|
- Vec3.spline(tB, v.t1, v.t2, v.t3, v.t4, t + 0.5, tension)
|
|
|
+ Vec3.spline(tB, v.t0, v.t1, v.t2, v.t3, t + 0.5, tension)
|
|
|
+ Vec3.spline(tanA, v.t0, v.t1, v.t2, v.t3, t + 0.5 + 0.01, tension)
|
|
|
+ Vec3.spline(tanB, v.t0, v.t1, v.t2, v.t3, t + 0.5 - 0.01, tension)
|
|
|
} else {
|
|
|
- Vec3.spline(tB, v.t2, v.t3, v.t4, v.t5, t - 0.5, tension)
|
|
|
+ Vec3.spline(tB, v.t1, v.t2, v.t3, v.t4, t - 0.5, tension)
|
|
|
+ Vec3.spline(tanA, v.t1, v.t2, v.t3, v.t4, t - 0.5 + 0.01, tension)
|
|
|
+ Vec3.spline(tanB, v.t1, v.t2, v.t3, v.t4, t - 0.5 - 0.01, tension)
|
|
|
}
|
|
|
- Vec3.spline(dB, v.d12, v.d23, v.d34, v.d45, t, tension)
|
|
|
-
|
|
|
- // reflect(reflectedControlPoint, tB, tA, 1)
|
|
|
Vec3.toArray(tB, controlPoints, j * 3)
|
|
|
+ Vec3.normalize(tangentVec, Vec3.sub(tangentVec, tanA, tanB))
|
|
|
+ Vec3.toArray(tangentVec, tangentVectors, j * 3)
|
|
|
+ }
|
|
|
|
|
|
- Vec3.normalize(torsionVec, Vec3.sub(torsionVec, tB, dB))
|
|
|
- // if (Vec3.dot(initialTorsionVec, torsionVec) < 0) Vec3.scale(torsionVec, torsionVec, -1)
|
|
|
- Vec3.toArray(torsionVec, torsionVectors, j * 3)
|
|
|
-
|
|
|
- // approximate tangent as direction to previous control point
|
|
|
- Vec3.normalize(tangentVec, Vec3.sub(tangentVec, tB, tA))
|
|
|
- Vec3.normalize(normalVec, Vec3.cross(normalVec, tangentVec, torsionVec))
|
|
|
- Vec3.toArray(normalVec, normalVectors, j * 3)
|
|
|
-
|
|
|
- // TODO size theme
|
|
|
- // builder.addCylinder(tA, tB, 1.0, { radiusTop: 0.3, radiusBottom: 0.3 })
|
|
|
-
|
|
|
- builder.addIcosahedron(dB, 0.1, 1)
|
|
|
-
|
|
|
- builder.addCylinder(tB, Vec3.add(tmp, tB, torsionVec), 1.0, { radiusTop: 0.1, radiusBottom: 0.1 })
|
|
|
- // builder.addCylinder(tB, Vec3.add(tmp, tB, normalVec), 1.0, { radiusTop: 0.1, radiusBottom: 0.1 })
|
|
|
-
|
|
|
- console.log(tA, tB)
|
|
|
+ const firstControlPoint = Vec3.zero()
|
|
|
+ const lastControlPoint = Vec3.zero()
|
|
|
+ const firstTangentVec = Vec3.zero()
|
|
|
+ const lastTangentVec = Vec3.zero()
|
|
|
+ const firstNormalVec = Vec3.zero()
|
|
|
+ const lastNormalVec = Vec3.zero()
|
|
|
+ const firstDirPoint = Vec3.zero()
|
|
|
+ const lastDirPoint = Vec3.zero()
|
|
|
+
|
|
|
+ Vec3.fromArray(firstControlPoint, controlPoints, 0)
|
|
|
+ Vec3.fromArray(lastControlPoint, controlPoints, linearSegments * 3)
|
|
|
+ Vec3.fromArray(firstTangentVec, tangentVectors, 0)
|
|
|
+ Vec3.fromArray(lastTangentVec, tangentVectors, linearSegments * 3)
|
|
|
+ Vec3.copy(firstDirPoint, v.d12)
|
|
|
+ Vec3.copy(lastDirPoint, v.d23)
|
|
|
+
|
|
|
+ Vec3.normalize(tmpNormal, Vec3.sub(tmp, firstControlPoint, firstDirPoint))
|
|
|
+ orthogonalize(firstNormalVec, firstTangentVec, tmpNormal)
|
|
|
+
|
|
|
+ Vec3.normalize(tmpNormal, Vec3.sub(tmp, lastControlPoint, lastDirPoint))
|
|
|
+ orthogonalize(lastNormalVec, lastTangentVec, tmpNormal)
|
|
|
+
|
|
|
+ // console.log('ELEMENT', i)
|
|
|
+ interpolateNormals(controlPoints, tangentVectors, normalVectors, binormalVectors, firstNormalVec, lastNormalVec)
|
|
|
+
|
|
|
+ // const controlPoint = Vec3.zero()
|
|
|
+ // for (let j = 0; j <= linearSegments; ++j) {
|
|
|
+ // Vec3.fromArray(controlPoint, controlPoints, j * 3)
|
|
|
+ // Vec3.fromArray(normalVec, normalVectors, j * 3)
|
|
|
+ // Vec3.fromArray(binormalVec, binormalVectors, j * 3)
|
|
|
+ // Vec3.fromArray(tangentVec, tangentVectors, j * 3)
|
|
|
+ // builder.addIcosahedron(controlPoint, 0.1, 1)
|
|
|
+ // builder.addCylinder(
|
|
|
+ // controlPoint,
|
|
|
+ // Vec3.add(tmp, controlPoint, normalVec),
|
|
|
+ // 1.5,
|
|
|
+ // { radiusTop: 0.07, radiusBottom: 0.07 }
|
|
|
+ // )
|
|
|
+ // builder.addCylinder(
|
|
|
+ // controlPoint,
|
|
|
+ // Vec3.add(tmp, controlPoint, binormalVec),
|
|
|
+ // 0.8,
|
|
|
+ // { radiusTop: 0.07, radiusBottom: 0.07 }
|
|
|
+ // )
|
|
|
+ // builder.addCylinder(
|
|
|
+ // controlPoint,
|
|
|
+ // Vec3.add(tmp, controlPoint, tangentVec),
|
|
|
+ // j === 0 ? 2 : 1.5,
|
|
|
+ // { radiusTop: 0.03, radiusBottom: 0.03 }
|
|
|
+ // )
|
|
|
+ // }
|
|
|
+
|
|
|
+ let width = 0.2
|
|
|
+ let height = 0.2
|
|
|
+ if (
|
|
|
+ SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta) ||
|
|
|
+ SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Helix)
|
|
|
+ ) {
|
|
|
+ width = 0.2
|
|
|
+ height = 0.6
|
|
|
}
|
|
|
|
|
|
- builder.addTube(controlPoints, torsionVectors, normalVectors, linearSegments, radialSegments)
|
|
|
+ // TODO size theme
|
|
|
+ builder.addTube(controlPoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height)
|
|
|
|
|
|
if (i % 10000 === 0 && ctx.shouldUpdate) {
|
|
|
await ctx.update({ message: 'Polymer trace mesh', current: i, max: polymerElementCount });
|