From db350ddfd32f708c018d5f3e35b2bb3c1da97ee5 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alexander.rose@weirdbyte.de>
Date: Mon, 30 Aug 2021 22:42:16 -0700
Subject: [PATCH] loci/marking performance improvements

- use Interval for ranges instead of SortedArray
- pre-check if loci overlaps with unit visual
---
 CHANGELOG.md                                  | 10 ++++++--
 .../structure/structure/element/loci.ts       | 23 +++++++++++--------
 src/mol-repr/structure/units-visual.ts        | 13 ++++++++++-
 src/mol-repr/structure/visual/util/element.ts |  3 ++-
 src/mol-repr/visual.ts                        |  1 +
 5 files changed, 36 insertions(+), 14 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 72167849c..2117ef1b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,9 +10,15 @@ Note that since we don't clearly distinguish between a public and private interf
 - Add ``Mesh`` processing helper ``.smoothEdges``
 - Smooth border of molecular-surface with ``includeParent`` enabled
 - Hide ``includeParent`` option from gaussian-surface visuals (not particularly useful)
-- Improved ``StructureElement.Loci.size`` performance (for marking large cellpack models)
 - Fix new ``TransformData`` issues (camera/bounding helper not showing up)
-- Improve marking performance (avoid superfluous calls to ``StructureElement.Loci.isWholeStructure``)
+- Improve marking performance
+    - Avoid superfluous calls to ``StructureElement.Loci.isWholeStructure``
+    - Check if loci is superset of visual
+    - Check if loci overlaps with unit visual
+    - Ensure ``Interval`` is used for ranges instead of ``SortedArray``
+    - Inline ``StructureElement.Loci.size`` code
+    - Add uniform marker type
+    - Special case for reversing previous mark
 - Add optional marking pass
     - Outlines visible and hidden parts of highlighted/selected groups
     - Add highlightStrength/selectStrength renderer params
diff --git a/src/mol-model/structure/structure/element/loci.ts b/src/mol-model/structure/structure/element/loci.ts
index cce5eff09..cb68abbdd 100644
--- a/src/mol-model/structure/structure/element/loci.ts
+++ b/src/mol-model/structure/structure/element/loci.ts
@@ -253,6 +253,14 @@ export namespace Loci {
         return isSubset;
     }
 
+    function makeIndexSet(newIndices: ArrayLike<UnitIndex>): OrderedSet<UnitIndex> {
+        if (newIndices.length > 3 && SortedArray.isRange(newIndices)) {
+            return Interval.ofRange(newIndices[0], newIndices[newIndices.length - 1]);
+        } else {
+            return SortedArray.ofSortedArray(newIndices);
+        }
+    }
+
     export function extendToWholeResidues(loci: Loci, restrictToConformation?: boolean): Loci {
         const elements: Loci['elements'][0][] = [];
         const residueAltIds = new Set<string>();
@@ -297,7 +305,7 @@ export namespace Loci {
                     }
                 }
 
-                elements[elements.length] = { unit: lociElement.unit, indices: SortedArray.ofSortedArray(newIndices) };
+                elements[elements.length] = { unit: lociElement.unit, indices: makeIndexSet(newIndices) };
             } else {
                 // coarse elements are already by-residue
                 elements[elements.length] = lociElement;
@@ -319,14 +327,6 @@ export namespace Loci {
         return element.unit.elements.length === OrderedSet.size(element.indices);
     }
 
-    function makeIndexSet(newIndices: number[]): OrderedSet<UnitIndex> {
-        if (newIndices.length > 12 && newIndices[newIndices.length - 1] - newIndices[0] === newIndices.length - 1) {
-            return Interval.ofRange(newIndices[0], newIndices[newIndices.length - 1]);
-        } else {
-            return SortedArray.ofSortedArray(newIndices);
-        }
-    }
-
     function collectChains(unit: Unit, chainIndices: Set<ChainIndex>, elements: Loci['elements'][0][]) {
         const { index } = getChainSegments(unit);
         const xs = unit.elements;
@@ -470,7 +470,10 @@ export namespace Loci {
     }
 
     function getUnitIndices(elements: SortedArray<ElementIndex>, indices: SortedArray<ElementIndex>) {
-        return OrderedSet.ofSortedArray(SortedArray.indicesOf<ElementIndex, UnitIndex>(elements, indices));
+        if (SortedArray.areEqual(elements, indices) && SortedArray.isRange(elements)) {
+            return Interval.ofLength(elements.length);
+        }
+        return makeIndexSet(SortedArray.indicesOf<ElementIndex, UnitIndex>(elements, indices));
     }
 
     export function extendToAllInstances(loci: Loci): Loci {
diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts
index 8acaf871b..e4105f9eb 100644
--- a/src/mol-repr/structure/units-visual.ts
+++ b/src/mol-repr/structure/units-visual.ts
@@ -290,7 +290,18 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
             return renderObject ? getLoci(pickingId, currentStructureGroup, renderObject.id) : EmptyLoci;
         },
         mark(loci: Loci, action: MarkerAction) {
-            return Visual.mark(renderObject, loci, action, lociApply, previousMark);
+            let hasInvariantId = true;
+            if (StructureElement.Loci.is(loci)) {
+                hasInvariantId = false;
+                const { invariantId } = currentStructureGroup.group.units[0];
+                for (const e of loci.elements) {
+                    if (e.unit.invariantId === invariantId) {
+                        hasInvariantId = true;
+                        break;
+                    }
+                }
+            }
+            return hasInvariantId ? Visual.mark(renderObject, loci, action, lociApply, previousMark) : false;
         },
         setVisibility(visible: boolean) {
             Visual.setVisibility(renderObject, visible);
diff --git a/src/mol-repr/structure/visual/util/element.ts b/src/mol-repr/structure/visual/util/element.ts
index b9a8ea919..34ca57ffa 100644
--- a/src/mol-repr/structure/visual/util/element.ts
+++ b/src/mol-repr/structure/visual/util/element.ts
@@ -164,8 +164,9 @@ export function eachElement(loci: Loci, structureGroup: StructureGroup, apply: (
     const { structure, group } = structureGroup;
     if (!Structure.areEquivalent(loci.structure, structure)) return false;
     const elementCount = group.elements.length;
+    const { unitIndexMap } = group;
     for (const e of loci.elements) {
-        const unitIdx = group.unitIndexMap.get(e.unit.id);
+        const unitIdx = unitIndexMap.get(e.unit.id);
         if (unitIdx !== undefined) {
             const offset = unitIdx * elementCount; // to target unit instance
             if (Interval.is(e.indices)) {
diff --git a/src/mol-repr/visual.ts b/src/mol-repr/visual.ts
index 13689cee5..163a47dfa 100644
--- a/src/mol-repr/visual.ts
+++ b/src/mol-repr/visual.ts
@@ -84,6 +84,7 @@ namespace Visual {
                 intervalSize += Interval.size(interval);
                 return true;
             }, true);
+            if (intervalSize === 0) return false;
             if (intervalSize === count) loci = EveryLoci;
         }
 
-- 
GitLab