molstar
Version:
A comprehensive macromolecular library.
112 lines (111 loc) • 5.74 kB
JavaScript
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
import { MeshBuilder } from '../mesh-builder';
import { transformPrimitive } from '../../../primitive/primitive';
import { Cylinder, DefaultCylinderProps } from '../../../primitive/cylinder';
import { Prism } from '../../../primitive/prism';
import { polygon } from '../../../primitive/polygon';
import { hashFnv32a } from '../../../../mol-data/util';
var cylinderMap = new Map();
var up = Vec3.create(0, 1, 0);
var tmpCylinderDir = Vec3();
var tmpCylinderMatDir = Vec3();
var tmpCylinderCenter = Vec3();
var tmpCylinderMat = Mat4();
var tmpCylinderMatRot = Mat4();
var tmpCylinderScale = Vec3();
var tmpCylinderStart = Vec3();
var tmpUp = Vec3();
function setCylinderMat(m, start, dir, length, matchDir) {
Vec3.setMagnitude(tmpCylinderMatDir, dir, length / 2);
Vec3.add(tmpCylinderCenter, start, tmpCylinderMatDir);
// ensure the direction used to create the rotation is always pointing in the same
// direction so the triangles of adjacent cylinder will line up
if (matchDir)
Vec3.matchDirection(tmpUp, up, tmpCylinderMatDir);
else
Vec3.copy(tmpUp, up);
Vec3.set(tmpCylinderScale, 1, length, 1);
Vec3.makeRotation(tmpCylinderMatRot, tmpUp, tmpCylinderMatDir);
Mat4.scale(m, tmpCylinderMatRot, tmpCylinderScale);
return Mat4.setTranslation(m, tmpCylinderCenter);
}
var tmpPropValues = new Int32Array(9);
function getCylinderPropsKey(props) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
tmpPropValues[0] = Math.round(1000 * ((_a = props.radiusTop) !== null && _a !== void 0 ? _a : DefaultCylinderProps.radiusTop));
tmpPropValues[1] = Math.round(1000 * ((_b = props.radiusBottom) !== null && _b !== void 0 ? _b : DefaultCylinderProps.radiusBottom));
tmpPropValues[2] = Math.round(1000 * ((_c = props.height) !== null && _c !== void 0 ? _c : DefaultCylinderProps.height));
tmpPropValues[3] = (_d = props.radialSegments) !== null && _d !== void 0 ? _d : DefaultCylinderProps.radialSegments;
tmpPropValues[4] = (_e = props.heightSegments) !== null && _e !== void 0 ? _e : DefaultCylinderProps.heightSegments;
tmpPropValues[5] = ((_f = props.topCap) !== null && _f !== void 0 ? _f : DefaultCylinderProps.topCap) ? 1 : 0;
tmpPropValues[6] = ((_g = props.bottomCap) !== null && _g !== void 0 ? _g : DefaultCylinderProps.bottomCap) ? 1 : 0;
tmpPropValues[7] = Math.round(1000 * ((_h = props.thetaStart) !== null && _h !== void 0 ? _h : DefaultCylinderProps.thetaStart));
tmpPropValues[8] = Math.round(1000 * ((_j = props.thetaLength) !== null && _j !== void 0 ? _j : DefaultCylinderProps.thetaLength));
return hashFnv32a(tmpPropValues);
}
function getCylinder(props) {
var key = getCylinderPropsKey(props);
var cylinder = cylinderMap.get(key);
if (cylinder === undefined) {
if (props.radialSegments && props.radialSegments <= 4) {
var sideCount = Math.max(3, props.radialSegments);
var prism = Prism(polygon(sideCount, true, props.radiusTop), props);
cylinder = transformPrimitive(prism, Mat4.rotX90);
}
else {
cylinder = Cylinder(props);
}
cylinderMap.set(key, cylinder);
}
return cylinder;
}
export function addSimpleCylinder(state, start, end, props) {
var d = Vec3.distance(start, end);
Vec3.sub(tmpCylinderDir, end, start);
setCylinderMat(tmpCylinderMat, start, tmpCylinderDir, d, false);
MeshBuilder.addPrimitive(state, tmpCylinderMat, getCylinder(props));
}
export function addCylinder(state, start, end, lengthScale, props) {
var d = Vec3.distance(start, end) * lengthScale;
Vec3.sub(tmpCylinderDir, end, start);
setCylinderMat(tmpCylinderMat, start, tmpCylinderDir, d, true);
MeshBuilder.addPrimitive(state, tmpCylinderMat, getCylinder(props));
}
export function addDoubleCylinder(state, start, end, lengthScale, shift, props) {
var d = Vec3.distance(start, end) * lengthScale;
var cylinder = getCylinder(props);
Vec3.sub(tmpCylinderDir, end, start);
// positivly shifted cylinder
Vec3.add(tmpCylinderStart, start, shift);
setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d, true);
MeshBuilder.addPrimitive(state, tmpCylinderMat, cylinder);
// negativly shifted cylinder
Vec3.sub(tmpCylinderStart, start, shift);
setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d, true);
MeshBuilder.addPrimitive(state, tmpCylinderMat, cylinder);
}
export function addFixedCountDashedCylinder(state, start, end, lengthScale, segmentCount, props) {
var s = Math.floor(segmentCount / 2);
var step = 1 / segmentCount;
// automatically adjust length so links/bonds that are rendered as two half cylinders
// have evenly spaced dashed cylinders
if (lengthScale < 1) {
var bias = lengthScale / 2 / segmentCount;
lengthScale += segmentCount % 2 === 1 ? bias : -bias;
}
var d = Vec3.distance(start, end) * lengthScale;
var cylinder = getCylinder(props);
Vec3.sub(tmpCylinderDir, end, start);
for (var j = 0; j < s; ++j) {
var f = step * (j * 2 + 1);
Vec3.setMagnitude(tmpCylinderDir, tmpCylinderDir, d * f);
Vec3.add(tmpCylinderStart, start, tmpCylinderDir);
setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d * step, false);
MeshBuilder.addPrimitive(state, tmpCylinderMat, cylinder);
}
}