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