Browse Source

wip, refactored visual's mark funciton

Alexander Rose 6 years ago
parent
commit
b18571f99d

+ 27 - 7
src/mol-geo/representation/structure/complex-visual.ts

@@ -12,11 +12,12 @@ import { RuntimeContext } from 'mol-task';
 import { LocationIterator } from './visual/util/location-iterator';
 import { createComplexMeshRenderObject, createColors } from './visual/util/common';
 import { StructureMeshProps, StructureProps } from '.';
-import { deepEqual } from 'mol-util';
+import { deepEqual, ValueCell } from 'mol-util';
 import { updateMeshValues, updateRenderableState } from '../util';
 import { PickingId } from '../../util/picking';
-import { Loci } from 'mol-model/loci';
-import { MarkerAction, MarkerData } from '../../util/marker-data';
+import { Loci, isEveryLoci } from 'mol-model/loci';
+import { MarkerAction, applyMarkerAction } from '../../util/marker-data';
+import { Interval } from 'mol-data/int';
 
 export interface  ComplexVisual<P extends StructureProps> extends Visual<Structure, P> { }
 
@@ -25,7 +26,7 @@ export interface ComplexMeshVisualBuilder<P extends StructureMeshProps> {
     createMesh(ctx: RuntimeContext, structure: Structure, props: P, mesh?: Mesh): Promise<Mesh>
     createLocationIterator(structure: Structure): LocationIterator
     getLoci(pickingId: PickingId, structure: Structure, id: number): Loci
-    mark(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData): void
+    mark(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean): boolean
 }
 
 export function ComplexMeshVisual<P extends StructureMeshProps>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> {
@@ -35,6 +36,7 @@ export function ComplexMeshVisual<P extends StructureMeshProps>(builder: Complex
     let currentProps: P
     let mesh: Mesh
     let currentStructure: Structure
+    let locationIt: LocationIterator
 
     return {
         get renderObject () { return renderObject },
@@ -44,7 +46,7 @@ export function ComplexMeshVisual<P extends StructureMeshProps>(builder: Complex
 
             mesh = await createMesh(ctx, currentStructure, currentProps, mesh)
 
-            const locationIt = createLocationIterator(structure)
+            locationIt = createLocationIterator(structure)
             renderObject = createComplexMeshRenderObject(structure, mesh, locationIt, currentProps)
         },
         async update(ctx: RuntimeContext, props: Partial<P>) {
@@ -62,7 +64,7 @@ export function ComplexMeshVisual<P extends StructureMeshProps>(builder: Complex
             }
 
             if (updateColor) {
-                createColors(createLocationIterator(currentStructure), newProps.colorTheme, renderObject.values)
+                createColors(locationIt, newProps.colorTheme, renderObject.values)
             }
 
             updateMeshValues(renderObject.values, newProps)
@@ -75,7 +77,25 @@ export function ComplexMeshVisual<P extends StructureMeshProps>(builder: Complex
             return getLoci(pickingId, currentStructure, renderObject.id)
         },
         mark(loci: Loci, action: MarkerAction) {
-            mark(loci, action, currentStructure, renderObject.values)
+            const { tMarker } = renderObject.values
+            const { elementCount, instanceCount } = locationIt
+
+            function apply(interval: Interval) {
+                const start = Interval.start(interval)
+                const end = Interval.end(interval)
+                return applyMarkerAction(tMarker.ref.value.array, start, end, action)
+            }
+
+            let changed = false
+            if (isEveryLoci(loci)) {
+                apply(Interval.ofBounds(0, elementCount * instanceCount))
+                changed = true
+            } else {
+                changed = mark(loci, currentStructure, apply)
+            }
+            if (changed) {
+                ValueCell.update(tMarker, tMarker.ref.value)
+            }
         },
         destroy() {
             // TODO

+ 26 - 6
src/mol-geo/representation/structure/units-visual.ts

@@ -11,12 +11,13 @@ import { RuntimeContext } from 'mol-task';
 import { PickingId } from '../../util/picking';
 import { LocationIterator } from './visual/util/location-iterator';
 import { Mesh } from '../../shape/mesh';
-import { MarkerData, MarkerAction } from '../../util/marker-data';
-import { Loci } from 'mol-model/loci';
+import { MarkerAction, applyMarkerAction } from '../../util/marker-data';
+import { Loci, isEveryLoci } from 'mol-model/loci';
 import { MeshRenderObject } from 'mol-gl/render-object';
 import { createUnitsMeshRenderObject, createColors } from './visual/util/common';
-import { deepEqual } from 'mol-util';
+import { deepEqual, ValueCell } from 'mol-util';
 import { updateMeshValues, updateRenderableState } from '../util';
+import { Interval } from 'mol-data/int';
 
 export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<Unit.SymmetryGroup, P> { }
 
@@ -31,7 +32,7 @@ export interface UnitsMeshVisualBuilder<P extends UnitsMeshProps> {
     createMesh(ctx: RuntimeContext, unit: Unit, props: P, mesh?: Mesh): Promise<Mesh>
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci
-    mark(loci: Loci, action: MarkerAction, group: Unit.SymmetryGroup, values: MarkerData): void
+    mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean
 }
 
 export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> {
@@ -41,6 +42,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
     let currentProps: P
     let mesh: Mesh
     let currentGroup: Unit.SymmetryGroup
+    let locationIt: LocationIterator
 
     return {
         get renderObject () { return renderObject },
@@ -53,7 +55,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
                 ? await createMesh(ctx, unit, currentProps, mesh)
                 : Mesh.createEmpty(mesh)
 
-            const locationIt = createLocationIterator(group)
+            locationIt = createLocationIterator(group)
             renderObject = createUnitsMeshRenderObject(group, mesh, locationIt, currentProps)
         },
         async update(ctx: RuntimeContext, props: Partial<P>) {
@@ -93,7 +95,25 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
             return getLoci(pickingId, currentGroup, renderObject.id)
         },
         mark(loci: Loci, action: MarkerAction) {
-            mark(loci, action, currentGroup, renderObject.values)
+            const { tMarker } = renderObject.values
+            const { elementCount, instanceCount } = locationIt
+
+            function apply(interval: Interval) {
+                const start = Interval.start(interval)
+                const end = Interval.end(interval)
+                return applyMarkerAction(tMarker.ref.value.array, start, end, action)
+            }
+
+            let changed = false
+            if (isEveryLoci(loci)) {
+                apply(Interval.ofBounds(0, elementCount * instanceCount))
+                changed = true
+            } else {
+                changed = mark(loci, currentGroup, apply)
+            }
+            if (changed) {
+                ValueCell.update(tMarker, tMarker.ref.value)
+            }
         },
         destroy() {
             // TODO

+ 6 - 23
src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts

@@ -4,21 +4,18 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-
 import { Unit, Structure, Link, StructureElement } from 'mol-model/structure';
 import { DefaultStructureProps, ComplexVisual } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
-import { MarkerAction, MarkerData, applyMarkerAction } from '../../../util/marker-data';
-import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
+import { Loci, EmptyLoci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { DefaultMeshProps } from '../../util';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { LocationIterator } from './util/location-iterator';
 import { createLinkCylinderMesh, DefaultLinkCylinderProps, LinkCylinderProps } from './util/link';
-import { OrderedSet } from 'mol-data/int';
+import { OrderedSet, Interval } from 'mol-data/int';
 import { ComplexMeshVisual } from '../complex-visual';
 
 // TODO create seperate visual
@@ -110,31 +107,17 @@ function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
     return EmptyLoci
 }
 
-function markLink(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) {
-    const tMarker = values.tMarker
-
+function markLink(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
     const { getLinkIndex } = structure.carbohydrates
-    const elementCount = structure.carbohydrates.links.length
 
     let changed = false
-    const array = tMarker.ref.value.array
-    if (isEveryLoci(loci)) {
-        if (applyMarkerAction(array, 0, elementCount, action)) {
-            changed = true
-        }
-    } else if (Link.isLoci(loci)) {
+    if (Link.isLoci(loci)) {
         for (const l of loci.links) {
             const idx = getLinkIndex(l.aUnit, l.aUnit.elements[l.aIndex], l.bUnit, l.bUnit.elements[l.bIndex])
             if (idx !== undefined) {
-                if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
-                    changed = true
-                }
+                if (apply(Interval.ofSingleton(idx))) changed = true
             }
         }
-    } else {
-        return
-    }
-    if (changed) {
-        ValueCell.update(tMarker, tMarker.ref.value)
     }
+    return changed
 }

+ 6 - 23
src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts

@@ -4,22 +4,19 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-
 import { Unit, Structure, StructureElement } from 'mol-model/structure';
 import { DefaultStructureProps, ComplexVisual } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
-import { MarkerAction, MarkerData, applyMarkerAction } from '../../../util/marker-data';
-import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
+import { Loci, EmptyLoci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { DefaultMeshProps } from '../../util';
 import { MeshBuilder } from '../../../shape/mesh-builder';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants';
 import { LocationIterator } from './util/location-iterator';
-import { OrderedSet } from 'mol-data/int';
+import { OrderedSet, Interval } from 'mol-data/int';
 import { ComplexMeshVisual } from '../complex-visual';
 
 const t = Mat4.identity()
@@ -163,33 +160,19 @@ function getCarbohydrateLoci(pickingId: PickingId, structure: Structure, id: num
     return EmptyLoci
 }
 
-function markCarbohydrate(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) {
-    const tMarker = values.tMarker
-
+function markCarbohydrate(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
     const { getElementIndex } = structure.carbohydrates
-    const elementCount = structure.carbohydrates.elements.length * 2
 
     let changed = false
-    const array = tMarker.ref.value.array
-    if (isEveryLoci(loci)) {
-        if (applyMarkerAction(array, 0, elementCount, action)) {
-            changed = true
-        }
-    } else if (StructureElement.isLoci(loci)) {
+    if (StructureElement.isLoci(loci)) {
         for (const e of loci.elements) {
             OrderedSet.forEach(e.indices, index => {
                 const idx = getElementIndex(e.unit, e.unit.elements[index])
                 if (idx !== undefined) {
-                    if (applyMarkerAction(array, idx * 2, idx * 2 + 2, action) && !changed) {
-                        changed = true
-                    }
+                    if (apply(Interval.ofBounds(idx * 2, idx * 2 + 2))) changed = true
                 }
             })
         }
-    } else {
-        return
-    }
-    if (changed) {
-        ValueCell.update(tMarker, tMarker.ref.value)
     }
+    return changed
 }

+ 6 - 26
src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts

@@ -4,8 +4,6 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-
 import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { DefaultStructureProps, ComplexVisual } from '..';
 import { RuntimeContext } from 'mol-task'
@@ -13,11 +11,11 @@ import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } f
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
-import { MarkerAction, applyMarkerAction, MarkerData } from '../../../util/marker-data';
+import { Loci, EmptyLoci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { ComplexMeshVisual } from '../complex-visual';
 import { LocationIterator } from './util/location-iterator';
+import { Interval } from 'mol-data/int';
 
 async function createCrossLinkRestraintCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
 
@@ -51,9 +49,6 @@ export const DefaultCrossLinkRestraintProps = {
 }
 export type CrossLinkRestraintProps = typeof DefaultCrossLinkRestraintProps
 
-// TODO update & create in-place
-// if (currentProps.radialSegments !== newProps.radialSegments) return false
-
 export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintProps> {
     return ComplexMeshVisual<CrossLinkRestraintProps>({
         defaultProps: DefaultCrossLinkRestraintProps,
@@ -96,34 +91,19 @@ function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
     return EmptyLoci
 }
 
-function markLink(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) {
-    const tMarker = values.tMarker
-
+function markLink(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
     const crossLinks = structure.crossLinkRestraints
-    const elementCount = crossLinks.count
-    const instanceCount = 1
 
     let changed = false
-    const array = tMarker.ref.value.array
-    if (isEveryLoci(loci)) {
-        applyMarkerAction(array, 0, elementCount * instanceCount, action)
-        changed = true
-    } else if (Link.isLoci(loci)) {
+    if (Link.isLoci(loci)) {
         for (const b of loci.links) {
             const indices = crossLinks.getPairIndices(b.aIndex, b.aUnit, b.bIndex, b.bUnit)
             if (indices) {
                 for (let i = 0, il = indices.length; i < il; ++i) {
-                    const idx = indices[i]
-                    if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
-                        changed = true
-                    }
+                    if (apply(Interval.ofSingleton(indices[i]))) changed = true
                 }
             }
         }
-    } else {
-        return
-    }
-    if (changed) {
-        ValueCell.update(tMarker, tMarker.ref.value)
     }
+    return changed
 }

+ 3 - 2
src/mol-geo/representation/structure/visual/element-point.ts

@@ -13,7 +13,7 @@ import { RuntimeContext } from 'mol-task'
 import { UnitsVisual, DefaultStructureProps } from '..';
 import VertexMap from '../../../shape/vertex-map';
 import { SizeTheme } from '../../../theme';
-import { markElement, getElementLoci } from './util/element';
+import { getElementLoci } from './util/element';
 import { createTransforms, createColors, createSizes } from './util/common';
 import { deepEqual, defaults } from 'mol-util';
 import { SortedArray } from 'mol-data/int';
@@ -133,7 +133,8 @@ export default function PointVisual(): UnitsVisual<PointProps> {
             return getElementLoci(pickingId, currentGroup, renderObject.id)
         },
         mark(loci: Loci, action: MarkerAction) {
-            markElement(loci, action, currentGroup, renderObject.values)
+            // TODO
+            // markElement(loci, action, currentGroup, renderObject.values)
         },
         destroy() {
             // TODO

+ 8 - 27
src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts

@@ -4,8 +4,6 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-
 import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { DefaultStructureProps, ComplexVisual } from '..';
 import { RuntimeContext } from 'mol-task'
@@ -13,11 +11,11 @@ import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } f
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
-import { MarkerAction, applyMarkerAction, MarkerData } from '../../../util/marker-data';
+import { Loci, EmptyLoci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { LinkIterator } from './util/location-iterator';
 import { ComplexMeshVisual } from '../complex-visual';
+import { Interval } from 'mol-data/int';
 
 async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
     const links = structure.links
@@ -72,32 +70,15 @@ function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
     return EmptyLoci
 }
 
-function markLink(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) {
-    const tMarker = values.tMarker
-
-    const links = structure.links
-    const elementCount = links.bondCount
-
+function markLink(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
     let changed = false
-    const array = tMarker.ref.value.array
-    if (isEveryLoci(loci)) {
-        if (applyMarkerAction(array, 0, elementCount, action)) {
-            changed = true
-        }
-    } else if (Link.isLoci(loci)) {
+    if (Link.isLoci(loci)) {
         for (const b of loci.links) {
-            const _idx = structure.links.getBondIndex(b.aIndex, b.aUnit, b.bIndex, b.bUnit)
-            if (_idx !== -1) {
-                const idx = _idx
-                if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
-                    changed = true
-                }
+            const idx = structure.links.getBondIndex(b.aIndex, b.aUnit, b.bIndex, b.bUnit)
+            if (idx !== -1) {
+                if (apply(Interval.ofSingleton(idx))) changed = true
             }
         }
-    } else {
-        return
-    }
-    if (changed) {
-        ValueCell.update(tMarker, tMarker.ref.value)
     }
+    return changed
 }

+ 8 - 26
src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts

@@ -5,8 +5,6 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-
 import { Unit, Link, StructureElement } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
@@ -14,11 +12,11 @@ import { DefaultLinkCylinderProps, LinkCylinderProps, createLinkCylinderMesh } f
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
-import { MarkerAction, applyMarkerAction, MarkerData } from '../../../util/marker-data';
+import { Loci, EmptyLoci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { LinkIterator } from './util/location-iterator';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
+import { Interval } from 'mol-data/int';
 
 async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, props: LinkCylinderProps, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
@@ -88,36 +86,20 @@ function getLinkLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number
     return EmptyLoci
 }
 
-function markLink(loci: Loci, action: MarkerAction, group: Unit.SymmetryGroup, values: MarkerData) {
-    const tMarker = values.tMarker
+function markLink(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean) {
     const unit = group.units[0]
-    if (!Unit.isAtomic(unit)) return
-
-    const elementCount = unit.links.edgeCount * 2
-    const instanceCount = group.units.length
 
     let changed = false
-    const array = tMarker.ref.value.array
-    if (isEveryLoci(loci)) {
-        applyMarkerAction(array, 0, elementCount * instanceCount, action)
-        changed = true
-    } else if (Link.isLoci(loci)) {
+    if (Unit.isAtomic(unit) && Link.isLoci(loci)) {
         for (const b of loci.links) {
             const unitIdx = Unit.findUnitById(b.aUnit.id, group.units)
             if (unitIdx !== -1) {
-                const _idx = unit.links.getDirectedEdgeIndex(b.aIndex, b.bIndex)
-                if (_idx !== -1) {
-                    const idx = _idx
-                    if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
-                        changed = true
-                    }
+                const idx = unit.links.getDirectedEdgeIndex(b.aIndex, b.bIndex)
+                if (idx !== -1) {
+                    if (apply(Interval.ofSingleton(idx))) changed = true
                 }
             }
         }
-    } else {
-        return
-    }
-    if (changed) {
-        ValueCell.update(tMarker, tMarker.ref.value)
     }
+    return changed
 }

+ 9 - 25
src/mol-geo/representation/structure/visual/util/element.ts

@@ -11,9 +11,8 @@ import { RuntimeContext } from 'mol-task';
 import { sphereVertexCount } from '../../../../primitive/sphere';
 import { Mesh } from '../../../../shape/mesh';
 import { MeshBuilder } from '../../../../shape/mesh-builder';
-import { ValueCell, defaults } from 'mol-util';
-import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
-import { MarkerAction, applyMarkerAction, MarkerData } from '../../../../util/marker-data';
+import { defaults } from 'mol-util';
+import { Loci, EmptyLoci } from 'mol-model/loci';
 import { Interval, OrderedSet } from 'mol-data/int';
 import { getPhysicalRadius } from '../../../../theme/structure/size/physical';
 import { PickingId } from '../../../../util/picking';
@@ -63,43 +62,28 @@ export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, p
     return meshBuilder.getMesh()
 }
 
-export function markElement(loci: Loci, action: MarkerAction, group: Unit.SymmetryGroup, values: MarkerData) {
-    const tMarker = values.tMarker
-
+export function markElement(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean) {
     const elementCount = group.elements.length
-    const instanceCount = group.units.length
 
     let changed = false
-    const array = tMarker.ref.value.array
-    if (isEveryLoci(loci)) {
-        applyMarkerAction(array, 0, elementCount * instanceCount, action)
-        changed = true
-    } else if (StructureElement.isLoci(loci)) {
+    if (StructureElement.isLoci(loci)) {
         for (const e of loci.elements) {
             const unitIdx = Unit.findUnitById(e.unit.id, group.units)
             if (unitIdx !== -1) {
                 if (Interval.is(e.indices)) {
-                    const idxStart = unitIdx * elementCount + Interval.start(e.indices);
-                    const idxEnd = unitIdx * elementCount + Interval.end(e.indices);
-                    if (applyMarkerAction(array, idxStart, idxEnd, action) && !changed) {
-                        changed = true
-                    }
+                    const start = unitIdx * elementCount + Interval.start(e.indices);
+                    const end = unitIdx * elementCount + Interval.end(e.indices);
+                    if (apply(Interval.ofBounds(start, end))) changed = true
                 } else {
                     for (let i = 0, _i = e.indices.length; i < _i; i++) {
                         const idx = unitIdx * elementCount + e.indices[i];
-                        if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
-                            changed = true
-                        }
+                        if (apply(Interval.ofSingleton(idx))) changed = true
                     }
                 }
             }
         }
-    } else {
-        return
-    }
-    if (changed) {
-        ValueCell.update(tMarker, tMarker.ref.value)
     }
+    return changed
 }
 
 export function getElementLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number) {