From 582a0e2a38a9c4573566927537dabac38748092c Mon Sep 17 00:00:00 2001
From: Alexander Rose <alexander.rose@weirdbyte.de>
Date: Sat, 29 Jan 2022 11:13:19 -0800
Subject: [PATCH] fix Sphere.expand for highly directional extrema

---
 CHANGELOG.md                                  |  2 ++
 src/mol-math/geometry/primitives/sphere3d.ts  | 26 ++++++++++++++++---
 .../visual/molecular-surface-mesh.ts          |  4 +--
 3 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 993d1e72f..28d051aa7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Fix color smoothing of elongated structures (by fixing ``Sphere.expand`` for spheres with highly directional extrema)
+
 ## [v3.0.1] - 2022-01-27
 
 - Fix marking pass not working with ``transparentBackground``
diff --git a/src/mol-math/geometry/primitives/sphere3d.ts b/src/mol-math/geometry/primitives/sphere3d.ts
index 1f4287627..8356dbf0d 100644
--- a/src/mol-math/geometry/primitives/sphere3d.ts
+++ b/src/mol-math/geometry/primitives/sphere3d.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -11,6 +11,7 @@ import { OrderedSet } from '../../../mol-data/int';
 import { NumberArray, PickRequired } from '../../../mol-util/type-helpers';
 import { Box3D } from './box3d';
 import { Axes3D } from './axes3d';
+import { PrincipalAxes } from '../../linear-algebra/matrix/principal-axes';
 
 interface Sphere3D {
     center: Vec3,
@@ -202,11 +203,28 @@ namespace Sphere3D {
             return out;
         }
         if (hasExtrema(sphere)) {
+            const positions = new Float32Array(sphere.extrema.length * 3);
+            for (let i = 0; i < sphere.extrema.length; i++) {
+                Vec3.toArray(sphere.extrema[i], positions, i * 3);
+            }
+
+            const axes = PrincipalAxes.calculateMomentsAxes(positions);
+            Axes3D.scale(axes, Axes3D.normalize(axes, axes), delta);
+
             setExtrema(out, sphere.extrema.map(e => {
                 Vec3.sub(tmpDir, e, sphere.center);
-                const dist = Vec3.distance(sphere.center, e);
-                Vec3.normalize(tmpDir, tmpDir);
-                return Vec3.scaleAndAdd(Vec3(), sphere.center, tmpDir, dist + delta);
+                const out = Vec3.clone(e);
+
+                const sA = Vec3.dot(tmpDir, axes.dirA) < 0 ? -1 : 1;
+                Vec3.scaleAndAdd(out, out, axes.dirA, sA);
+
+                const sB = Vec3.dot(tmpDir, axes.dirB) < 0 ? -1 : 1;
+                Vec3.scaleAndAdd(out, out, axes.dirB, sB);
+
+                const sC = Vec3.dot(tmpDir, axes.dirC) < 0 ? -1 : 1;
+                Vec3.scaleAndAdd(out, out, axes.dirC, sC);
+
+                return out;
             }));
         }
         return out;
diff --git a/src/mol-repr/structure/visual/molecular-surface-mesh.ts b/src/mol-repr/structure/visual/molecular-surface-mesh.ts
index 08f329686..306bccb0c 100644
--- a/src/mol-repr/structure/visual/molecular-surface-mesh.ts
+++ b/src/mol-repr/structure/visual/molecular-surface-mesh.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -57,7 +57,7 @@ async function createMolecularSurfaceMesh(ctx: VisualContext, unit: Unit, struct
     Mesh.transform(surface, transform);
     if (ctx.webgl && !ctx.webgl.isWebGL2) Mesh.uniformTriangleGroup(surface);
 
-    const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.probeRadius + getUnitExtraRadius(unit));
+    const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, getUnitExtraRadius(unit));
     surface.setBoundingSphere(sphere);
     (surface.meta as MolecularSurfaceMeta).resolution = resolution;
 
-- 
GitLab