From 73631fcf637f8021e63e9ce9aedf707fee30c2a4 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 7 Sep 2018 15:00:48 -0700 Subject: [PATCH] wip, improved marking and picking --- .../component/structure-representation.tsx | 2 +- src/apps/canvas/component/viewport.tsx | 24 ++++++++------ src/apps/canvas/structure-view.ts | 6 ++-- src/apps/viewer/index.tsx | 6 ---- src/mol-app/ui/visualization/viewport.tsx | 23 ++++++++------ src/mol-geo/representation/index.ts | 4 +-- src/mol-geo/representation/shape/index.ts | 3 +- .../structure/complex-representation.ts | 2 +- .../structure/complex-visual.ts | 6 ++-- .../structure/representation/backbone.ts | 2 +- .../representation/ball-and-stick.ts | 7 +++-- .../structure/representation/carbohydrate.ts | 5 +-- .../structure/representation/cartoon.ts | 9 +++--- .../representation/distance-restraint.ts | 2 +- .../structure/representation/point.ts | 2 +- .../structure/representation/spacefill.ts | 2 +- .../structure/units-representation.ts | 6 +++- .../representation/structure/units-visual.ts | 6 ++-- .../structure/visual/element-point.ts | 3 +- .../structure/visual/util/nucleotide.ts | 4 +-- src/mol-geo/representation/volume/index.ts | 1 + src/mol-geo/representation/volume/surface.ts | 1 + src/mol-geo/util/marker-data.ts | 19 +----------- src/mol-view/viewer.ts | 31 ++++++++++++------- 24 files changed, 91 insertions(+), 85 deletions(-) diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx index ce10f4d5a..f91bfb00a 100644 --- a/src/apps/canvas/component/structure-representation.tsx +++ b/src/apps/canvas/component/structure-representation.tsx @@ -52,7 +52,7 @@ export class StructureRepresentationComponent extends React.Component<StructureR await repr.createOrUpdate(props).run() this.props.viewer.add(repr) - this.props.viewer.requestDraw() + this.props.viewer.requestDraw(true) console.log(this.props.viewer.stats) const newState = { diff --git a/src/apps/canvas/component/viewport.tsx b/src/apps/canvas/component/viewport.tsx index f391125b2..81c1914a8 100644 --- a/src/apps/canvas/component/viewport.tsx +++ b/src/apps/canvas/component/viewport.tsx @@ -7,7 +7,7 @@ import * as React from 'react' import { App } from '../app'; import { MarkerAction } from 'mol-geo/util/marker-data'; -import { EveryLoci } from 'mol-model/loci'; +import { EveryLoci, EmptyLoci, Loci, areLociEqual } from 'mol-model/loci'; import { labelFirst } from 'mol-view/label'; interface ViewportProps { @@ -42,17 +42,23 @@ export class Viewport extends React.Component<ViewportProps, ViewportState> { viewer.input.resize.subscribe(() => this.handleResize()) + let prevLoci: Loci = EmptyLoci viewer.input.move.subscribe(({x, y, inside, buttons}) => { if (!inside || buttons) return const p = viewer.identify(x, y) - const loci = viewer.getLoci(p) - - viewer.mark(EveryLoci, MarkerAction.RemoveHighlight) - viewer.mark(loci, MarkerAction.Highlight) - - const label = labelFirst(loci) - const info = `${label}` - this.setState({ info }) + if (p) { + const loci = viewer.getLoci(p) + + if (!areLociEqual(loci, prevLoci)) { + viewer.mark(prevLoci, MarkerAction.RemoveHighlight) + viewer.mark(loci, MarkerAction.Highlight) + prevLoci = loci + + const label = labelFirst(loci) + const info = `${label}` + this.setState({ info }) + } + } }) } diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index a1a5de9e2..dc7501c39 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -243,7 +243,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> viewer.add(polymerSphere) updated.next(null) - viewer.requestDraw() + viewer.requestDraw(true) console.log(viewer.stats) } @@ -276,7 +276,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> viewer.remove(symmetryAxes) } updated.next(null) - viewer.requestDraw() + viewer.requestDraw(true) } await setModel(0, props.assemblyId, props.symmetryFeatureId) @@ -315,7 +315,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> } viewer.remove(polymerSphere) viewer.remove(symmetryAxes) - viewer.requestDraw() + viewer.requestDraw(true) polymerSphere.destroy() symmetryAxes.destroy() diff --git a/src/apps/viewer/index.tsx b/src/apps/viewer/index.tsx index 940efc7d0..f1c68970f 100644 --- a/src/apps/viewer/index.tsx +++ b/src/apps/viewer/index.tsx @@ -104,10 +104,4 @@ ctx.dispatcher.getStream(InteractivityEvents.HighlightLoci).subscribe(event => { } }) -ctx.dispatcher.getStream(InteractivityEvents.SelectLoci).subscribe(event => { - if (event && event.data) { - ctx.stage.viewer.mark(event.data, MarkerAction.ToggleSelect) - } -}) - ReactDOM.render(React.createElement(Layout, { controller: ctx.layout }), elm); diff --git a/src/mol-app/ui/visualization/viewport.tsx b/src/mol-app/ui/visualization/viewport.tsx index d23f182e1..ed6c563a4 100644 --- a/src/mol-app/ui/visualization/viewport.tsx +++ b/src/mol-app/ui/visualization/viewport.tsx @@ -163,19 +163,24 @@ export class Viewport extends View<ViewportController, ViewportState, { noWebGl? viewer.input.move.subscribe(({x, y, inside, buttons}) => { if (!inside || buttons) return const p = viewer.identify(x, y) - const loci = viewer.getLoci(p) - InteractivityEvents.HighlightLoci.dispatch(this.controller.context, loci); - - // TODO use LabelLoci event and make configurable - const label = labelFirst(loci) - const info = `Object: ${p.objectId}, Instance: ${p.instanceId}, Group: ${p.groupId}, Label: ${label}` - this.setState({ info }) + if (p) { + const loci = viewer.getLoci(p) + InteractivityEvents.HighlightLoci.dispatch(this.controller.context, loci); + + // TODO use LabelLoci event and make configurable + const label = labelFirst(loci) + const info = `Object: ${p.objectId}, Instance: ${p.instanceId}, Group: ${p.groupId}, Label: ${label}` + this.setState({ info }) + } }) // TODO filter only for left button/single finger touch? viewer.input.click.subscribe(({x, y}) => { - const loci = viewer.getLoci(viewer.identify(x, y)) - InteractivityEvents.SelectLoci.dispatch(this.controller.context, loci); + const p = viewer.identify(x, y) + if (p) { + const loci = viewer.getLoci(p) + InteractivityEvents.SelectLoci.dispatch(this.controller.context, loci); + } }) } diff --git a/src/mol-geo/representation/index.ts b/src/mol-geo/representation/index.ts index 90ac94490..ce1425040 100644 --- a/src/mol-geo/representation/index.ts +++ b/src/mol-geo/representation/index.ts @@ -18,7 +18,7 @@ export interface Representation<D, P extends RepresentationProps = {}> { readonly props: Readonly<P> createOrUpdate: (props?: Partial<P>, data?: D) => Task<void> getLoci: (pickingId: PickingId) => Loci - mark: (loci: Loci, action: MarkerAction) => void + mark: (loci: Loci, action: MarkerAction) => boolean destroy: () => void } @@ -26,6 +26,6 @@ export interface Visual<D, P extends RepresentationProps = {}> { readonly renderObject: RenderObject | undefined createOrUpdate: (ctx: RuntimeContext, props?: Partial<P>, data?: D) => Promise<void> getLoci: (pickingId: PickingId) => Loci - mark: (loci: Loci, action: MarkerAction) => void + mark: (loci: Loci, action: MarkerAction) => boolean destroy: () => void } \ No newline at end of file diff --git a/src/mol-geo/representation/shape/index.ts b/src/mol-geo/representation/shape/index.ts index 2aec89462..c5b86178a 100644 --- a/src/mol-geo/representation/shape/index.ts +++ b/src/mol-geo/representation/shape/index.ts @@ -86,7 +86,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation return EmptyLoci }, mark(loci: Loci, action: MarkerAction) { - if (!_renderObject) return + if (!_renderObject) return false const { tMarker } = _renderObject.values let changed = false if (isEveryLoci(loci)) { @@ -108,6 +108,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation if (changed) { ValueCell.update(tMarker, tMarker.ref.value) } + return changed }, destroy() { // TODO diff --git a/src/mol-geo/representation/structure/complex-representation.ts b/src/mol-geo/representation/structure/complex-representation.ts index a5e9f3aac..3f624d4af 100644 --- a/src/mol-geo/representation/structure/complex-representation.ts +++ b/src/mol-geo/representation/structure/complex-representation.ts @@ -31,7 +31,7 @@ export function ComplexRepresentation<P extends StructureProps>(label: string, v } function mark(loci: Loci, action: MarkerAction) { - if (visual) visual.mark(loci, action) + return visual ? visual.mark(loci, action) : false } function destroy() { diff --git a/src/mol-geo/representation/structure/complex-visual.ts b/src/mol-geo/representation/structure/complex-visual.ts index 179b9803d..53a59c6d4 100644 --- a/src/mol-geo/representation/structure/complex-visual.ts +++ b/src/mol-geo/representation/structure/complex-visual.ts @@ -117,7 +117,7 @@ export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMe return renderObject ? getLoci(pickingId, currentStructure, renderObject.id) : EmptyLoci }, mark(loci: Loci, action: MarkerAction) { - if (!renderObject) return + if (!renderObject) return false const { tMarker } = renderObject.values const { groupCount, instanceCount } = locationIt @@ -129,14 +129,14 @@ export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMe let changed = false if (isEveryLoci(loci)) { - apply(Interval.ofBounds(0, groupCount * instanceCount)) - changed = true + changed = apply(Interval.ofBounds(0, groupCount * instanceCount)) } else { changed = mark(loci, currentStructure, apply) } if (changed) { ValueCell.update(tMarker, tMarker.ref.value) } + return changed }, destroy() { // TODO diff --git a/src/mol-geo/representation/structure/representation/backbone.ts b/src/mol-geo/representation/structure/representation/backbone.ts index 6cd7425a2..2d618326b 100644 --- a/src/mol-geo/representation/structure/representation/backbone.ts +++ b/src/mol-geo/representation/structure/representation/backbone.ts @@ -43,7 +43,7 @@ export function BackboneRepresentation(): BackboneRepresentation { return traceRepr.getLoci(pickingId) }, mark: (loci: Loci, action: MarkerAction) => { - traceRepr.mark(loci, action) + return traceRepr.mark(loci, action) }, destroy() { traceRepr.destroy() diff --git a/src/mol-geo/representation/structure/representation/ball-and-stick.ts b/src/mol-geo/representation/structure/representation/ball-and-stick.ts index e465f7ff4..c031185cf 100644 --- a/src/mol-geo/representation/structure/representation/ball-and-stick.ts +++ b/src/mol-geo/representation/structure/representation/ball-and-stick.ts @@ -65,9 +65,10 @@ export function BallAndStickRepresentation(): BallAndStickRepresentation { } }, mark: (loci: Loci, action: MarkerAction) => { - elmementRepr.mark(loci, action) - intraLinkRepr.mark(loci, action) - interLinkRepr.mark(loci, action) + const markElement = elmementRepr.mark(loci, action) + const markIntraLink = intraLinkRepr.mark(loci, action) + const markInterLink = interLinkRepr.mark(loci, action) + return markElement || markIntraLink || markInterLink }, destroy() { elmementRepr.destroy() diff --git a/src/mol-geo/representation/structure/representation/carbohydrate.ts b/src/mol-geo/representation/structure/representation/carbohydrate.ts index e1f6d9da0..46220707b 100644 --- a/src/mol-geo/representation/structure/representation/carbohydrate.ts +++ b/src/mol-geo/representation/structure/representation/carbohydrate.ts @@ -53,8 +53,9 @@ export function CarbohydrateRepresentation(): CarbohydrateRepresentation { : carbohydrateLinkLoci }, mark: (loci: Loci, action: MarkerAction) => { - carbohydrateSymbolRepr.mark(loci, action) - carbohydrateLinkRepr.mark(loci, action) + const markSymbol = carbohydrateSymbolRepr.mark(loci, action) + const markLink = carbohydrateLinkRepr.mark(loci, action) + return markSymbol || markLink }, destroy() { carbohydrateSymbolRepr.destroy() diff --git a/src/mol-geo/representation/structure/representation/cartoon.ts b/src/mol-geo/representation/structure/representation/cartoon.ts index 7e42ec115..2bafe115f 100644 --- a/src/mol-geo/representation/structure/representation/cartoon.ts +++ b/src/mol-geo/representation/structure/representation/cartoon.ts @@ -68,10 +68,11 @@ export function CartoonRepresentation(): CartoonRepresentation { // : directionLoci }, mark: (loci: Loci, action: MarkerAction) => { - traceRepr.mark(loci, action) - gapRepr.mark(loci, action) - blockRepr.mark(loci, action) - // directionRepr.mark(loci, action) + const markTrace = traceRepr.mark(loci, action) + const markGap = gapRepr.mark(loci, action) + const markBlock = blockRepr.mark(loci, action) + // const markDirection = directionRepr.mark(loci, action) + return markTrace || markGap || markBlock // \\ markDirection }, destroy() { traceRepr.destroy() diff --git a/src/mol-geo/representation/structure/representation/distance-restraint.ts b/src/mol-geo/representation/structure/representation/distance-restraint.ts index 0a2d9c289..d1ed2524e 100644 --- a/src/mol-geo/representation/structure/representation/distance-restraint.ts +++ b/src/mol-geo/representation/structure/representation/distance-restraint.ts @@ -45,7 +45,7 @@ export function DistanceRestraintRepresentation(): DistanceRestraintRepresentati return crossLinkRepr.getLoci(pickingId) }, mark: (loci: Loci, action: MarkerAction) => { - crossLinkRepr.mark(loci, action) + return crossLinkRepr.mark(loci, action) }, destroy() { crossLinkRepr.destroy() diff --git a/src/mol-geo/representation/structure/representation/point.ts b/src/mol-geo/representation/structure/representation/point.ts index 5c45a6ca1..9c388317c 100644 --- a/src/mol-geo/representation/structure/representation/point.ts +++ b/src/mol-geo/representation/structure/representation/point.ts @@ -38,7 +38,7 @@ export function PointRepresentation(): PointRepresentation { return pointRepr.getLoci(pickingId) }, mark: (loci: Loci, action: MarkerAction) => { - pointRepr.mark(loci, action) + return pointRepr.mark(loci, action) }, destroy() { pointRepr.destroy() diff --git a/src/mol-geo/representation/structure/representation/spacefill.ts b/src/mol-geo/representation/structure/representation/spacefill.ts index ee18231d9..5de426027 100644 --- a/src/mol-geo/representation/structure/representation/spacefill.ts +++ b/src/mol-geo/representation/structure/representation/spacefill.ts @@ -40,7 +40,7 @@ export function SpacefillRepresentation(): SpacefillRepresentation { return sphereRepr.getLoci(pickingId) }, mark: (loci: Loci, action: MarkerAction) => { - sphereRepr.mark(loci, action) + return sphereRepr.mark(loci, action) }, destroy() { sphereRepr.destroy() diff --git a/src/mol-geo/representation/structure/units-representation.ts b/src/mol-geo/representation/structure/units-representation.ts index 79a9c4806..8b7e9ec5e 100644 --- a/src/mol-geo/representation/structure/units-representation.ts +++ b/src/mol-geo/representation/structure/units-representation.ts @@ -115,7 +115,11 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis } function mark(loci: Loci, action: MarkerAction) { - visuals.forEach(({ visual }) => visual.mark(loci, action)) + let changed = false + visuals.forEach(({ visual }) => { + changed = visual.mark(loci, action) || changed + }) + return changed } function destroy() { diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts index 3bfdf283a..534764f3d 100644 --- a/src/mol-geo/representation/structure/units-visual.ts +++ b/src/mol-geo/representation/structure/units-visual.ts @@ -140,7 +140,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu return renderObject ? getLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci }, mark(loci: Loci, action: MarkerAction) { - if (!renderObject) return + if (!renderObject) return false const { tMarker } = renderObject.values const { groupCount, instanceCount } = locationIt @@ -152,14 +152,14 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu let changed = false if (isEveryLoci(loci)) { - apply(Interval.ofBounds(0, groupCount * instanceCount)) - changed = true + changed = apply(Interval.ofBounds(0, groupCount * instanceCount)) } else { changed = mark(loci, currentGroup, apply) } if (changed) { ValueCell.update(tMarker, tMarker.ref.value) } + return changed }, destroy() { // TODO diff --git a/src/mol-geo/representation/structure/visual/element-point.ts b/src/mol-geo/representation/structure/visual/element-point.ts index 9c9dec03f..d0b74c089 100644 --- a/src/mol-geo/representation/structure/visual/element-point.ts +++ b/src/mol-geo/representation/structure/visual/element-point.ts @@ -139,7 +139,7 @@ export function ElementPointVisual(): UnitsVisual<ElementPointProps> { return renderObject ? getElementLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci }, mark(loci: Loci, action: MarkerAction) { - if (!renderObject) return + if (!renderObject) return false const { tMarker } = renderObject.values const { groupCount, instanceCount } = locationIt @@ -159,6 +159,7 @@ export function ElementPointVisual(): UnitsVisual<ElementPointProps> { if (changed) { ValueCell.update(tMarker, tMarker.ref.value) } + return changed }, destroy() { // TODO diff --git a/src/mol-geo/representation/structure/visual/util/nucleotide.ts b/src/mol-geo/representation/structure/visual/util/nucleotide.ts index ab334d1d0..d62fae0e1 100644 --- a/src/mol-geo/representation/structure/visual/util/nucleotide.ts +++ b/src/mol-geo/representation/structure/visual/util/nucleotide.ts @@ -52,8 +52,8 @@ export function markNucleotideElement(loci: Loci, group: Unit.SymmetryGroup, app const unitIdx = group.unitIndexMap.get(e.unit.id) if (unitIdx !== undefined && Unit.isAtomic(e.unit)) { if (Interval.is(e.indices)) { - const min = unitIdx * groupCount + OrderedSet.indexOf(e.unit.nucleotideElements, e.unit.elements[Interval.min(e.indices)]) - const max = unitIdx * groupCount + OrderedSet.indexOf(e.unit.nucleotideElements, e.unit.elements[Interval.max(e.indices)]) + const min = OrderedSet.indexOf(e.unit.nucleotideElements, e.unit.elements[Interval.min(e.indices)]) + const max = OrderedSet.indexOf(e.unit.nucleotideElements, e.unit.elements[Interval.max(e.indices)]) if (min !== -1 && max !== -1) { if (apply(Interval.ofRange(unitIdx * groupCount + min, unitIdx * groupCount + max))) changed = true } diff --git a/src/mol-geo/representation/volume/index.ts b/src/mol-geo/representation/volume/index.ts index 5f3234dcb..9197885d3 100644 --- a/src/mol-geo/representation/volume/index.ts +++ b/src/mol-geo/representation/volume/index.ts @@ -52,6 +52,7 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD }, mark(loci: Loci, action: MarkerAction) { // TODO + return false }, destroy() { // TODO diff --git a/src/mol-geo/representation/volume/surface.ts b/src/mol-geo/representation/volume/surface.ts index 7fa9446da..46671107f 100644 --- a/src/mol-geo/representation/volume/surface.ts +++ b/src/mol-geo/representation/volume/surface.ts @@ -105,6 +105,7 @@ export default function SurfaceVisual(): VolumeVisual<SurfaceProps> { }, mark(loci: Loci, action: MarkerAction) { // TODO + return false }, destroy() { // TODO diff --git a/src/mol-geo/util/marker-data.ts b/src/mol-geo/util/marker-data.ts index 55dc24945..ed813f2fa 100644 --- a/src/mol-geo/util/marker-data.ts +++ b/src/mol-geo/util/marker-data.ts @@ -18,7 +18,6 @@ export enum MarkerAction { RemoveHighlight, Select, Deselect, - ToggleSelect, Clear } @@ -30,42 +29,26 @@ export function applyMarkerAction(array: Uint8Array, start: number, end: number, case MarkerAction.Highlight: if (v % 2 === 0) { v += 1 - changed = true } break case MarkerAction.RemoveHighlight: if (v % 2 !== 0) { v -= 1 - changed = true } break case MarkerAction.Select: v += 2 - changed = true break case MarkerAction.Deselect: if (v >= 2) { v -= 2 - changed = true } break - case MarkerAction.ToggleSelect: - if (v === 0) { - v = 2 - } else if (v === 1) { - v = 3 - } else if (v === 2) { - v = 0 - } else { - v -= 2 - } - changed = true - break case MarkerAction.Clear: v = 0 - changed = true break } + changed = array[i] !== v || changed array[i] = v } return changed diff --git a/src/mol-view/viewer.ts b/src/mol-view/viewer.ts index 0580a883f..9cf36a5f2 100644 --- a/src/mol-view/viewer.ts +++ b/src/mol-view/viewer.ts @@ -38,10 +38,10 @@ interface Viewer { clear: () => void draw: (force?: boolean) => void - requestDraw: () => void + requestDraw: (force?: boolean) => void animate: () => void pick: () => void - identify: (x: number, y: number) => PickingId + identify: (x: number, y: number) => PickingId | undefined mark: (loci: Loci, action: MarkerAction) => void getLoci: (pickingId: PickingId) => Loci @@ -128,9 +128,16 @@ namespace Viewer { } function mark(loci: Loci, action: MarkerAction) { - reprMap.forEach((roSet, repr) => repr.mark(loci, action)) - scene.update() - requestDraw() + let changed = false + reprMap.forEach((roSet, repr) => { + changed = repr.mark(loci, action) || changed + }) + if (changed) { + // console.log('changed') + scene.update() + draw(true) + pickDirty = false // picking buffers should not have changed + } } let nearPlaneDelta = 0 @@ -181,7 +188,6 @@ namespace Viewer { if (variant === 'draw') { lastRenderTime = performance.now() pickDirty = true - // pick() } didRender = true } @@ -195,22 +201,21 @@ namespace Viewer { drawPending = false } - function requestDraw () { + function requestDraw(force?: boolean) { if (drawPending) return drawPending = true - window.requestAnimationFrame(() => draw(true)) + window.requestAnimationFrame(() => draw(force)) } - function animate () { + function animate() { draw(false) - if (performance.now() - lastRenderTime > 500) { + if (performance.now() - lastRenderTime > 200) { if (pickDirty) pick() } window.requestAnimationFrame(() => animate()) } function pick() { - console.log('pick') render('pickObject', pickDirty) render('pickInstance', pickDirty) render('pickGroup', pickDirty) @@ -218,7 +223,9 @@ namespace Viewer { pickDirty = false } - function identify (x: number, y: number): PickingId { + function identify(x: number, y: number): PickingId | undefined { + if (pickDirty) return undefined + x *= ctx.pixelRatio y *= ctx.pixelRatio y = canvas.height - y // flip y -- GitLab