Browse Source

mol-repr: link cylinder offset based on atom radius

David Sehnal 4 years ago
parent
commit
04e17872d0

+ 31 - 9
src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts

@@ -38,6 +38,24 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
 
     if (!edgeCount) return Mesh.createEmpty(mesh);
 
+    const delta = Vec3();
+
+    const radiusA = (edgeIndex: number) => {
+        const b = edges[edgeIndex];
+        tmpLoc.structure = structure;
+        tmpLoc.unit = structure.unitMap.get(b.unitA);
+        tmpLoc.element = tmpLoc.unit.elements[b.indexA];
+        return theme.size.size(tmpLoc) * sizeFactor;
+    };
+
+    const radiusB = (edgeIndex: number) => {
+        const b = edges[edgeIndex];
+        tmpLoc.structure = structure;
+        tmpLoc.unit = structure.unitMap.get(b.unitB);
+        tmpLoc.element = tmpLoc.unit.elements[b.indexB];
+        return theme.size.size(tmpLoc) * sizeFactor;
+    };
+
     const builderProps = {
         linkCount: edgeCount,
         referencePosition: (edgeIndex: number) => {
@@ -63,8 +81,20 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
             const b = edges[edgeIndex];
             const uA = structure.unitMap.get(b.unitA);
             const uB = structure.unitMap.get(b.unitB);
+
+            const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
+            const r = Math.min(rA, rB) * sizeAspectRatio;
+            const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
+            const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
+
             uA.conformation.position(uA.elements[b.indexA], posA);
             uB.conformation.position(uB.elements[b.indexB], posB);
+
+            if (oA <= 0.01 && oB <= 0.01) return;
+
+            Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
+            Vec3.scaleAndAdd(posA, posA, delta, oA);
+            Vec3.scaleAndAdd(posB, posB, delta, -oB);
         },
         style: (edgeIndex: number) => {
             const o = edges[edgeIndex].props.order;
@@ -81,15 +111,7 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
             }
         },
         radius: (edgeIndex: number) => {
-            const b = edges[edgeIndex];
-            tmpLoc.structure = structure;
-            tmpLoc.unit = structure.unitMap.get(b.unitA);
-            tmpLoc.element = tmpLoc.unit.elements[b.indexA];
-            const sizeA = theme.size.size(tmpLoc);
-            tmpLoc.unit = structure.unitMap.get(b.unitB);
-            tmpLoc.element = tmpLoc.unit.elements[b.indexB];
-            const sizeB = theme.size.size(tmpLoc);
-            return Math.min(sizeA, sizeB) * sizeFactor * sizeAspectRatio;
+            return Math.min(radiusA(edgeIndex), radiusB(edgeIndex)) * sizeAspectRatio;
         },
         ignore: makeInterBondIgnoreTest(structure, props)
     };

+ 23 - 6
src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts

@@ -36,9 +36,19 @@ function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structu
 
     if (!edgeCount) return Mesh.createEmpty(mesh);
 
-    const vRef = Vec3();
+    const vRef = Vec3(), delta = Vec3();
     const pos = unit.conformation.invariantPosition;
 
+    const radiusA = (edgeIndex: number) => {
+        location.element = elements[a[edgeIndex]];
+        return theme.size.size(location) * sizeFactor;
+    };
+
+    const radiusB = (edgeIndex: number) => {
+        location.element = elements[b[edgeIndex]];
+        return theme.size.size(location) * sizeFactor;
+    };
+
     const builderProps = {
         linkCount: edgeCount * 2,
         referencePosition: (edgeIndex: number) => {
@@ -59,8 +69,19 @@ function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structu
             return null;
         },
         position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
+            const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
+            const r = Math.min(rA, rB) * sizeAspectRatio;
+            const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
+            const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
+                        
             pos(elements[a[edgeIndex]], posA);
             pos(elements[b[edgeIndex]], posB);
+
+            if (oA <= 0.01 && oB <= 0.01) return;
+
+            Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
+            Vec3.scaleAndAdd(posA, posA, delta, oA);
+            Vec3.scaleAndAdd(posB, posB, delta, -oB);
         },
         style: (edgeIndex: number) => {
             const o = _order[edgeIndex];
@@ -77,11 +98,7 @@ function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structu
             }
         },
         radius: (edgeIndex: number) => {
-            location.element = elements[a[edgeIndex]];
-            const sizeA = theme.size.size(location);
-            location.element = elements[b[edgeIndex]];
-            const sizeB = theme.size.size(location);
-            return Math.min(sizeA, sizeB) * sizeFactor * sizeAspectRatio;
+            return Math.min(radiusA(edgeIndex), radiusB(edgeIndex)) * sizeAspectRatio;
         },
         ignore: makeIntraBondIgnoreTest(unit, props)
     };

+ 1 - 1
src/mol-repr/structure/visual/util/link.ts

@@ -65,7 +65,7 @@ export function calculateShiftDir (out: Vec3, v1: Vec3, v2: Vec3, v3: Vec3 | nul
 export interface LinkBuilderProps {
     linkCount: number
     position: (posA: Vec3, posB: Vec3, edgeIndex: number) => void
-    radius: (edgeIndex: number) => number
+    radius: (edgeIndex: number) => number,
     referencePosition?: (edgeIndex: number) => Vec3 | null
     style?: (edgeIndex: number) => LinkStyle
     ignore?: (edgeIndex: number) => boolean