UNPKG

molstar

Version:

A comprehensive macromolecular library.

199 lines (198 loc) 9.82 kB
"use strict"; /** * Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> * @author Gianluca Tomasello <giagitom@gmail.com> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.addTube = addTube; const linear_algebra_1 = require("../../../../mol-math/linear-algebra"); const util_1 = require("../../../../mol-data/util"); const normalVector = (0, linear_algebra_1.Vec3)(); const capNormalSmoothingVector = (0, linear_algebra_1.Vec3)(); const surfacePoint = (0, linear_algebra_1.Vec3)(); const controlPoint = (0, linear_algebra_1.Vec3)(); const u = (0, linear_algebra_1.Vec3)(); const v = (0, linear_algebra_1.Vec3)(); function add2AndScale2(out, a, b, sa, sb) { out[0] = (a[0] * sa) + (b[0] * sb); out[1] = (a[1] * sa) + (b[1] * sb); out[2] = (a[2] * sa) + (b[2] * sb); } function add3AndScale2(out, a, b, c, sa, sb) { out[0] = (a[0] * sa) + (b[0] * sb) + c[0]; out[1] = (a[1] * sa) + (b[1] * sb) + c[1]; out[2] = (a[2] * sa) + (b[2] * sb) + c[2]; } // avoiding namespace lookup improved performance in Chrome (Aug 2020) const v3fromArray = linear_algebra_1.Vec3.fromArray; const v3normalize = linear_algebra_1.Vec3.normalize; const v3scaleAndAdd = linear_algebra_1.Vec3.scaleAndAdd; const v3cross = linear_algebra_1.Vec3.cross; const v3slerp = linear_algebra_1.Vec3.slerp; const v3dot = linear_algebra_1.Vec3.dot; const v3unitX = linear_algebra_1.Vec3.unitX; const caAdd3 = util_1.ChunkedArray.add3; const CosSinCache = new Map(); function getCosSin(radialSegments, shift) { const offset = shift ? 1 : 0; const hash = (0, util_1.cantorPairing)(radialSegments, offset); if (!CosSinCache.has(hash)) { const cos = []; const sin = []; for (let j = 0; j < radialSegments; ++j) { const t = (j * 2 + offset) / radialSegments * Math.PI; cos[j] = Math.cos(t); sin[j] = Math.sin(t); } CosSinCache.set(hash, { cos, sin }); } return CosSinCache.get(hash); } function addTube(state, controlPoints, normalVectors, binormalVectors, linearSegments, radialSegments, widthValues, heightValues, startCap, endCap, crossSection, roundCap = false) { const { currentGroup, vertices, normals, indices, groups } = state; let vertexCount = vertices.elementCount; const { cos, sin } = getCosSin(radialSegments, crossSection === 'rounded'); const q1 = Math.round(radialSegments / 4); const q3 = q1 * 3; const roundCapFlag = roundCap && linearSegments && (startCap || endCap); let halfLinearSegments; const doubleRoundCap = roundCapFlag && startCap && endCap; if (doubleRoundCap) halfLinearSegments = linearSegments / 2; for (let i = 0; i <= linearSegments; ++i) { const i3 = i * 3; v3fromArray(u, normalVectors, i3); v3fromArray(v, binormalVectors, i3); v3fromArray(controlPoint, controlPoints, i3); let width = widthValues[i]; let height = heightValues[i]; let capSmoothingFactor; if (roundCapFlag) { const sc = doubleRoundCap ? i <= halfLinearSegments : startCap; if (doubleRoundCap) { capSmoothingFactor = Math.max(Number.EPSILON, Math.sqrt(1 - Math.pow((sc ? halfLinearSegments - i : i - halfLinearSegments) / halfLinearSegments, 2))); } else { capSmoothingFactor = Math.max(Number.EPSILON, Math.sqrt(1 - Math.pow((sc ? linearSegments - i : i) / linearSegments, 2))); } width *= capSmoothingFactor; height *= capSmoothingFactor; v3cross(capNormalSmoothingVector, sc ? v : u, sc ? u : v); v3normalize(capNormalSmoothingVector, capNormalSmoothingVector); } const rounded = crossSection === 'rounded' && height > width; for (let j = 0; j < radialSegments; ++j) { if (rounded) { add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[j], width * sin[j]); const h = v3dot(v, v3unitX) < 0 ? (j < q1 || j >= q3) ? height - width : -height + width : (j >= q1 && j < q3) ? -height + width : height - width; v3scaleAndAdd(surfacePoint, surfacePoint, u, h); if (j === q1 || j === q1 - 1) { add2AndScale2(normalVector, u, v, 0, 1); } else if (j === q3 || j === q3 - 1) { add2AndScale2(normalVector, u, v, 0, -1); } else { add2AndScale2(normalVector, u, v, cos[j], sin[j]); } } else { add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[j], width * sin[j]); add2AndScale2(normalVector, u, v, width * cos[j], height * sin[j]); } v3normalize(normalVector, normalVector); caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]); if (roundCapFlag) { v3slerp(normalVector, capNormalSmoothingVector, normalVector, capSmoothingFactor); } caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]); } } const radialSegmentsHalf = Math.round(radialSegments / 2); for (let i = 0; i < linearSegments; ++i) { // the triangles are arranged such that opposing triangles of the sheet align // which prevents triangle intersection within tight curves for (let j = 0; j < radialSegmentsHalf; ++j) { caAdd3(indices, vertexCount + i * radialSegments + (j + 1) % radialSegments, // a vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c vertexCount + i * radialSegments + j // b ); caAdd3(indices, vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c vertexCount + (i + 1) * radialSegments + j, // d vertexCount + i * radialSegments + j // b ); } for (let j = radialSegmentsHalf; j < radialSegments; ++j) { caAdd3(indices, vertexCount + i * radialSegments + (j + 1) % radialSegments, // a vertexCount + (i + 1) * radialSegments + j, // d vertexCount + i * radialSegments + j // b ); caAdd3(indices, vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c vertexCount + (i + 1) * radialSegments + j, // d vertexCount + i * radialSegments + (j + 1) % radialSegments); } } if (startCap) { const offset = 0; const centerVertex = vertices.elementCount; v3fromArray(u, normalVectors, offset); v3fromArray(v, binormalVectors, offset); v3fromArray(controlPoint, controlPoints, offset); v3cross(normalVector, v, u); caAdd3(vertices, controlPoint[0], controlPoint[1], controlPoint[2]); caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]); const width = roundCapFlag ? 0 : widthValues[0]; let height = roundCapFlag ? 0 : heightValues[0]; const rounded = crossSection === 'rounded' && height > width; if (rounded) height -= width; vertexCount = vertices.elementCount; for (let i = 0; i < radialSegments; ++i) { if (rounded) { add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[i], width * sin[i]); v3scaleAndAdd(surfacePoint, surfacePoint, u, (i < q1 || i >= q3) ? height : -height); } else { add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]); } caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]); caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]); caAdd3(indices, vertexCount + (i + 1) % radialSegments, vertexCount + i, centerVertex); } } if (endCap) { const offset = linearSegments * 3; const centerVertex = vertices.elementCount; v3fromArray(u, normalVectors, offset); v3fromArray(v, binormalVectors, offset); v3fromArray(controlPoint, controlPoints, offset); v3cross(normalVector, u, v); caAdd3(vertices, controlPoint[0], controlPoint[1], controlPoint[2]); caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]); const width = roundCapFlag ? 0 : widthValues[linearSegments]; let height = roundCapFlag ? 0 : heightValues[linearSegments]; const rounded = crossSection === 'rounded' && height > width; if (rounded) height -= width; vertexCount = vertices.elementCount; for (let i = 0; i < radialSegments; ++i) { if (rounded) { add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[i], width * sin[i]); v3scaleAndAdd(surfacePoint, surfacePoint, u, (i < q1 || i >= q3) ? height : -height); } else { add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]); } caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]); caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]); caAdd3(indices, vertexCount + i, vertexCount + (i + 1) % radialSegments, centerVertex); } } const addedVertexCount = (linearSegments + 1) * radialSegments + (startCap ? radialSegments + 1 : 0) + (endCap ? radialSegments + 1 : 0); util_1.ChunkedArray.addRepeat(groups, addedVertexCount, currentGroup); }