diff --git a/src/extensions/anvil/algorithm.ts b/src/extensions/anvil/algorithm.ts index b7faf8409c5213631fbaabbca0a34663da46c822..b800a30643a0d57ca2b822c39689f48f048f293b 100644 --- a/src/extensions/anvil/algorithm.ts +++ b/src/extensions/anvil/algorithm.ts @@ -59,6 +59,21 @@ export function computeANVIL(structure: Structure, props: ANVILProps) { }); } +// avoiding namespace lookup improved performance in Chrome (Aug 2020) +const v3add = Vec3.add; +const v3clone = Vec3.clone; +const v3create = Vec3.create; +const v3distance = Vec3.distance; +const v3dot = Vec3.dot; +const v3magnitude = Vec3.magnitude; +const v3normalize = Vec3.normalize; +const v3scale = Vec3.scale; +const v3scaleAndAdd = Vec3.scaleAndAdd; +const v3set = Vec3.set; +const v3squaredDistance = Vec3.squaredDistance; +const v3sub = Vec3.sub; +const v3zero = Vec3.zero; + const centroidHelper = new CentroidHelper(); async function initialize(runtime: RuntimeContext, structure: Structure, props: ANVILProps): Promise<ANVILContext> { const l = StructureElement.Location.create(structure); @@ -74,7 +89,7 @@ async function initialize(runtime: RuntimeContext, structure: Structure, props: const accessibleSurfaceArea = await AccessibleSurfaceArea.compute(structure, p).runInContext(runtime); const asaCutoff = props.asaCutoff / 100; - const vec = Vec3(); + const vec = v3zero(); let m = 0; let n = 0; for (let i = 0, il = structure.units.length; i < il; ++i) { @@ -102,7 +117,7 @@ async function initialize(runtime: RuntimeContext, structure: Structure, props: } // while iterating use first pass to compute centroid - Vec3.set(vec, x(l), y(l), z(l)); + v3set(vec, x(l), y(l), z(l)); centroidHelper.includeStep(vec); // keep track of offsets and exposed state to reuse @@ -119,10 +134,10 @@ async function initialize(runtime: RuntimeContext, structure: Structure, props: // calculate centroid and extent centroidHelper.finishedIncludeStep(); - const centroid = Vec3.clone(centroidHelper.center); + const centroid = v3clone(centroidHelper.center); for (let k = 0, kl = offsets.length; k < kl; k++) { setLocation(l, structure, offsets[k]); - Vec3.set(vec, x(l), y(l), z(l)); + v3set(vec, x(l), y(l), z(l)); centroidHelper.radiusStep(vec); } const extent = 1.2 * Math.sqrt(centroidHelper.radiusSq); @@ -150,13 +165,13 @@ export async function calculate(runtime: RuntimeContext, structure: Structure, p membrane = await adjustThickness(runtime, 'Adjusting membrane thickness...', ctx, membrane, initialHphobHphil); } - const normalVector = Vec3.zero(); - const center = Vec3.zero(); - Vec3.sub(normalVector, membrane.planePoint1, membrane.planePoint2); - Vec3.normalize(normalVector, normalVector); + const normalVector = v3zero(); + const center = v3zero(); + v3sub(normalVector, membrane.planePoint1, membrane.planePoint2); + v3normalize(normalVector, normalVector); - Vec3.add(center, membrane.planePoint1, membrane.planePoint2); - Vec3.scale(center, center, 0.5); + v3add(center, membrane.planePoint1, membrane.planePoint2); + v3scale(center, center, 0.5); const extent = adjustExtent(ctx, membrane, center); return { @@ -187,8 +202,8 @@ namespace MembraneCandidate { } export function scored(spherePoint: Vec3, planePoint1: Vec3, planePoint2: Vec3, stats: HphobHphil, qmax: number, centroid: Vec3): MembraneCandidate { - const normalVector = Vec3(); - Vec3.sub(normalVector, centroid, spherePoint); + const normalVector = v3zero(); + v3sub(normalVector, centroid, spherePoint); return { planePoint1, planePoint2, @@ -208,25 +223,25 @@ async function findMembrane(runtime: RuntimeContext, message: string | undefined let qmax = 0; // construct slices of thickness 1.0 along the axis connecting the centroid and the spherePoint - const diam = Vec3(); + const diam = v3zero(); for (let n = 0, nl = spherePoints.length; n < nl; n++) { if (runtime?.shouldUpdate && message && (n + 1) % UPDATE_INTERVAL === 0) { await runtime.update({ message, current: (n + 1), max: nl }); } const spherePoint = spherePoints[n]; - Vec3.sub(diam, centroid, spherePoint); - Vec3.scale(diam, diam, 2); - const diamNorm = Vec3.magnitude(diam); + v3sub(diam, centroid, spherePoint); + v3scale(diam, diam, 2); + const diamNorm = v3magnitude(diam); const qvartemp = []; for (let i = 0, il = diamNorm - stepSize; i < il; i += stepSize) { - const c1 = Vec3(); - const c2 = Vec3(); - Vec3.scaleAndAdd(c1, spherePoint, diam, i / diamNorm); - Vec3.scaleAndAdd(c2, spherePoint, diam, (i + stepSize) / diamNorm); - const d1 = -Vec3.dot(diam, c1); - const d2 = -Vec3.dot(diam, c2); + const c1 = v3zero(); + const c2 = v3zero(); + v3scaleAndAdd(c1, spherePoint, diam, i / diamNorm); + v3scaleAndAdd(c2, spherePoint, diam, (i + stepSize) / diamNorm); + const d1 = -v3dot(diam, c1); + const d2 = -v3dot(diam, c2); const dMin = Math.min(d1, d2); const dMax = Math.max(d1, d2); @@ -273,7 +288,7 @@ async function findMembrane(runtime: RuntimeContext, message: string | undefined async function adjustThickness(runtime: RuntimeContext, message: string | undefined, ctx: ANVILContext, membrane: MembraneCandidate, initialHphobHphil: HphobHphil): Promise<MembraneCandidate> { const { minThickness } = ctx; const step = 0.3; - let maxThickness = Vec3.distance(membrane.planePoint1, membrane.planePoint2); + let maxThickness = v3distance(membrane.planePoint1, membrane.planePoint2); let maxNos = membraneSegments(ctx, membrane).length; let optimalThickness = membrane; @@ -310,12 +325,12 @@ function membraneSegments(ctx: ANVILContext, membrane: MembraneCandidate): Array const { offsets, structure, adjust } = ctx; const { normalVector, planePoint1, planePoint2 } = membrane; const l = StructureElement.Location.create(structure); - const testPoint = Vec3(); + const testPoint = v3zero(); const { auth_asym_id } = StructureProperties.chain; const { auth_seq_id } = StructureProperties.residue; - const d1 = -Vec3.dot(normalVector!, planePoint1); - const d2 = -Vec3.dot(normalVector!, planePoint2); + const d1 = -v3dot(normalVector!, planePoint1); + const d2 = -v3dot(normalVector!, planePoint2); const dMin = Math.min(d1, d2); const dMax = Math.max(d1, d2); @@ -341,7 +356,7 @@ function membraneSegments(ctx: ANVILContext, membrane: MembraneCandidate): Array } authSeqId = auth_seq_id(l); - Vec3.set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element)); + v3set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element)); if (_isInMembranePlane(testPoint, normalVector!, dMin, dMax)) { inMembrane[authAsymId].add(authSeqId); } else { @@ -387,12 +402,12 @@ function membraneSegments(ctx: ANVILContext, membrane: MembraneCandidate): Array // evaluate residues 1 pos outside of membrane setLocation(l, structure, offsets[start - 1]); - Vec3.set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element)); - const d3 = -Vec3.dot(normalVector!, testPoint); + v3set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element)); + const d3 = -v3dot(normalVector!, testPoint); setLocation(l, structure, offsets[end + 1]); - Vec3.set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element)); - const d4 = -Vec3.dot(normalVector!, testPoint); + v3set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element)); + const d4 = -v3dot(normalVector!, testPoint); if (Math.min(d3, d4) < dMin && Math.max(d3, d4) > dMax) { // reject this refinement @@ -415,20 +430,20 @@ function adjustExtent(ctx: ANVILContext, membrane: MembraneCandidate, centroid: const { offsets, structure } = ctx; const { normalVector, planePoint1, planePoint2 } = membrane; const l = StructureElement.Location.create(structure); - const testPoint = Vec3(); + const testPoint = v3zero(); const { x, y, z } = StructureProperties.atom; - const d1 = -Vec3.dot(normalVector!, planePoint1); - const d2 = -Vec3.dot(normalVector!, planePoint2); + const d1 = -v3dot(normalVector!, planePoint1); + const d2 = -v3dot(normalVector!, planePoint2); const dMin = Math.min(d1, d2); const dMax = Math.max(d1, d2); let extent = 0; for (let k = 0, kl = offsets.length; k < kl; k++) { setLocation(l, structure, offsets[k]); - Vec3.set(testPoint, x(l), y(l), z(l)); + v3set(testPoint, x(l), y(l), z(l)); if (_isInMembranePlane(testPoint, normalVector!, dMin, dMax)) { - const dsq = Vec3.squaredDistance(testPoint, centroid); + const dsq = v3squaredDistance(testPoint, centroid); if (dsq > extent) extent = dsq; } } @@ -451,13 +466,13 @@ function qValue(currentStats: HphobHphil, initialStats: HphobHphil): number { } export function isInMembranePlane(testPoint: Vec3, normalVector: Vec3, planePoint1: Vec3, planePoint2: Vec3): boolean { - const d1 = -Vec3.dot(normalVector, planePoint1); - const d2 = -Vec3.dot(normalVector, planePoint2); + const d1 = -v3dot(normalVector, planePoint1); + const d2 = -v3dot(normalVector, planePoint2); return _isInMembranePlane(testPoint, normalVector, Math.min(d1, d2), Math.max(d1, d2)); } function _isInMembranePlane(testPoint: Vec3, normalVector: Vec3, min: number, max: number): boolean { - const d = -Vec3.dot(normalVector, testPoint); + const d = -v3dot(normalVector, testPoint); return d > min && d < max; } @@ -471,7 +486,7 @@ function generateSpherePoints(ctx: ANVILContext, numberOfSpherePoints: number): theta = Math.acos(h); phi = (k === 1 || k === numberOfSpherePoints) ? 0 : (oldPhi + 3.6 / Math.sqrt(numberOfSpherePoints * (1 - h * h))) % (2 * Math.PI); - const point = Vec3.create( + const point = v3create( extent * Math.sin(phi) * Math.sin(theta) + centroid[0], extent * Math.cos(theta) + centroid[1], extent * Math.cos(phi) * Math.sin(theta) + centroid[2] @@ -494,7 +509,7 @@ function findProximateAxes(ctx: ANVILContext, membrane: MembraneCandidate): Vec3 const dsq = (s + j) * (s + j); sphere_pts2 = []; for (let i = 0, il = points.length; i < il; i++) { - if (Vec3.squaredDistance(points[i], membrane.spherePoint!) < dsq) { + if (v3squaredDistance(points[i], membrane.spherePoint!) < dsq) { sphere_pts2.push(points[i]); } } @@ -516,7 +531,7 @@ namespace HphobHphil { }; } - const testPoint = Vec3(); + const testPoint = v3zero(); export function filtered(ctx: ANVILContext, filter?: { normalVector: Vec3, dMin: number, dMax: number }): HphobHphil { const { exposed, structure } = ctx; const { label_comp_id } = StructureProperties.atom; @@ -528,7 +543,7 @@ namespace HphobHphil { // testPoints have to be in putative membrane layer if (filter) { - Vec3.set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element)); + v3set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element)); if (!_isInMembranePlane(testPoint, filter.normalVector, filter.dMin, filter.dMax)) { continue; }