molstar
Version:
A comprehensive macromolecular library.
449 lines (448 loc) • 25.9 kB
JavaScript
"use strict";
/**
* Copyright (c) 2018-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Zhenyu Zhang <jump2cn@gmail.com>
* @author Gianluca Tomasello <giagitom@gmail.com>
* @author David Sehnal <david.sehnal@gmail.com>
* @author Herman Bergwerf <post@hbergwerf.nl>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmptyLinkBuilderProps = exports.DefaultLinkLineProps = exports.LinkLineParams = exports.DefaultLinkCylinderProps = exports.LinkCylinderParams = void 0;
exports.calculateShiftDir = calculateShiftDir;
exports.createLinkCylinderMesh = createLinkCylinderMesh;
exports.addLinkCylinderMesh = addLinkCylinderMesh;
exports.createLinkCylinderImpostors = createLinkCylinderImpostors;
exports.createLinkLines = createLinkLines;
const linear_algebra_1 = require("../../../../mol-math/linear-algebra");
const param_definition_1 = require("../../../../mol-util/param-definition");
const mesh_1 = require("../../../../mol-geo/geometry/mesh/mesh");
const mesh_builder_1 = require("../../../../mol-geo/geometry/mesh/mesh-builder");
const cylinder_1 = require("../../../../mol-geo/geometry/mesh/builder/cylinder");
const base_1 = require("../../../../mol-geo/geometry/base");
const lines_1 = require("../../../../mol-geo/geometry/lines/lines");
const lines_builder_1 = require("../../../../mol-geo/geometry/lines/lines-builder");
const cylinders_1 = require("../../../../mol-geo/geometry/cylinders/cylinders");
const cylinders_builder_1 = require("../../../../mol-geo/geometry/cylinders/cylinders-builder");
const sphere3d_1 = require("../../../../mol-math/geometry/primitives/sphere3d");
exports.LinkCylinderParams = {
linkScale: param_definition_1.ParamDefinition.Numeric(0.45, { min: 0, max: 1, step: 0.01 }),
linkSpacing: param_definition_1.ParamDefinition.Numeric(1, { min: 0, max: 2, step: 0.01 }),
linkCap: param_definition_1.ParamDefinition.Boolean(false),
aromaticScale: param_definition_1.ParamDefinition.Numeric(0.3, { min: 0, max: 1, step: 0.01 }),
aromaticSpacing: param_definition_1.ParamDefinition.Numeric(1.5, { min: 0, max: 3, step: 0.01 }),
aromaticDashCount: param_definition_1.ParamDefinition.Numeric(2, { min: 1, max: 6, step: 1 }),
dashCount: param_definition_1.ParamDefinition.Numeric(4, { min: 0, max: 10, step: 1 }),
dashScale: param_definition_1.ParamDefinition.Numeric(0.8, { min: 0, max: 2, step: 0.1 }),
dashCap: param_definition_1.ParamDefinition.Boolean(true),
stubCap: param_definition_1.ParamDefinition.Boolean(true),
radialSegments: param_definition_1.ParamDefinition.Numeric(16, { min: 2, max: 56, step: 2 }, base_1.BaseGeometry.CustomQualityParamInfo),
colorMode: param_definition_1.ParamDefinition.Select('default', param_definition_1.ParamDefinition.arrayToOptions(['default', 'interpolate']), base_1.BaseGeometry.ShadingCategory)
};
exports.DefaultLinkCylinderProps = param_definition_1.ParamDefinition.getDefaultValues(exports.LinkCylinderParams);
exports.LinkLineParams = {
linkScale: param_definition_1.ParamDefinition.Numeric(0.5, { min: 0, max: 1, step: 0.1 }),
linkSpacing: param_definition_1.ParamDefinition.Numeric(0.1, { min: 0, max: 2, step: 0.01 }),
aromaticDashCount: param_definition_1.ParamDefinition.Numeric(2, { min: 1, max: 6, step: 1 }),
dashCount: param_definition_1.ParamDefinition.Numeric(4, { min: 0, max: 10, step: 1 }),
};
exports.DefaultLinkLineProps = param_definition_1.ParamDefinition.getDefaultValues(exports.LinkLineParams);
const tmpV12 = (0, linear_algebra_1.Vec3)();
const tmpShiftV12 = (0, linear_algebra_1.Vec3)();
const tmpShiftV13 = (0, linear_algebra_1.Vec3)();
const up = linear_algebra_1.Vec3.create(0, 1, 0);
/** Calculate 'shift' direction that is perpendiculat to v1 - v2 and goes through v3 */
function calculateShiftDir(out, v1, v2, v3) {
linear_algebra_1.Vec3.normalize(tmpShiftV12, linear_algebra_1.Vec3.sub(tmpShiftV12, v1, v2));
if (v3 !== null) {
linear_algebra_1.Vec3.sub(tmpShiftV13, v1, v3);
}
else {
linear_algebra_1.Vec3.copy(tmpShiftV13, v1); // no reference point, use v1
}
linear_algebra_1.Vec3.normalize(tmpShiftV13, tmpShiftV13);
// ensure v13 and v12 are not colinear
let dp = linear_algebra_1.Vec3.dot(tmpShiftV12, tmpShiftV13);
if (1 - Math.abs(dp) < 1e-5) {
linear_algebra_1.Vec3.set(tmpShiftV13, 1, 0, 0);
dp = linear_algebra_1.Vec3.dot(tmpShiftV12, tmpShiftV13);
if (1 - Math.abs(dp) < 1e-5) {
linear_algebra_1.Vec3.set(tmpShiftV13, 0, 1, 0);
dp = linear_algebra_1.Vec3.dot(tmpShiftV12, tmpShiftV13);
}
}
linear_algebra_1.Vec3.setMagnitude(tmpShiftV12, tmpShiftV12, dp);
linear_algebra_1.Vec3.sub(tmpShiftV13, tmpShiftV13, tmpShiftV12);
return linear_algebra_1.Vec3.normalize(out, tmpShiftV13);
}
exports.EmptyLinkBuilderProps = {
linkCount: 0,
position: () => { },
radius: () => 0
};
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3scale = linear_algebra_1.Vec3.scale;
const v3add = linear_algebra_1.Vec3.add;
const v3sub = linear_algebra_1.Vec3.sub;
const v3setMagnitude = linear_algebra_1.Vec3.setMagnitude;
const v3dot = linear_algebra_1.Vec3.dot;
/**
* Each edge is included twice to allow for coloring/picking
* the half closer to the first vertex, i.e. vertex a.
*/
function createLinkCylinderMesh(ctx, linkBuilder, props, mesh) {
const { linkCount, referencePosition, position, style, radius, ignore, stub } = linkBuilder;
if (!linkCount)
return { mesh: mesh_1.Mesh.createEmpty(mesh) };
const { radialSegments, stubCap } = props;
const vertexCountEstimate = radialSegments * 2 * linkCount * 2;
const builderState = mesh_builder_1.MeshBuilder.createState(vertexCountEstimate, vertexCountEstimate / 4, mesh);
const center = (0, linear_algebra_1.Vec3)();
let count = 0;
let edgeIndex = 0;
const addOptions = {
builderState,
props,
assignNonAdjustedPosition: (posA, posB) => {
position(posA, posB, edgeIndex, false);
},
referencePosition: referencePosition ? () => referencePosition(edgeIndex) : undefined,
};
const addParams = {
a: (0, linear_algebra_1.Vec3)(),
b: (0, linear_algebra_1.Vec3)(),
group: 0,
linkStub: false,
linkStyle: 0 /* LinkStyle.Solid */,
linkRadius: 0,
};
for (let _eI = linkCount; edgeIndex < _eI; ++edgeIndex) {
if (ignore && ignore(edgeIndex))
continue;
position(addParams.a, addParams.b, edgeIndex, true);
v3add(center, center, addParams.a);
v3add(center, center, addParams.b);
count += 2;
addParams.group = edgeIndex;
addParams.linkStub = stubCap && (stub ? stub(edgeIndex) : false);
addParams.linkRadius = radius(edgeIndex);
addParams.linkStyle = style ? style(edgeIndex) : 0 /* LinkStyle.Solid */;
addLinkCylinderMesh(addOptions, addParams);
}
const m = mesh_builder_1.MeshBuilder.getMesh(builderState);
if (count === 0)
return { mesh: m };
// re-use boundingSphere if it has not changed much
linear_algebra_1.Vec3.scale(center, center, 1 / count);
const oldBoundingSphere = mesh ? sphere3d_1.Sphere3D.clone(mesh.boundingSphere) : undefined;
if (oldBoundingSphere && linear_algebra_1.Vec3.distance(center, oldBoundingSphere.center) / oldBoundingSphere.radius < 0.1) {
return { mesh: m, boundingSphere: oldBoundingSphere };
}
else {
return { mesh: m };
}
}
;
const AddLinkCylinderState = {
cylinderProps: {
radiusTop: 1,
radiusBottom: 1,
radialSegments: 8,
topCap: false,
bottomCap: false,
},
vShift: (0, linear_algebra_1.Vec3)(),
va: (0, linear_algebra_1.Vec3)(),
vb: (0, linear_algebra_1.Vec3)(),
};
function addLinkCylinderMesh(options, params) {
const { builderState, props, assignNonAdjustedPosition, referencePosition } = options;
const { linkRadius, linkStub, linkStyle, a, b } = params;
const { linkScale, linkSpacing, radialSegments, linkCap, aromaticScale, aromaticSpacing, aromaticDashCount, dashCount, dashScale, dashCap } = props;
const { cylinderProps, vShift, va, vb } = AddLinkCylinderState;
cylinderProps.radialSegments = radialSegments;
linear_algebra_1.Vec3.copy(va, a);
linear_algebra_1.Vec3.copy(vb, b);
v3sub(tmpV12, vb, va);
const dirFlag = v3dot(tmpV12, up) > 0;
const [topCap, bottomCap] = dirFlag ? [linkStub, linkCap] : [linkCap, linkStub];
builderState.currentGroup = params.group;
if (linkStyle === 0 /* LinkStyle.Solid */) {
cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius;
cylinderProps.topCap = topCap;
cylinderProps.bottomCap = bottomCap;
(0, cylinder_1.addCylinder)(builderState, va, vb, 0.5, cylinderProps);
}
else if (linkStyle === 1 /* LinkStyle.Dashed */) {
cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius * dashScale;
cylinderProps.topCap = cylinderProps.bottomCap = dashCap;
(0, cylinder_1.addFixedCountDashedCylinder)(builderState, va, vb, 0.5, dashCount, linkStub, cylinderProps);
}
else if (linkStyle === 2 /* LinkStyle.Double */ || linkStyle === 3 /* LinkStyle.OffsetDouble */ || linkStyle === 4 /* LinkStyle.Triple */ || linkStyle === 5 /* LinkStyle.OffsetTriple */ || linkStyle === 7 /* LinkStyle.Aromatic */ || linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
const order = (linkStyle === 2 /* LinkStyle.Double */ || linkStyle === 3 /* LinkStyle.OffsetDouble */) ? 2 :
(linkStyle === 4 /* LinkStyle.Triple */ || linkStyle === 5 /* LinkStyle.OffsetTriple */) ? 3 : 1.5;
const multiRadius = linkRadius * (linkScale / (0.5 * order));
const absOffset = (linkRadius - multiRadius) * linkSpacing;
calculateShiftDir(vShift, va, vb, referencePosition ? referencePosition() : null);
cylinderProps.topCap = topCap;
cylinderProps.bottomCap = bottomCap;
if (linkStyle === 7 /* LinkStyle.Aromatic */ || linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius;
(0, cylinder_1.addCylinder)(builderState, va, vb, 0.5, cylinderProps);
const aromaticOffset = linkRadius + aromaticScale * linkRadius + aromaticScale * linkRadius * aromaticSpacing;
if (assignNonAdjustedPosition) {
assignNonAdjustedPosition(va, vb);
}
v3setMagnitude(tmpV12, v3sub(tmpV12, vb, va), linkRadius * 0.5);
v3add(va, va, tmpV12);
v3sub(vb, vb, tmpV12);
cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius * aromaticScale;
cylinderProps.topCap = cylinderProps.bottomCap = dashCap;
v3setMagnitude(vShift, vShift, aromaticOffset);
v3sub(va, va, vShift);
v3sub(vb, vb, vShift);
(0, cylinder_1.addFixedCountDashedCylinder)(builderState, va, vb, 0.5, aromaticDashCount, linkStub, cylinderProps);
if (linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
v3setMagnitude(vShift, vShift, aromaticOffset * 2);
v3add(va, va, vShift);
v3add(vb, vb, vShift);
(0, cylinder_1.addFixedCountDashedCylinder)(builderState, va, vb, 0.5, aromaticDashCount, linkStub, cylinderProps);
}
}
else if (linkStyle === 3 /* LinkStyle.OffsetDouble */ || linkStyle === 5 /* LinkStyle.OffsetTriple */) {
const multipleOffset = linkRadius + multiRadius + linkScale * linkRadius * linkSpacing;
v3setMagnitude(vShift, vShift, multipleOffset);
cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius;
(0, cylinder_1.addCylinder)(builderState, va, vb, 0.5, cylinderProps);
v3scale(tmpV12, tmpV12, linkSpacing * linkScale * 0.2);
v3add(va, va, tmpV12);
v3sub(vb, vb, tmpV12);
cylinderProps.radiusTop = cylinderProps.radiusBottom = multiRadius;
cylinderProps.topCap = dirFlag ? linkStub : dashCap;
cylinderProps.bottomCap = dirFlag ? dashCap : linkStub;
v3setMagnitude(vShift, vShift, multipleOffset);
v3sub(va, va, vShift);
v3sub(vb, vb, vShift);
(0, cylinder_1.addCylinder)(builderState, va, vb, 0.5, cylinderProps);
if (order === 3) {
v3setMagnitude(vShift, vShift, multipleOffset * 2);
v3add(va, va, vShift);
v3add(vb, vb, vShift);
(0, cylinder_1.addCylinder)(builderState, va, vb, 0.5, cylinderProps);
}
}
else {
v3setMagnitude(vShift, vShift, absOffset);
cylinderProps.radiusTop = cylinderProps.radiusBottom = multiRadius;
if (order === 3)
(0, cylinder_1.addCylinder)(builderState, va, vb, 0.5, cylinderProps);
(0, cylinder_1.addDoubleCylinder)(builderState, va, vb, 0.5, vShift, cylinderProps);
}
}
else if (linkStyle === 6 /* LinkStyle.Disk */) {
v3scale(tmpV12, tmpV12, 0.475);
v3add(va, va, tmpV12);
v3sub(vb, vb, tmpV12);
cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius;
cylinderProps.topCap = topCap;
cylinderProps.bottomCap = bottomCap;
(0, cylinder_1.addCylinder)(builderState, va, vb, 0.5, cylinderProps);
}
}
;
/**
* Each edge is included twice to allow for coloring/picking
* the half closer to the first vertex, i.e. vertex a.
*/
function createLinkCylinderImpostors(ctx, linkBuilder, props, cylinders) {
const { linkCount, referencePosition, position, style, radius, ignore, stub } = linkBuilder;
if (!linkCount)
return { cylinders: cylinders_1.Cylinders.createEmpty(cylinders) };
const { linkScale, linkSpacing, linkCap, aromaticScale, aromaticSpacing, aromaticDashCount, dashCount, dashScale, dashCap, stubCap, colorMode } = props;
const interpolate = colorMode === 'interpolate';
const colorModeFlag = interpolate === true ? 3 : 2;
const cylindersCountEstimate = linkCount * 2;
const builder = cylinders_builder_1.CylindersBuilder.create(cylindersCountEstimate, cylindersCountEstimate / 4, cylinders);
const va = (0, linear_algebra_1.Vec3)();
const vb = (0, linear_algebra_1.Vec3)();
const vm = (0, linear_algebra_1.Vec3)();
const vShift = (0, linear_algebra_1.Vec3)();
const center = (0, linear_algebra_1.Vec3)();
let count = 0;
for (let edgeIndex = 0, _eI = linkCount; edgeIndex < _eI; ++edgeIndex) {
if (ignore && ignore(edgeIndex))
continue;
position(va, vb, edgeIndex, true);
v3add(center, center, va);
v3add(center, center, vb);
count += 2;
const linkRadius = radius(edgeIndex);
const linkStyle = style ? style(edgeIndex) : 0 /* LinkStyle.Solid */;
const linkStub = stubCap && (stub ? stub(edgeIndex) : false);
if (linkStyle === 0 /* LinkStyle.Solid */) {
v3scale(vm, v3add(vm, va, vb), 0.5);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, colorModeFlag, edgeIndex);
}
else if (linkStyle === 1 /* LinkStyle.Dashed */) {
v3scale(vm, v3add(vm, va, vb), 0.5);
builder.addFixedCountDashes(va, vm, dashCount, dashScale, dashCap, dashCap, linkStub, interpolate, edgeIndex);
}
else if (linkStyle === 2 /* LinkStyle.Double */ || linkStyle === 3 /* LinkStyle.OffsetDouble */ || linkStyle === 4 /* LinkStyle.Triple */ || linkStyle === 5 /* LinkStyle.OffsetTriple */ || linkStyle === 7 /* LinkStyle.Aromatic */ || linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
const order = (linkStyle === 2 /* LinkStyle.Double */ || linkStyle === 3 /* LinkStyle.OffsetDouble */) ? 2 :
(linkStyle === 4 /* LinkStyle.Triple */ || linkStyle === 5 /* LinkStyle.OffsetTriple */) ? 3 : 1.5;
const multiScale = linkScale / (0.5 * order);
const absOffset = (linkRadius - multiScale * linkRadius) * linkSpacing;
v3scale(vm, v3add(vm, va, vb), 0.5);
calculateShiftDir(vShift, va, vb, referencePosition ? referencePosition(edgeIndex) : null);
if (linkStyle === 7 /* LinkStyle.Aromatic */ || linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, colorModeFlag, edgeIndex);
const aromaticOffset = linkRadius + aromaticScale * linkRadius + aromaticScale * linkRadius * aromaticSpacing;
position(va, vb, edgeIndex, false);
v3setMagnitude(tmpV12, v3sub(tmpV12, vb, va), linkRadius * 0.5);
v3add(va, va, tmpV12);
v3setMagnitude(vShift, vShift, aromaticOffset);
v3sub(va, va, vShift);
v3sub(vm, vm, vShift);
builder.addFixedCountDashes(va, vm, aromaticDashCount, aromaticScale, dashCap, dashCap, linkStub, interpolate, edgeIndex);
if (linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
v3setMagnitude(vShift, vShift, aromaticOffset * 2);
v3add(va, va, vShift);
v3add(vm, vm, vShift);
builder.addFixedCountDashes(va, vm, aromaticDashCount, aromaticScale, dashCap, dashCap, linkStub, interpolate, edgeIndex);
}
}
else if (linkStyle === 3 /* LinkStyle.OffsetDouble */ || linkStyle === 5 /* LinkStyle.OffsetTriple */) {
const multipleOffset = linkRadius + multiScale * linkRadius + linkScale * linkRadius * linkSpacing;
v3setMagnitude(vShift, vShift, multipleOffset);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, colorModeFlag, edgeIndex);
v3setMagnitude(tmpV12, v3sub(tmpV12, va, vm), linkRadius / 1.5);
v3sub(va, va, tmpV12);
if (order === 3)
builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], multiScale, linkCap, linkStub, colorModeFlag, edgeIndex);
builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], multiScale, dashCap, linkStub, colorModeFlag, edgeIndex);
}
else {
v3setMagnitude(vShift, vShift, absOffset);
if (order === 3)
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], multiScale, linkCap, linkStub, colorModeFlag, edgeIndex);
builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], multiScale, linkCap, linkStub, colorModeFlag, edgeIndex);
builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], multiScale, linkCap, linkStub, colorModeFlag, edgeIndex);
}
}
else if (linkStyle === 6 /* LinkStyle.Disk */) {
v3scale(tmpV12, v3sub(tmpV12, vm, va), 0.475);
v3add(va, va, tmpV12);
v3sub(vm, vm, tmpV12);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, colorModeFlag, edgeIndex);
}
}
const c = builder.getCylinders();
if (count === 0)
return { cylinders: c };
// re-use boundingSphere if it has not changed much
linear_algebra_1.Vec3.scale(center, center, 1 / count);
const oldBoundingSphere = cylinders ? sphere3d_1.Sphere3D.clone(cylinders.boundingSphere) : undefined;
if (oldBoundingSphere && linear_algebra_1.Vec3.distance(center, oldBoundingSphere.center) / oldBoundingSphere.radius < 0.1) {
return { cylinders: c, boundingSphere: oldBoundingSphere };
}
else {
return { cylinders: c };
}
}
/**
* Each edge is included twice to allow for coloring/picking
* the half closer to the first vertex, i.e. vertex a.
*/
function createLinkLines(ctx, linkBuilder, props, lines) {
const { linkCount, referencePosition, position, style, ignore } = linkBuilder;
if (!linkCount)
return { lines: lines_1.Lines.createEmpty(lines) };
const { linkScale, linkSpacing, aromaticDashCount, dashCount } = props;
const linesCountEstimate = linkCount * 2;
const builder = lines_builder_1.LinesBuilder.create(linesCountEstimate, linesCountEstimate / 4, lines);
const va = (0, linear_algebra_1.Vec3)();
const vb = (0, linear_algebra_1.Vec3)();
const vm = (0, linear_algebra_1.Vec3)();
const vShift = (0, linear_algebra_1.Vec3)();
const center = (0, linear_algebra_1.Vec3)();
let count = 0;
const aromaticOffsetFactor = 4.5;
const multipleOffsetFactor = 3;
for (let edgeIndex = 0, _eI = linkCount; edgeIndex < _eI; ++edgeIndex) {
if (ignore && ignore(edgeIndex))
continue;
position(va, vb, edgeIndex, true);
v3add(center, center, va);
v3add(center, center, vb);
count += 2;
const linkStyle = style ? style(edgeIndex) : 0 /* LinkStyle.Solid */;
if (linkStyle === 0 /* LinkStyle.Solid */) {
v3scale(vm, v3add(vm, va, vb), 0.5);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
}
else if (linkStyle === 1 /* LinkStyle.Dashed */) {
v3scale(vm, v3add(vm, va, vb), 0.5);
builder.addFixedCountDashes(va, vm, dashCount, edgeIndex);
}
else if (linkStyle === 2 /* LinkStyle.Double */ || linkStyle === 3 /* LinkStyle.OffsetDouble */ || linkStyle === 4 /* LinkStyle.Triple */ || linkStyle === 5 /* LinkStyle.OffsetTriple */ || linkStyle === 7 /* LinkStyle.Aromatic */ || linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
const order = linkStyle === 2 /* LinkStyle.Double */ || linkStyle === 3 /* LinkStyle.OffsetDouble */ ? 2 :
linkStyle === 4 /* LinkStyle.Triple */ || linkStyle === 5 /* LinkStyle.OffsetTriple */ ? 3 : 1.5;
const multiRadius = 1 * (linkScale / (0.5 * order));
const absOffset = (1 - multiRadius) * linkSpacing;
v3scale(vm, v3add(vm, va, vb), 0.5);
calculateShiftDir(vShift, va, vb, referencePosition ? referencePosition(edgeIndex) : null);
if (linkStyle === 7 /* LinkStyle.Aromatic */ || linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
const aromaticOffset = absOffset * aromaticOffsetFactor;
v3setMagnitude(tmpV12, v3sub(tmpV12, vb, va), aromaticOffset * 0.5);
v3add(va, va, tmpV12);
v3setMagnitude(vShift, vShift, aromaticOffset);
v3sub(va, va, vShift);
v3sub(vm, vm, vShift);
builder.addFixedCountDashes(va, vm, aromaticDashCount, edgeIndex);
if (linkStyle === 8 /* LinkStyle.MirroredAromatic */) {
v3setMagnitude(vShift, vShift, aromaticOffset * 2);
v3add(va, va, vShift);
v3add(vm, vm, vShift);
builder.addFixedCountDashes(va, vm, aromaticDashCount, edgeIndex);
}
}
else if (linkStyle === 3 /* LinkStyle.OffsetDouble */ || linkStyle === 5 /* LinkStyle.OffsetTriple */) {
v3setMagnitude(vShift, vShift, absOffset * multipleOffsetFactor);
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
v3scale(tmpV12, v3sub(tmpV12, va, vm), linkSpacing * linkScale);
v3sub(va, va, tmpV12);
if (order === 3)
builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], edgeIndex);
builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], edgeIndex);
}
else {
v3setMagnitude(vShift, vShift, absOffset * 1.5);
if (order === 3)
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], edgeIndex);
builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], edgeIndex);
}
}
else if (linkStyle === 6 /* LinkStyle.Disk */) {
v3scale(tmpV12, v3sub(tmpV12, vm, va), 0.475);
v3add(va, va, tmpV12);
v3sub(vm, vm, tmpV12);
// TODO what to do here? Line as disk doesn't work well.
builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
}
}
const l = builder.getLines();
if (count === 0)
return { lines: l };
// re-use boundingSphere if it has not changed much
linear_algebra_1.Vec3.scale(center, center, 1 / count);
const oldBoundingSphere = lines ? sphere3d_1.Sphere3D.clone(lines.boundingSphere) : undefined;
if (oldBoundingSphere && linear_algebra_1.Vec3.distance(center, oldBoundingSphere.center) / oldBoundingSphere.radius < 0.1) {
return { lines: l, boundingSphere: oldBoundingSphere };
}
else {
return { lines: l };
}
}