molstar
Version:
A comprehensive macromolecular library.
100 lines (99 loc) • 3.72 kB
JavaScript
"use strict";
/**
* Copyright (c) 2026 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeFrenetFrames = computeFrenetFrames;
const vec3_1 = require("./vec3.js");
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3fromArray = vec3_1.Vec3.fromArray;
const v3sub = vec3_1.Vec3.sub;
const v3normalize = vec3_1.Vec3.normalize;
const v3isZero = vec3_1.Vec3.isZero;
const v3set = vec3_1.Vec3.set;
const v3cross = vec3_1.Vec3.cross;
const v3toArray = vec3_1.Vec3.toArray;
const v3copy = vec3_1.Vec3.copy;
const v3magnitude = vec3_1.Vec3.magnitude;
const v3rotateAroundAxis = vec3_1.Vec3.rotateAroundAxis;
const v3dot = vec3_1.Vec3.dot;
/**
* Compute normal and binormal vectors along a curve using parallel transport
*/
function computeFrenetFrames(curvePoints, normalVectors, binormalVectors, n) {
const tangent = (0, vec3_1.Vec3)();
const prevTangent = (0, vec3_1.Vec3)();
const normal = (0, vec3_1.Vec3)();
const binormal = (0, vec3_1.Vec3)();
const p0 = (0, vec3_1.Vec3)();
const p1 = (0, vec3_1.Vec3)();
// Compute initial tangent
v3fromArray(p0, curvePoints, 0);
v3fromArray(p1, curvePoints, 3);
v3sub(tangent, p1, p0);
v3normalize(tangent, tangent);
if (v3isZero(tangent)) {
v3set(tangent, 1, 0, 0);
}
// Find initial normal (perpendicular to tangent)
// Use the smallest component of tangent to find a perpendicular vector
const absX = Math.abs(tangent[0]);
const absY = Math.abs(tangent[1]);
const absZ = Math.abs(tangent[2]);
if (absX <= absY && absX <= absZ) {
v3set(normal, 1, 0, 0);
}
else if (absY <= absZ) {
v3set(normal, 0, 1, 0);
}
else {
v3set(normal, 0, 0, 1);
}
// Orthogonalize normal against tangent
v3cross(binormal, tangent, normal);
v3normalize(binormal, binormal);
v3cross(normal, binormal, tangent);
v3normalize(normal, normal);
// Store first frame
v3toArray(normal, normalVectors, 0);
v3toArray(binormal, binormalVectors, 0);
// Propagate frames along the curve using parallel transport
v3copy(prevTangent, tangent);
for (let i = 1; i < n; ++i) {
// Compute tangent at this point
if (i < n - 1) {
v3fromArray(p0, curvePoints, (i - 1) * 3);
v3fromArray(p1, curvePoints, (i + 1) * 3);
v3sub(tangent, p1, p0);
}
else {
v3fromArray(p0, curvePoints, (i - 1) * 3);
v3fromArray(p1, curvePoints, i * 3);
v3sub(tangent, p1, p0);
}
v3normalize(tangent, tangent);
// Parallel transport: rotate the previous frame
const dot = v3dot(prevTangent, tangent);
if (dot < 0.9999) {
const axis = (0, vec3_1.Vec3)();
v3cross(axis, prevTangent, tangent);
if (v3magnitude(axis) > 0.0001) {
v3normalize(axis, axis);
const angle = Math.acos(Math.min(1, Math.max(-1, dot)));
// Rotate normal and binormal around axis by angle
v3rotateAroundAxis(normal, normal, axis, angle);
v3rotateAroundAxis(binormal, binormal, axis, angle);
}
}
// Ensure orthogonality
v3cross(binormal, tangent, normal);
v3normalize(binormal, binormal);
v3cross(normal, binormal, tangent);
v3normalize(normal, normal);
v3toArray(normal, normalVectors, i * 3);
v3toArray(binormal, binormalVectors, i * 3);
v3copy(prevTangent, tangent);
}
}