فهرست منبع

repr state handling fixes and support for allowed marker actions

Alexander Rose 5 سال پیش
والد
کامیت
7725071ae2

+ 2 - 0
src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts

@@ -16,6 +16,7 @@ import { RuntimeContext } from '../../../mol-task';
 import { Shape } from '../../../mol-model/shape';
 import { ColorNames } from '../../../mol-util/color/names';
 import { ShapeRepresentation } from '../../../mol-repr/shape/representation';
+import { MarkerActions } from '../../../mol-util/marker-action';
 
 export const AssemblySymmetryAxesParams = {
     ...Mesh.Params,
@@ -57,6 +58,7 @@ export async function getAssemblySymmetryAxesRepresentation(ctx: RuntimeContext,
     const repr = prev || ShapeRepresentation(getAxesShape, Mesh.Utils);
     const data = AssemblySymmetryProvider.get(structure).value
     await repr.createOrUpdate(params, data).runInContext(ctx);
+    repr.setState({ markerActions: MarkerActions.Highlighting })
     return repr;
 }
 

+ 4 - 1
src/mol-model-props/rcsb/representations/validation-report-clashes.ts

@@ -24,6 +24,7 @@ import { ClashesProvider } from '../validation-report';
 import { CustomProperty } from '../../common/custom-property';
 import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual } from '../../../mol-repr/structure/complex-visual';
 import { Color } from '../../../mol-util/color';
+import { MarkerActions } from '../../../mol-util/marker-action';
 
 //
 
@@ -217,7 +218,9 @@ export function getClashesParams(ctx: ThemeRegistryContext, structure: Structure
 
 export type ClashesRepresentation = StructureRepresentation<ClashesParams>
 export function ClashesRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, ClashesParams>): ClashesRepresentation {
-    return Representation.createMulti('Clashes', ctx, getParams, StructureRepresentationStateBuilder, ClashesVisuals as unknown as Representation.Def<Structure, ClashesParams>)
+    const repr = Representation.createMulti('Clashes', ctx, getParams, StructureRepresentationStateBuilder, ClashesVisuals as unknown as Representation.Def<Structure, ClashesParams>)
+    repr.setState({ markerActions: MarkerActions.Highlighting })
+    return repr
 }
 
 export const ClashesRepresentationProvider: StructureRepresentationProvider<ClashesParams> = {

+ 10 - 5
src/mol-repr/representation.ts

@@ -13,7 +13,7 @@ import { Subject } from 'rxjs';
 import { GraphicsRenderObject } from '../mol-gl/render-object';
 import { Task } from '../mol-task';
 import { PickingId } from '../mol-geo/geometry/picking';
-import { MarkerAction } from '../mol-util/marker-action';
+import { MarkerAction, MarkerActions } from '../mol-util/marker-action';
 import { Loci as ModelLoci, EmptyLoci, isEmptyLoci } from '../mol-model/loci';
 import { Overpaint } from '../mol-theme/overpaint';
 import { Transparency } from '../mol-theme/transparency';
@@ -167,9 +167,11 @@ namespace Representation {
         syncManually: boolean
         /** A transformation applied to the representation's renderobjects */
         transform: Mat4
+        /** Bit mask of allowed marker actions */
+        markerActions: MarkerActions
     }
     export function createState(): State {
-        return { visible: false, alphaFactor: 0, pickable: false, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty, transparency: Transparency.Empty }
+        return { visible: true, alphaFactor: 1, pickable: true, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty, transparency: Transparency.Empty, markerActions: MarkerActions.All }
     }
     export function updateState(state: State, update: Partial<State>) {
         if (update.visible !== undefined) state.visible = update.visible
@@ -179,6 +181,7 @@ namespace Representation {
         if (update.transparency !== undefined) state.transparency = update.transparency
         if (update.syncManually !== undefined) state.syncManually = update.syncManually
         if (update.transform !== undefined) Mat4.copy(state.transform, update.transform)
+        if (update.markerActions !== undefined) state.markerActions = update.markerActions
     }
     export interface StateBuilder<S extends State> {
         create(): S
@@ -212,7 +215,9 @@ namespace Representation {
         const reprMap: { [k: number]: string } = {}
         const reprList: Representation<D, P>[] = Object.keys(reprDefs).map((name, i) => {
             reprMap[i] = name
-            return reprDefs[name](ctx, getParams)
+            const repr = reprDefs[name](ctx, getParams)
+            repr.setState(currentState)
+            return repr
         })
 
         return {
@@ -283,10 +288,10 @@ namespace Representation {
                 return marked
             },
             setState: (state: Partial<S>) => {
+                stateBuilder.update(currentState, state)
                 for (let i = 0, il = reprList.length; i < il; ++i) {
-                    reprList[i].setState(state)
+                    reprList[i].setState(currentState)
                 }
-                stateBuilder.update(currentState, state)
             },
             setTheme: (theme: Theme) => {
                 for (let i = 0, il = reprList.length; i < il; ++i) {

+ 2 - 1
src/mol-repr/shape/loci/angle.ts

@@ -21,6 +21,7 @@ import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
 import { radToDeg, arcLength } from '../../../mol-math/misc';
 import { Circle } from '../../../mol-geo/primitive/circle';
 import { transformPrimitive } from '../../../mol-geo/primitive/primitive';
+import { MarkerActions } from '../../../mol-util/marker-action';
 
 export interface AngleData {
     triples: Loci.Bundle<3>[]
@@ -68,7 +69,7 @@ type TextParams = typeof TextParams
 const AngleVisuals = {
     'vectors': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, VectorsParams>) => ShapeRepresentation(getVectorsShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
     'arc': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, ArcParams>) => ShapeRepresentation(getArcShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
-    'sector': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, SectorParams>) => ShapeRepresentation(getSectorShape, Mesh.Utils, { modifyProps: p => ({ ...p, alpha: p.sectorOpacity }) }),
+    'sector': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, SectorParams>) => ShapeRepresentation(getSectorShape, Mesh.Utils, { modifyProps: p => ({ ...p, alpha: p.sectorOpacity }), modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) }),
     'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<AngleData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils),
 }
 

+ 2 - 1
src/mol-repr/shape/loci/dihedral.ts

@@ -21,6 +21,7 @@ import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
 import { arcLength, halfPI, radToDeg } from '../../../mol-math/misc';
 import { Circle } from '../../../mol-geo/primitive/circle';
 import { transformPrimitive } from '../../../mol-geo/primitive/primitive';
+import { MarkerActions } from '../../../mol-util/marker-action';
 
 export interface DihedralData {
     quads: Loci.Bundle<4>[]
@@ -74,7 +75,7 @@ const DihedralVisuals = {
     'vectors': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, VectorsParams>) => ShapeRepresentation(getVectorsShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
     'extenders': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, ExtendersParams>) => ShapeRepresentation(getExtendersShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
     'arc': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, ArcParams>) => ShapeRepresentation(getArcShape, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
-    'sector': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, SectorParams>) => ShapeRepresentation(getSectorShape, Mesh.Utils, { modifyProps: p => ({ ...p, alpha: p.sectorOpacity }) }),
+    'sector': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, SectorParams>) => ShapeRepresentation(getSectorShape, Mesh.Utils, { modifyProps: p => ({ ...p, alpha: p.sectorOpacity }), modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) }),
     'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<DihedralData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils),
 }
 

+ 4 - 1
src/mol-repr/shape/loci/distance.ts

@@ -16,6 +16,7 @@ import { Shape } from '../../../mol-model/shape';
 import { LinesBuilder } from '../../../mol-geo/geometry/lines/lines-builder';
 import { TextBuilder } from '../../../mol-geo/geometry/text/text-builder';
 import { Vec3 } from '../../../mol-math/linear-algebra';
+import { MarkerActions } from '../../../mol-util/marker-action';
 
 export interface DistanceData {
     pairs: Loci.Bundle<2>[]
@@ -141,5 +142,7 @@ function getTextShape(ctx: RuntimeContext, data: DistanceData, props: DistancePr
 
 export type DistanceRepresentation = Representation<DistanceData, DistanceParams>
 export function DistanceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<DistanceData, DistanceParams>): DistanceRepresentation {
-    return Representation.createMulti('Distance', ctx, getParams, Representation.StateBuilder, DistanceVisuals as unknown as Representation.Def<DistanceData, DistanceParams>)
+    const repr = Representation.createMulti('Distance', ctx, getParams, Representation.StateBuilder, DistanceVisuals as unknown as Representation.Def<DistanceData, DistanceParams>)
+    repr.setState({ markerActions: MarkerActions.Highlighting })
+    return repr
 }

+ 4 - 1
src/mol-repr/shape/loci/orientation.ts

@@ -19,6 +19,7 @@ import { addOrientedBox } from '../../../mol-geo/geometry/mesh/builder/box';
 import { addEllipsoid } from '../../../mol-geo/geometry/mesh/builder/ellipsoid';
 import { Axes3D } from '../../../mol-math/geometry';
 import { Vec3 } from '../../../mol-math/linear-algebra';
+import { MarkerActions } from '../../../mol-util/marker-action';
 
 export interface OrientationData {
     locis: Loci[]
@@ -153,5 +154,7 @@ function getEllipsoidShape(ctx: RuntimeContext, data: OrientationData, props: Or
 
 export type OrientationRepresentation = Representation<OrientationData, OrientationParams>
 export function OrientationRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<OrientationData, OrientationParams>): OrientationRepresentation {
-    return Representation.createMulti('Orientation', ctx, getParams, Representation.StateBuilder, OrientationVisuals as unknown as Representation.Def<OrientationData, OrientationParams>)
+    const repr = Representation.createMulti('Orientation', ctx, getParams, Representation.StateBuilder, OrientationVisuals as unknown as Representation.Def<OrientationData, OrientationParams>)
+    repr.setState({ markerActions: MarkerActions.Highlighting })
+    return repr
 }

+ 7 - 1
src/mol-repr/shape/representation.ts

@@ -15,7 +15,7 @@ import { VisualUpdateState } from '../util';
 import { ShapeGroupColorTheme } from '../../mol-theme/color/shape-group';
 import { ShapeGroupSizeTheme } from '../../mol-theme/size/shape-group';
 import { createMarkers } from '../../mol-geo/geometry/marker-data';
-import { MarkerAction } from '../../mol-util/marker-action';
+import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
 import { ValueCell } from '../../mol-util';
 import { createColors } from '../../mol-geo/geometry/color-data';
 import { createSizes, SizeData } from '../../mol-geo/geometry/size-data';
@@ -189,6 +189,12 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
             return EmptyLoci
         },
         mark(loci: Loci, action: MarkerAction) {
+            if (!MarkerActions.is(_state.markerActions, action)) return false
+            if (ShapeGroup.isLoci(loci) || Shape.isLoci(loci)) {
+                if (loci.shape !== _shape) return false
+            } else if (!isEveryLoci(loci)) {
+                return false
+            }
             return Visual.mark(_renderObject, loci, action, lociApply)
         },
         setState(state: Partial<Representation.State>) {

+ 11 - 2
src/mol-repr/structure/complex-representation.ts

@@ -15,7 +15,7 @@ import { Theme } from '../../mol-theme/theme';
 import { Task } from '../../mol-task';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci, isEveryLoci } from '../../mol-model/loci';
-import { MarkerAction } from '../../mol-util/marker-action';
+import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
 import { Overpaint } from '../../mol-theme/overpaint';
 import { Interactions } from '../../mol-model-props/computed/interactions/interactions';
 
@@ -41,9 +41,17 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
         _props = Object.assign({}, _props, props)
 
         return Task.create('Creating or updating ComplexRepresentation', async runtime => {
-            if (!visual) visual = visualCtor(materialId)
+            let newVisual = false
+            if (!visual) {
+                visual = visualCtor(materialId)
+                newVisual = true
+            }
             const promise = visual.createOrUpdate({ webgl: ctx.webgl, runtime }, _theme, _props, structure)
             if (promise) await promise
+            if (newVisual) {
+                // ensure state is current for new visual
+                setState(_state)
+            }
             // update list of renderObjects
             renderObjects.length = 0
             if (visual && visual.renderObject) renderObjects.push(visual.renderObject)
@@ -59,6 +67,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
 
     function mark(loci: Loci, action: MarkerAction) {
         if (!_structure) return false
+        if (!MarkerActions.is(_state.markerActions, action)) return false
         if (Structure.isLoci(loci) || StructureElement.Loci.is(loci) || Bond.isLoci(loci) || Interactions.isLoci(loci)) {
             if (!Structure.areRootsEquivalent(loci.structure, _structure)) return false
             // Remap `loci` from equivalent structure to the current `_structure`

+ 2 - 2
src/mol-repr/structure/complex-visual.ts

@@ -219,10 +219,10 @@ export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geom
             Visual.setTransform(renderObject, matrix, instanceMatrices)
         },
         setOverpaint(overpaint: Overpaint) {
-            return Visual.setOverpaint(renderObject, overpaint, lociApply, true)
+            Visual.setOverpaint(renderObject, overpaint, lociApply, true)
         },
         setTransparency(transparency: Transparency) {
-            return Visual.setTransparency(renderObject, transparency, lociApply, true)
+            Visual.setTransparency(renderObject, transparency, lociApply, true)
         },
         destroy() {
             // TODO

+ 47 - 22
src/mol-repr/structure/units-representation.ts

@@ -18,9 +18,11 @@ import { Theme } from '../../mol-theme/theme';
 import { Task } from '../../mol-task';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { Loci, EmptyLoci, isEmptyLoci, isEveryLoci } from '../../mol-model/loci';
-import { MarkerAction } from '../../mol-util/marker-action';
+import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
 import { Overpaint } from '../../mol-theme/overpaint';
 import { Interactions } from '../../mol-model-props/computed/interactions/interactions';
+import { Transparency } from '../../mol-theme/transparency';
+import { Mat4, EPSILON } from '../../mol-math/linear-algebra';
 
 export const UnitsParams = {
     ...StructureParams,
@@ -63,6 +65,8 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
                     const visual = visualCtor(materialId)
                     const promise = visual.createOrUpdate({ webgl: ctx.webgl, runtime }, _theme, _props, { group, structure })
                     if (promise) await promise
+                    // ensure state is current for new visual
+                    setVisualState(visual, group, _state)
                     visuals.set(group.hashCode, { visual, group })
                     if (runtime.shouldUpdate) await runtime.update({ message: 'Creating or updating UnitsVisual', current: i, max: _groups.length })
                 }
@@ -89,6 +93,8 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
                         // console.log(label, 'not found visualGroup to reuse, creating new')
                         // newGroups.push(group)
                         const visual = visualCtor(materialId)
+                        // ensure state is current for new visual
+                        setVisualState(visual, group, _state)
                         const promise = visual.createOrUpdate({ webgl: ctx.webgl, runtime }, _theme, _props, { group, structure })
                         if (promise) await promise
                         visuals.set(group.hashCode, { visual, group })
@@ -166,8 +172,8 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
     }
 
     function mark(loci: Loci, action: MarkerAction) {
-        let changed = false
         if (!_structure) return false
+        if (!MarkerActions.is(_state.markerActions, action)) return false
         if (Structure.isLoci(loci) || StructureElement.Loci.is(loci) || Bond.isLoci(loci) || Interactions.isLoci(loci)) {
             if (!Structure.areRootsEquivalent(loci.structure, _structure)) return false
             // Remap `loci` from equivalent structure to the current `_structure`
@@ -176,38 +182,57 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
         } else if (!isEveryLoci(loci)) {
             return false
         }
+
+        let changed = false
         visuals.forEach(({ visual }) => {
             changed = visual.mark(loci, action) || changed
         })
         return changed
     }
 
-    function setState(state: Partial<StructureRepresentationState>) {
+    function setVisualState(visual: UnitsVisual<P>, group: Unit.SymmetryGroup, state: Partial<StructureRepresentationState>) {
         const { visible, alphaFactor, pickable, overpaint, transparency, transform, unitTransforms } = state
-        if (visible !== undefined) visuals.forEach(({ visual }) => visual.setVisibility(visible))
-        if (alphaFactor !== undefined) visuals.forEach(({ visual }) => visual.setAlphaFactor(alphaFactor))
-        if (pickable !== undefined) visuals.forEach(({ visual }) => visual.setPickable(pickable))
-        if (overpaint !== undefined) {
-            // Remap loci from equivalent structure to the current `_structure`
+
+        if (visible !== undefined) visual.setVisibility(visible)
+        if (alphaFactor !== undefined) visual.setAlphaFactor(alphaFactor)
+        if (pickable !== undefined) visual.setPickable(pickable)
+        if (overpaint !== undefined) visual.setOverpaint(overpaint)
+        if (transparency !== undefined) visual.setTransparency(transparency)
+        if (transform !== undefined) visual.setTransform(transform)
+        if (unitTransforms !== undefined) {
+            if (unitTransforms) {
+                // console.log(group.hashCode, unitTransforms.getSymmetryGroupTransforms(group))
+                visual.setTransform(undefined, unitTransforms.getSymmetryGroupTransforms(group))
+            } else {
+                visual.setTransform(undefined, null)
+            }
+        }
+    }
+
+    function setState(state: Partial<StructureRepresentationState>) {
+        const { visible, alphaFactor, pickable, overpaint, transparency, transform, unitTransforms, syncManually, markerActions } = state
+        const newState: Partial<StructureRepresentationState> = {}
+
+        if (visible !== _state.visible) newState.visible = visible
+        if (alphaFactor !== _state.alphaFactor) newState.alphaFactor = alphaFactor
+        if (pickable !== _state.pickable) newState.pickable = pickable
+        if (overpaint !== undefined && !Overpaint.areEqual(overpaint, _state.overpaint)) {
             if (_structure) {
-                const remappedOverpaint = Overpaint.remap(overpaint, _structure)
-                visuals.forEach(({ visual }) => visual.setOverpaint(remappedOverpaint))
+                newState.overpaint = Overpaint.remap(overpaint, _structure)
             }
         }
-        if (transparency !== undefined) visuals.forEach(({ visual }) => visual.setTransparency(transparency))
-        if (transform !== undefined) visuals.forEach(({ visual }) => visual.setTransform(transform))
-        if (unitTransforms !== undefined) {
-            visuals.forEach(({ visual, group }) => {
-                if (unitTransforms) {
-                    // console.log(group.hashCode, unitTransforms.getSymmetryGroupTransforms(group))
-                    visual.setTransform(undefined, unitTransforms.getSymmetryGroupTransforms(group))
-                } else {
-                    visual.setTransform(undefined, null)
-                }
-            })
+        if (transparency !== undefined && !Transparency.areEqual(transparency, _state.transparency)) {
+            newState.transparency = transparency
         }
+        if (transform !== undefined && !Mat4.areEqual(transform, _state.transform, EPSILON)) { newState.transform = transform
+        }
+        if (unitTransforms !== _state.unitTransforms) newState.unitTransforms = unitTransforms
+        if (syncManually !== _state.syncManually) newState.syncManually = syncManually
+        if (markerActions !== _state.markerActions) newState.markerActions = markerActions
+
+        visuals.forEach(({ visual, group }) => setVisualState(visual, group, newState))
 
-        StructureRepresentationStateBuilder.update(_state, state)
+        StructureRepresentationStateBuilder.update(_state, newState)
     }
 
     function setTheme(theme: Theme) {

+ 2 - 2
src/mol-repr/structure/units-visual.ts

@@ -264,10 +264,10 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry
             Visual.setTransform(renderObject, matrix, instanceMatrices)
         },
         setOverpaint(overpaint: Overpaint) {
-            return Visual.setOverpaint(renderObject, overpaint, lociApply, true)
+            Visual.setOverpaint(renderObject, overpaint, lociApply, true)
         },
         setTransparency(transparency: Transparency) {
-            return Visual.setTransparency(renderObject, transparency, lociApply, true)
+            Visual.setTransparency(renderObject, transparency, lociApply, true)
         },
         destroy() {
             // TODO

+ 28 - 7
src/mol-util/marker-action.ts

@@ -1,18 +1,39 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { OrderedSet, Interval } from '../mol-data/int';
+import BitFlags from './bit-flags';
 
 export enum MarkerAction {
-    Highlight,
-    RemoveHighlight,
-    Select,
-    Deselect,
-    Toggle,
-    Clear
+    None = 0x0,
+    Highlight = 0x1,
+    RemoveHighlight = 0x2,
+    Select = 0x4,
+    Deselect = 0x8,
+    Toggle = 0x10,
+    Clear = 0x20
+}
+
+export type MarkerActions = BitFlags<MarkerAction>
+export namespace MarkerActions {
+    export const is: (m: MarkerActions, f: MarkerAction) => boolean = BitFlags.has
+
+    export const All = (
+        MarkerAction.Highlight | MarkerAction.RemoveHighlight |
+        MarkerAction.Select | MarkerAction.Deselect | MarkerAction.Toggle |
+        MarkerAction.Clear
+    ) as MarkerActions
+    export const Highlighting = (
+        MarkerAction.Highlight | MarkerAction.RemoveHighlight |
+        MarkerAction.Clear
+    ) as MarkerActions
+    export const Selecting = (
+        MarkerAction.Select | MarkerAction.Deselect | MarkerAction.Toggle |
+        MarkerAction.Clear
+    ) as MarkerActions
 }
 
 export function applyMarkerActionAtPosition(array: Uint8Array, i: number, action: MarkerAction) {