From 68623ccc11c776ebc72bd2130db0d0dc600a08c4 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Tue, 14 Aug 2018 13:47:19 -0700
Subject: [PATCH] wip, visuals

---
 src/mol-app/ui/transform/backbone.tsx         |  1 -
 src/mol-app/ui/transform/carbohydrate.tsx     |  1 -
 src/mol-app/ui/transform/cartoon.tsx          |  1 -
 src/mol-app/ui/transform/spacefill.tsx        | 18 ++++++++-
 .../structure/complex-representation.ts       |  2 +-
 .../structure/complex-visual.ts               | 40 ++++++++++++++-----
 src/mol-geo/representation/structure/index.ts | 17 ++++++++
 .../structure/units-representation.ts         |  2 +-
 .../representation/structure/units-visual.ts  | 39 ++++++++++--------
 .../visual/carbohydrate-link-cylinder.ts      |  7 +++-
 .../visual/carbohydrate-symbol-mesh.ts        | 11 +++--
 .../visual/cross-link-restraint-cylinder.ts   | 11 +++--
 .../structure/visual/element-sphere.ts        |  7 +++-
 .../visual/inter-unit-link-cylinder.ts        | 11 +++--
 .../visual/intra-unit-link-cylinder.ts        |  3 +-
 .../structure/visual/nucleotide-block-mesh.ts |  3 +-
 .../visual/polymer-backbone-cylinder.ts       |  3 +-
 .../visual/polymer-direction-wedge.ts         |  3 +-
 .../structure/visual/polymer-gap-cylinder.ts  |  3 +-
 .../structure/visual/polymer-trace-mesh.ts    |  3 +-
 .../visual/util/location-iterator.ts          | 13 ++++++
 21 files changed, 140 insertions(+), 59 deletions(-)

diff --git a/src/mol-app/ui/transform/backbone.tsx b/src/mol-app/ui/transform/backbone.tsx
index 6af3137f9..f5c3f072c 100644
--- a/src/mol-app/ui/transform/backbone.tsx
+++ b/src/mol-app/ui/transform/backbone.tsx
@@ -60,7 +60,6 @@ export class Backbone extends View<Controller<any>, BackboneState, { transform:
     }
 
     update(state?: Partial<BackboneState>) {
-        console.log(state)
         const { transform, entity, ctx } = this.props
         const newState = { ...this.state, ...state }
         this.setState(newState)
diff --git a/src/mol-app/ui/transform/carbohydrate.tsx b/src/mol-app/ui/transform/carbohydrate.tsx
index d672d4d81..34392b1f1 100644
--- a/src/mol-app/ui/transform/carbohydrate.tsx
+++ b/src/mol-app/ui/transform/carbohydrate.tsx
@@ -68,7 +68,6 @@ export class Carbohydrate extends View<Controller<any>, CarbohydrateState, { tra
     }
 
     update(state?: Partial<CarbohydrateState>) {
-        console.log(state)
         const { transform, entity, ctx } = this.props
         const newState = { ...this.state, ...state }
         this.setState(newState)
diff --git a/src/mol-app/ui/transform/cartoon.tsx b/src/mol-app/ui/transform/cartoon.tsx
index 32dfeba9a..4b5e2cb8f 100644
--- a/src/mol-app/ui/transform/cartoon.tsx
+++ b/src/mol-app/ui/transform/cartoon.tsx
@@ -60,7 +60,6 @@ export class Cartoon extends View<Controller<any>, CartoonState, { transform: Ca
     }
 
     update(state?: Partial<CartoonState>) {
-        console.log(state)
         const { transform, entity, ctx } = this.props
         const newState = { ...this.state, ...state }
         this.setState(newState)
diff --git a/src/mol-app/ui/transform/spacefill.tsx b/src/mol-app/ui/transform/spacefill.tsx
index d4a4d9e57..49bd5578f 100644
--- a/src/mol-app/ui/transform/spacefill.tsx
+++ b/src/mol-app/ui/transform/spacefill.tsx
@@ -46,7 +46,7 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform
         detail: 2,
         colorTheme: { name: 'element-symbol' } as ColorThemeProps,
         colorValue: 0x000000,
-        sizeTheme: { name: 'uniform' } as SizeThemeProps,
+        sizeTheme: { name: 'uniform', factor: 1 } as SizeThemeProps,
         visible: true,
         alpha: 1,
         depthMask: true,
@@ -60,7 +60,6 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform
     }
 
     update(state?: Partial<SpacefillState>) {
-        console.log(state)
         const { transform, entity, ctx } = this.props
         const newState = { ...this.state, ...state }
         this.setState(newState)
@@ -220,6 +219,21 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform
                                 />
                             </div>
                         </div>
+                        <div className='molstar-control-row molstar-options-group'>
+                            <div>
+                                <Slider
+                                    value={this.state.sizeTheme.factor || 1}
+                                    label='Size factor'
+                                    min={0.1}
+                                    max={3}
+                                    step={0.01}
+                                    callOnChangeWhileSliding={true}
+                                    onChange={value => this.update({
+                                        sizeTheme: { ...this.state.sizeTheme, factor: value }
+                                    })}
+                                />
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>
diff --git a/src/mol-geo/representation/structure/complex-representation.ts b/src/mol-geo/representation/structure/complex-representation.ts
index 5bc2b81a2..d19f96b26 100644
--- a/src/mol-geo/representation/structure/complex-representation.ts
+++ b/src/mol-geo/representation/structure/complex-representation.ts
@@ -33,7 +33,7 @@ export function ComplexRepresentation<P extends StructureProps>(visualCtor: () =
                     await update(_props)
                 } else {
                     if (!await visual.update(ctx, _props)) {
-                        await visual.create(ctx, _structure, _props)
+                        await visual.create(ctx, structure, _props)
                     }
                 }
             }
diff --git a/src/mol-geo/representation/structure/complex-visual.ts b/src/mol-geo/representation/structure/complex-visual.ts
index 71dfdd7bf..083a610e7 100644
--- a/src/mol-geo/representation/structure/complex-visual.ts
+++ b/src/mol-geo/representation/structure/complex-visual.ts
@@ -11,7 +11,7 @@ import { Mesh } from '../../shape/mesh';
 import { RuntimeContext } from 'mol-task';
 import { LocationIterator } from './visual/util/location-iterator';
 import { createComplexMeshRenderObject, createColors } from './visual/util/common';
-import { StructureMeshProps, StructureProps } from '.';
+import { StructureProps, DefaultStructureMeshProps, MeshUpdateState } from '.';
 import { deepEqual, ValueCell } from 'mol-util';
 import { updateMeshValues, updateRenderableState } from '../util';
 import { PickingId } from '../../util/picking';
@@ -21,16 +21,23 @@ import { Interval } from 'mol-data/int';
 
 export interface  ComplexVisual<P extends StructureProps> extends Visual<Structure, P> { }
 
-export interface ComplexMeshVisualBuilder<P extends StructureMeshProps> {
+export const DefaultComplexMeshProps = {
+    ...DefaultStructureMeshProps
+}
+export type ComplexMeshProps = typeof DefaultComplexMeshProps
+
+export interface ComplexMeshVisualBuilder<P extends ComplexMeshProps> {
     defaultProps: P
     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, structure: Structure, apply: (interval: Interval) => boolean): boolean
+    mark(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean): boolean,
+    setUpdateState(state: MeshUpdateState, newProps: P, currentProps: P): void
 }
 
-export function ComplexMeshVisual<P extends StructureMeshProps>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> {
-    const { defaultProps, createMesh, createLocationIterator, getLoci, mark } = builder
+export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> {
+    const { defaultProps, createMesh, createLocationIterator, getLoci, mark, setUpdateState } = builder
+    const updateState = MeshUpdateState.create()
 
     let renderObject: MeshRenderObject
     let currentProps: P
@@ -54,16 +61,27 @@ export function ComplexMeshVisual<P extends StructureMeshProps>(builder: Complex
 
             if (!renderObject) return false
 
-            let updateColor = false
+            locationIt.reset()
+            MeshUpdateState.reset(updateState)
+            setUpdateState(updateState, newProps, currentProps)
 
-            // TODO create in-place
-            // if (currentProps.radialSegments !== newProps.radialSegments) return false
+            if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) {
+                updateState.createMesh = true
+            }
 
             if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
-                updateColor = true
+                updateState.updateColor = true
+            }
+
+            //
+
+            if (updateState.createMesh) {
+                mesh = await createMesh(ctx, currentStructure, newProps, mesh)
+                ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
+                updateState.updateColor = true
             }
 
-            if (updateColor) {
+            if (updateState.updateColor) {
                 createColors(locationIt, newProps.colorTheme, renderObject.values)
             }
 
@@ -71,7 +89,7 @@ export function ComplexMeshVisual<P extends StructureMeshProps>(builder: Complex
             updateRenderableState(renderObject.state, newProps)
 
             currentProps = newProps
-            return false
+            return true
         },
         getLoci(pickingId: PickingId) {
             return getLoci(pickingId, currentStructure, renderObject.id)
diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts
index 4433e1ebf..06a4cba0e 100644
--- a/src/mol-geo/representation/structure/index.ts
+++ b/src/mol-geo/representation/structure/index.ts
@@ -26,6 +26,23 @@ export const DefaultStructureMeshProps = {
 }
 export type StructureMeshProps = typeof DefaultStructureMeshProps
 
+export interface MeshUpdateState {
+    updateColor: boolean
+    createMesh: boolean
+}
+export namespace MeshUpdateState {
+    export function create(): MeshUpdateState {
+        return {
+            updateColor: false,
+            createMesh: false
+        }
+    }
+    export function reset(state: MeshUpdateState) {
+        state.updateColor = false
+        state.createMesh = false
+    }
+}
+
 export { ComplexRepresentation } from './complex-representation'
 export { UnitsRepresentation } from './units-representation'
 export { ComplexVisual } from './complex-visual'
diff --git a/src/mol-geo/representation/structure/units-representation.ts b/src/mol-geo/representation/structure/units-representation.ts
index 78c3a5795..f2ed113e7 100644
--- a/src/mol-geo/representation/structure/units-representation.ts
+++ b/src/mol-geo/representation/structure/units-representation.ts
@@ -65,7 +65,7 @@ export function UnitsRepresentation<P extends StructureProps>(visualCtor: () =>
                         }
                     }
 
-                    // for new groups, reuse leftover visuals
+                    // for new groups, re-use leftover visuals
                     const unusedVisuals: UnitsVisual<P>[] = []
                     oldUnitsVisuals.forEach(({ visual }) => unusedVisuals.push(visual))
                     newGroups.forEach(async group => {
diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts
index 58986c4dd..9118f2b48 100644
--- a/src/mol-geo/representation/structure/units-visual.ts
+++ b/src/mol-geo/representation/structure/units-visual.ts
@@ -6,7 +6,7 @@
 
 import { Unit } from 'mol-model/structure';
 import { RepresentationProps, Visual } from '..';
-import { DefaultStructureMeshProps } from '.';
+import { DefaultStructureMeshProps, MeshUpdateState } from '.';
 import { RuntimeContext } from 'mol-task';
 import { PickingId } from '../../util/picking';
 import { LocationIterator } from './visual/util/location-iterator';
@@ -33,10 +33,12 @@ export interface UnitsMeshVisualBuilder<P extends UnitsMeshProps> {
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci
     mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean
+    setUpdateState(state: MeshUpdateState, newProps: P, currentProps: P): void
 }
 
 export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> {
-    const { defaultProps, createMesh, createLocationIterator, getLoci, mark } = builder
+    const { defaultProps, createMesh, createLocationIterator, getLoci, mark, setUpdateState } = builder
+    const updateState = MeshUpdateState.create()
 
     let renderObject: MeshRenderObject
     let currentProps: P
@@ -60,29 +62,32 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
         },
         async update(ctx: RuntimeContext, props: Partial<P>) {
             const newProps = Object.assign({}, currentProps, props)
+            const unit = currentGroup.units[0]
 
             if (!renderObject) return false
 
-            let updateColor = false
+            locationIt.reset()
+            MeshUpdateState.reset(updateState)
+            setUpdateState(updateState, newProps, currentProps)
 
-            // TODO create in-place
-            // if (currentProps.radialSegments !== newProps.radialSegments) return false
-
-            // TODO
-            // if (newProps.detail !== currentProps.detail) {
-            //     const unit = currentGroup.units[0]
-            //     const radius = getElementRadius(unit, newProps.sizeTheme)
-            //     mesh = await createElementSphereMesh(ctx, unit, radius, newProps.detail, mesh)
-            //     ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
-            //     updateColor = true
-            // }
+            if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) {
+                updateState.createMesh = true
+            }
 
             if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
-                updateColor = true
+                updateState.updateColor = true
+            }
+
+            //
+
+            if (updateState.createMesh) {
+                mesh = await createMesh(ctx, unit, newProps, mesh)
+                ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
+                updateState.updateColor = true
             }
 
-            if (updateColor) {
-                createColors(createLocationIterator(currentGroup), newProps.colorTheme, renderObject.values)
+            if (updateState.updateColor) {
+                createColors(locationIt, newProps.colorTheme, renderObject.values)
             }
 
             updateMeshValues(renderObject.values, newProps)
diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts
index 547763c76..5421bf4e0 100644
--- a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts
@@ -5,7 +5,7 @@
  */
 
 import { Unit, Structure, Link, StructureElement } from 'mol-model/structure';
-import { DefaultStructureProps, ComplexVisual } from '..';
+import { DefaultStructureProps, ComplexVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
@@ -64,7 +64,10 @@ export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkProps> {
         createMesh: createCarbohydrateLinkCylinderMesh,
         createLocationIterator: CarbohydrateLinkIterator,
         getLoci: getLinkLoci,
-        mark: markLink
+        mark: markLink,
+        setUpdateState: (state: MeshUpdateState, newProps: CarbohydrateLinkProps, currentProps: CarbohydrateLinkProps) => {
+            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        }
     })
 }
 
diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
index f4336e8c6..7186ca7ae 100644
--- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
@@ -5,18 +5,17 @@
  */
 
 import { Unit, Structure, StructureElement } from 'mol-model/structure';
-import { DefaultStructureProps, ComplexVisual } from '..';
+import { ComplexVisual } from '..';
 import { RuntimeContext } from 'mol-task'
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
 import { Loci, EmptyLoci } from 'mol-model/loci';
-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, Interval } from 'mol-data/int';
-import { ComplexMeshVisual } from '../complex-visual';
+import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual';
 import { SizeThemeProps } from 'mol-view/theme/size';
 
 const t = Mat4.identity()
@@ -113,8 +112,7 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru
 }
 
 export const DefaultCarbohydrateSymbolProps = {
-    ...DefaultMeshProps,
-    ...DefaultStructureProps,
+    ...DefaultComplexMeshProps,
     sizeTheme: { name: 'physical', factor: 1 } as SizeThemeProps,
     detail: 0,
     unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[]
@@ -127,7 +125,8 @@ export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolProp
         createMesh: createCarbohydrateSymbolMesh,
         createLocationIterator: CarbohydrateElementIterator,
         getLoci: getCarbohydrateLoci,
-        mark: markCarbohydrate
+        mark: markCarbohydrate,
+        setUpdateState: () => {}
     })
 }
 
diff --git a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
index bfa5997a3..b16bd15d7 100644
--- a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
@@ -5,14 +5,14 @@
  */
 
 import { Link, Structure, StructureElement } from 'mol-model/structure';
-import { DefaultStructureProps, ComplexVisual } from '..';
+import { ComplexVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link';
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
-import { ComplexMeshVisual } from '../complex-visual';
+import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual';
 import { LocationIterator } from './util/location-iterator';
 import { Interval } from 'mol-data/int';
 import { SizeThemeProps } from 'mol-view/theme/size';
@@ -41,7 +41,7 @@ async function createCrossLinkRestraintCylinderMesh(ctx: RuntimeContext, structu
 }
 
 export const DefaultCrossLinkRestraintProps = {
-    ...DefaultStructureProps,
+    ...DefaultComplexMeshProps,
     ...DefaultLinkCylinderProps,
     sizeTheme: { name: 'physical', factor: 0.3 } as SizeThemeProps,
     flipSided: false,
@@ -55,7 +55,10 @@ export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintProp
         createMesh: createCrossLinkRestraintCylinderMesh,
         createLocationIterator: CrossLinkRestraintIterator,
         getLoci: getLinkLoci,
-        mark: markLink
+        mark: markLink,
+        setUpdateState: (state: MeshUpdateState, newProps: CrossLinkRestraintProps, currentProps: CrossLinkRestraintProps) => {
+            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        }
     })
 }
 
diff --git a/src/mol-geo/representation/structure/visual/element-sphere.ts b/src/mol-geo/representation/structure/visual/element-sphere.ts
index 6b5c78cc1..8f151ccf1 100644
--- a/src/mol-geo/representation/structure/visual/element-sphere.ts
+++ b/src/mol-geo/representation/structure/visual/element-sphere.ts
@@ -5,7 +5,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { UnitsVisual } from '..';
+import { UnitsVisual, MeshUpdateState } from '..';
 import { createElementSphereMesh, markElement, getElementLoci } from './util/element';
 import { StructureElementIterator } from './util/location-iterator';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
@@ -22,6 +22,9 @@ export function ElementSphereVisual(): UnitsVisual<ElementSphereProps> {
         createMesh: createElementSphereMesh,
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
-        mark: markElement
+        mark: markElement,
+        setUpdateState: (state: MeshUpdateState, newProps: ElementSphereProps, currentProps: ElementSphereProps) => {
+            state.createMesh = newProps.detail !== currentProps.detail
+        }
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
index ca09e68c3..1e0e2ef0d 100644
--- a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
@@ -5,7 +5,7 @@
  */
 
 import { Link, Structure, StructureElement } from 'mol-model/structure';
-import { DefaultStructureProps, ComplexVisual } from '..';
+import { ComplexVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link';
 import { Mesh } from '../../../shape/mesh';
@@ -13,7 +13,7 @@ import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { LinkIterator } from './util/location-iterator';
-import { ComplexMeshVisual } from '../complex-visual';
+import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual';
 import { Interval } from 'mol-data/int';
 import { SizeThemeProps } from 'mol-view/theme/size';
 
@@ -40,7 +40,7 @@ async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: S
 }
 
 export const DefaultInterUnitLinkProps = {
-    ...DefaultStructureProps,
+    ...DefaultComplexMeshProps,
     ...DefaultLinkCylinderProps,
     sizeTheme: { name: 'physical', factor: 0.3 } as SizeThemeProps,
 }
@@ -52,7 +52,10 @@ export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkProps> {
         createMesh: createInterUnitLinkCylinderMesh,
         createLocationIterator: LinkIterator.fromStructure,
         getLoci: getLinkLoci,
-        mark: markLink
+        mark: markLink,
+        setUpdateState: (state: MeshUpdateState, newProps: InterUnitLinkProps, currentProps: InterUnitLinkProps) => {
+            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        }
     })
 }
 
diff --git a/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
index e9a1ccae0..0e702290f 100644
--- a/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
@@ -68,7 +68,8 @@ export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> {
         createMesh: createIntraUnitLinkCylinderMesh,
         createLocationIterator: LinkIterator.fromGroup,
         getLoci: getLinkLoci,
-        mark: markLink
+        mark: markLink,
+        setUpdateState: () => {}
     })
 }
 
diff --git a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
index 3bd6af749..25578ea9b 100644
--- a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
@@ -114,6 +114,7 @@ export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockProps> {
         createMesh: createNucleotideBlockMesh,
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
-        mark: markElement
+        mark: markElement,
+        setUpdateState: () => {}
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
index 1cf481582..74a1d3841 100644
--- a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
@@ -60,6 +60,7 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> {
         createMesh: createPolymerBackboneCylinderMesh,
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
-        mark: markElement
+        mark: markElement,
+        setUpdateState: () => {}
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
index 3ac265684..9e33253f4 100644
--- a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
@@ -87,6 +87,7 @@ export function PolymerDirectionVisual(): UnitsVisual<PolymerDirectionProps> {
         createMesh: createPolymerDirectionWedgeMesh,
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
-        mark: markElement
+        mark: markElement,
+        setUpdateState: () => {}
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
index 172b2b52f..114839207 100644
--- a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
@@ -66,6 +66,7 @@ export function PolymerGapVisual(): UnitsVisual<PolymerGapProps> {
         createMesh: createPolymerGapCylinderMesh,
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
-        mark: markElement
+        mark: markElement,
+        setUpdateState: () => {}
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
index 91b25ffe0..7373c3958 100644
--- a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
@@ -80,6 +80,7 @@ export function PolymerTraceVisual(): UnitsVisual<PolymerTraceProps> {
         createMesh: createPolymerTraceMesh,
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
-        mark: markElement
+        mark: markElement,
+        setUpdateState: () => {}
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/util/location-iterator.ts b/src/mol-geo/representation/structure/visual/util/location-iterator.ts
index 2fc20e011..79e580629 100644
--- a/src/mol-geo/representation/structure/visual/util/location-iterator.ts
+++ b/src/mol-geo/representation/structure/visual/util/location-iterator.ts
@@ -30,6 +30,7 @@ export interface LocationIterator extends Iterator<LocationValue> {
     readonly elementCount: number
     readonly instanceCount: number
     move(): LocationValue
+    reset(): void
     skipInstance(): void
 }
 
@@ -74,6 +75,18 @@ export function LocationIterator(elementCount: number, instanceCount: number, ge
             }
             return value
         },
+        reset() {
+            value.location = NullLocation
+            value.index = 0
+            value.elementIndex = 0
+            value.instanceIndex = 0
+            value.isSecondary = false
+
+            hasNext = value.elementIndex < elementCount
+            isNextNewInstance = false
+            elementIndex = 0
+            instanceIndex = 0
+        },
         skipInstance() {
             if (hasNext && value.instanceIndex === instanceIndex) {
                 ++instanceIndex
-- 
GitLab