diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx index ce10f4d5a4542317a1ca1d2b4b5df411f621c7b1..f91bfb00aa689937b63bbc5172da1236546d9145 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 f391125b28d0d2dbe1d4302dc650e27f57b656c5..81c1914a820f44af4fcb4b76132b1c9d04e4e062 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 a1a5de9e25cee2dc50b72c7fead0450d20cc5ffe..dc7501c397e2391d500e3f0a29299f031a658f4c 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 940efc7d074991890d466e3ec52e10da2fa25c69..f1c68970f4779c1fd3a8740d974f277b3c464298 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 d23f182e1b5bbfa3731d1f98edc7fa2cb9243692..ed6c563a40673701268de1330556a64680dc23b9 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 90ac94490bdfeb28ba54360b9035b28ea27739ee..ce1425040fd53f1705d7e241c155ddb6a2db4d5e 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 2aec89462bd3239ea67c43228d16675e04839bef..c5b86178a700bd021cbd759e1dcfe36ee43c5df2 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 a5e9f3aac9f59bfcf539d44b5c18db579f4c29e3..3f624d4af188435dccb9238decbf223f9de2665c 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 179b9803d42cba49b812c808b8db67cd75ec5704..53a59c6d4ed75890f3a3463971c138f14445192b 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 6cd7425a269b485b5fed1f2695c7997908ebf859..2d618326baff0370501be0557e13a74a6bce2a71 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 e465f7ff4de5a6ba7a48e7450a2b2745adc9bc7c..c031185cf1fcd557487dd50550fd6da3757c4453 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 e1f6d9da04a25ace5254abcbe8916945ba2c737b..46220707b4a823d84dc9c6d3777f18b8a348bb89 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 7e42ec1155a6d4a80806a575dca6700bb08bab48..2bafe115fc5788ef70a58f9e5b075ec515126d01 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 0a2d9c289a7d10b26741a7956380d802119d1e14..d1ed2524ee9dcd68226d126442ca945d9f374fa7 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 5c45a6ca12131e3e3e43982b002f95a5e1277d01..9c388317cd9badd0c37899a7833201cb20831d9b 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 ee18231d9462c62333031fadaaa8a471e1adeb99..5de4260277c617a03b0012cab38c7de44aeb5686 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 79a9c480645714079b51d38c571d7da268f82ced..8b7e9ec5e2c22db5460a928eec7ecf2d254d35ce 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 3bfdf283a03bb824cd7bf353d84b9cd9007edfad..534764f3d57028e1ad33103d4e90125d3c69d4fe 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 9c9dec03ff60d85515da77bbaf1b1950d5043c11..d0b74c089e9f37c81a817f653f54702e4574fabf 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 ab334d1d0d156d8043f129a9a20999fd14349192..d62fae0e165047165c9d0266c5c4c8a8735fa18b 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 5f3234dcbaca949311962fc458ed057cb51421e0..9197885d3c9cebb758d2cd37218437ec3164f561 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 7fa9446daa299864d42b2823cfb63e6120cb4647..46671107fcfff72a825598c02692718e6c1953b7 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 55dc2494560d9bc734dba5a1a11f73f835f797b7..ed813f2fa8432c84960cab9368716ff21eda7f8b 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 0580a883fc1af868a05190b0b03fa92ca2f96227..9cf36a5f27a1a15be1306164d72da9cb932a702b 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