diff --git a/src/mol-base/math/linear-algebra-3d.ts b/src/mol-base/math/linear-algebra-3d.ts index cbfe8de21086a9199cc577c46fe736a6c98d32bd..0f998a1260911d79c9d688b56a8126099a7e0bab 100644 --- a/src/mol-base/math/linear-algebra-3d.ts +++ b/src/mol-base/math/linear-algebra-3d.ts @@ -568,7 +568,7 @@ export namespace Vec3 { return out; } - export function transformMat4(out: Vec3, a: Vec3, m: number[]) { + export function transformMat4(out: Vec3, a: Vec3, m: Mat4) { const x = a[0], y = a[1], z = a[2], w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; diff --git a/src/mol-data/structure/query/properties.ts b/src/mol-data/structure/query/properties.ts index df1dbfdd517c81b2ff30f5de714909ca8ee4483d..ef9ef07dfb581e5a6fd55556cabfcc4440ef9e23 100644 --- a/src/mol-data/structure/query/properties.ts +++ b/src/mol-data/structure/query/properties.ts @@ -13,6 +13,10 @@ const constant = { } const atom = { + x: Atom.property(l => l.unit.x(l.atom)), + y: Atom.property(l => l.unit.y(l.atom)), + z: Atom.property(l => l.unit.z(l.atom)), + type_symbol: Atom.property(l => l.unit.hierarchy.atoms.type_symbol.value(l.atom)) } diff --git a/src/mol-data/structure/structure/atom.ts b/src/mol-data/structure/structure/atom.ts index 2a5b1c0192e1f407224187ac47a5fc0515339d96..61aea8741c1235b8b7980703c330600f8505d4e2 100644 --- a/src/mol-data/structure/structure/atom.ts +++ b/src/mol-data/structure/structure/atom.ts @@ -28,7 +28,7 @@ namespace Atom { export interface Property<T> { (location: Location): T } export interface Predicate extends Property<boolean> { } - export function setLocation(structure: Structure, l: Location, atom: Atom) { + export function updateLocation(structure: Structure, l: Location, atom: Atom) { l.unit = structure.units[unit(atom)]; l.atom = index(atom); } diff --git a/src/mol-data/structure/structure/operator.ts b/src/mol-data/structure/structure/operator.ts index dd1df5c18cf4d1ad2add8429adf8424bbe2d1e31..e6fc2fd2f5aadfd7b715f8bf71d63ce559084b41 100644 --- a/src/mol-data/structure/structure/operator.ts +++ b/src/mol-data/structure/structure/operator.ts @@ -9,7 +9,7 @@ import { Mat4 } from 'mol-base/math/linear-algebra-3d' interface Operator extends Readonly<{ name: string, hkl: number[], // defaults to [0, 0, 0] for non symmetry entries - transform: Mat4, + matrix: Mat4, // cache the inverse of the transform inverse: Mat4, // optimize the identity case @@ -17,7 +17,7 @@ interface Operator extends Readonly<{ }> { } namespace Operator { - export const Identity: Operator = { name: '1_555', hkl: [0, 0, 0], transform: Mat4.identity(), inverse: Mat4.identity(), isIdentity: true }; + export const Identity: Operator = { name: '1_555', hkl: [0, 0, 0], matrix: Mat4.identity(), inverse: Mat4.identity(), isIdentity: true }; } export default Operator \ No newline at end of file diff --git a/src/mol-data/structure/structure/unit.ts b/src/mol-data/structure/structure/unit.ts index a90e87c243b16b9b4b649d5f22ce704bae23ff4c..b58145ca2d4ed29d25f0cabd1cfaceca6e62c5d6 100644 --- a/src/mol-data/structure/structure/unit.ts +++ b/src/mol-data/structure/structure/unit.ts @@ -6,6 +6,7 @@ import { Model } from '../model' import Operator from './operator' +import { Vec3, Mat4 } from 'mol-base/math/linear-algebra-3d' interface Unit extends Readonly<{ // Structure-level unique identifier of the unit. @@ -18,22 +19,30 @@ interface Unit extends Readonly<{ // The transform and and inverse are baked into the "getPosition" function operator: Operator, - // Cache residue and chain indices for fast access. + // Reference some commonly accessed things for faster access. residueIndex: ArrayLike<number>, chainIndex: ArrayLike<number>, hierarchy: Model['hierarchy'], conformation: Model['conformation'] }> { - // // returns the untransformed position. Used for spatial queries. - // getInvariantPosition(atom: number, slot: Vec3): Vec3 + // returns the untransformed position. Used for spatial queries. + getInvariantPosition(atom: number, slot: Vec3): Vec3, - // // gets the transformed position of the specified atom - // getPosition(atom: number, slot: Vec3): Vec3 + // gets the transformed position of the specified atom. + getPosition(atom: number, slot: Vec3): Vec3, + + // optimized x/y/z coordinate projections for your query needs. + x(atom: number): number, + y(atom: number): number, + z(atom: number): number } namespace Unit { export function create(model: Model, operator: Operator): Unit { const h = model.hierarchy; + const { __x: x, __y: y, __z: z } = model.conformation; + const getInvariantPosition = makeGetInvariantPosition(x, y, z); + const getPosition = operator.isIdentity ? getInvariantPosition : makeGetPosition(operator.matrix, x, y, z); return { id: nextUnitId(), model, @@ -41,7 +50,12 @@ namespace Unit { residueIndex: h.residueSegments.segmentMap, chainIndex: h.chainSegments.segmentMap, hierarchy: model.hierarchy, - conformation: model.conformation + conformation: model.conformation, + getInvariantPosition, + getPosition, + x: operator.isIdentity ? makeGetCoord(x) : makeX(operator.matrix, x, y, z), + y: operator.isIdentity ? makeGetCoord(y) : makeY(operator.matrix, x, y, z), + z: operator.isIdentity ? makeGetCoord(z) : makeZ(operator.matrix, x, y, z) }; } } @@ -53,4 +67,73 @@ function nextUnitId() { const ret = _id; _id = (_id + 1) % 0x3fffffff; return ret; +} + +function makeGetInvariantPosition(x: ArrayLike<number>, y: ArrayLike<number>, z: ArrayLike<number>) { + return (a: number, r: Vec3): Vec3 => { + r[0] = x[a]; + r[1] = y[a]; + r[2] = z[a]; + return r; + } +} + +function makeGetCoord(xs: ArrayLike<number>) { + return (i: number) => xs[i]; +} + +function isWConst(m: Mat4) { + return m[3] === 0 && m[7] === 0 && m[11] === 0; +} + +function makeX(m: Mat4, xs: ArrayLike<number>, ys: ArrayLike<number>, zs: ArrayLike<number>) { + const xx = m[0], yy = m[4], zz = m[8], ww = m[12]; + + if (isWConst(m)) { + const w = m[15] || 1.0; + return (i: number) => (xx * xs[i] + yy * ys[i] + zz * zs[i] + ww) / w; + } + + return (i: number) => { + const x = xs[i], y = ys[i], z = zs[i], w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; + return (xx * x + yy * y + zz * z + ww) / w; + } +} + +function makeY(m: Mat4, xs: ArrayLike<number>, ys: ArrayLike<number>, zs: ArrayLike<number>) { + const xx = m[1], yy = m[5], zz = m[9], ww = m[13]; + + if (isWConst(m)) { + const w = m[15] || 1.0; + return (i: number) => (xx * xs[i] + yy * ys[i] + zz * zs[i] + ww) / w; + } + + return (i: number) => { + const x = xs[i], y = ys[i], z = zs[i], w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; + return (xx * x + yy * y + zz * z + ww) / w; + } +} + +function makeZ(m: Mat4, xs: ArrayLike<number>, ys: ArrayLike<number>, zs: ArrayLike<number>) { + const xx = m[2], yy = m[6], zz = m[10], ww = m[14]; + + if (isWConst(m)) { + const w = m[15] || 1.0; + return (i: number) => (xx * xs[i] + yy * ys[i] + zz * zs[i] + ww) / w; + } + + return (i: number) => { + const x = xs[i], y = ys[i], z = zs[i], w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0; + return (xx * x + yy * y + zz * z + ww) / w; + } +} + +function makeGetPosition(m: Mat4, x: ArrayLike<number>, y: ArrayLike<number>, z: ArrayLike<number>) { + return (a: number, r: Vec3): Vec3 => { + r[0] = x[a]; + r[1] = y[a]; + r[2] = z[a]; + Vec3.transformMat4(r, r, m); + return r; + } } \ No newline at end of file diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 11b08e456a37ade7ec3c45ffe9563ed01138ef0d..f35ba202252ec608a74664a6163ab44613df1999 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -251,6 +251,13 @@ export namespace PropertyAccess { console.log(sumDirect(structures[0])); console.log('r', sumPropertyResidue(structures[0], l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom]))); + console.time('atom.x'); + console.log('atom.x', sumProperty(structures[0], Q.props.atom.x)); + console.timeEnd('atom.x'); + console.time('__x') + console.log('__x', sumProperty(structures[0], l => l.unit.conformation.__x[l.atom])); + console.timeEnd('__x') + //const authSeqId = Atom.property(l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom])); //const auth_seq_id = Q.props.residue.auth_seq_id; @@ -277,7 +284,7 @@ export namespace PropertyAccess { console.time('q2') const q2r = q2(structures[0]); console.timeEnd('q2') - console.log(Selection.structureCount(q2r)) + console.log(Selection.structureCount(q2r)); //console.log(q1(structures[0])); //const col = models[0].conformation.atomId.value;