Skip to content
Snippets Groups Projects
Commit 8b7333b4 authored by Alexander Rose's avatar Alexander Rose
Browse files

avoid unnecessary draw calls/ui updates when marking

- fix wrong type narrowing of Loci.isEmpty
parent 7d26567d
Branches
No related tags found
No related merge requests found
......@@ -230,7 +230,7 @@ interface Canvas3D {
/** Sets drawPaused = false without starting the built in animation loop */
resume(): void
identify(x: number, y: number): PickData | undefined
mark(loci: Representation.Loci, action: MarkerAction): void
mark(loci: Representation.Loci, action: MarkerAction, noDraw?: boolean): void
getLoci(pickingId: PickingId | undefined): Representation.Loci
notifyDidDraw: boolean,
......@@ -339,7 +339,7 @@ namespace Canvas3D {
return { loci, repr };
}
function mark(reprLoci: Representation.Loci, action: MarkerAction) {
function mark(reprLoci: Representation.Loci, action: MarkerAction, noDraw = false) {
const { repr, loci } = reprLoci;
let changed = false;
if (repr) {
......@@ -349,7 +349,7 @@ namespace Canvas3D {
changed = helper.camera.mark(loci, action) || changed;
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
}
if (changed) {
if (changed && !noDraw) {
scene.update(void 0, true);
helper.handle.scene.update(void 0, true);
helper.camera.scene.update(void 0, true);
......
......@@ -116,7 +116,7 @@ namespace Loci {
return !!loci && loci.kind === 'every-loci';
}
export function isEmpty(loci: Loci): loci is EmptyLoci {
export function isEmpty(loci: Loci) {
if (isEveryLoci(loci)) return false;
if (isEmptyLoci(loci)) return true;
if (isDataLoci(loci)) return isDataLociEmpty(loci);
......
......@@ -11,7 +11,7 @@ import { StateTransforms } from '../../mol-plugin-state/transforms';
import { PluginContext } from '../../mol-plugin/context';
import { StateBuilder, StateObjectCell, StateSelection, StateTransform } from '../../mol-state';
import { StructureComponentRef } from '../manager/structure/hierarchy-state';
import { EmptyLoci, Loci } from '../../mol-model/loci';
import { EmptyLoci, isEmptyLoci, Loci } from '../../mol-model/loci';
import { Clipping } from '../../mol-theme/clipping';
type ClippingEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, clipping?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.ClippingStructureRepresentation3DFromBundle>>) => Promise<void>
......@@ -25,7 +25,7 @@ export async function setStructureClipping(plugin: PluginContext, components: St
// always use the root structure to get the loci so the clipping
// stays applicable as long as the root structure does not change
const loci = await lociGetter(structure.root);
if (Loci.isEmpty(loci)) return;
if (Loci.isEmpty(loci) || isEmptyLoci(loci)) return;
const layer = {
bundle: StructureElement.Bundle.fromLoci(loci),
......
......@@ -13,7 +13,7 @@ import { StateBuilder, StateObjectCell, StateSelection, StateTransform } from '.
import { Overpaint } from '../../mol-theme/overpaint';
import { Color } from '../../mol-util/color';
import { StructureComponentRef } from '../manager/structure/hierarchy-state';
import { EmptyLoci, Loci } from '../../mol-model/loci';
import { EmptyLoci, isEmptyLoci, Loci } from '../../mol-model/loci';
type OverpaintEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, overpaint?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.OverpaintStructureRepresentation3DFromBundle>>) => Promise<void>
const OverpaintManagerTag = 'overpaint-controls';
......@@ -26,7 +26,7 @@ export async function setStructureOverpaint(plugin: PluginContext, components: S
// always use the root structure to get the loci so the overpaint
// stays applicable as long as the root structure does not change
const loci = await lociGetter(structure.root);
if (Loci.isEmpty(loci)) return;
if (Loci.isEmpty(loci) || isEmptyLoci(loci)) return;
const layer = {
bundle: StructureElement.Bundle.fromLoci(loci),
......
......@@ -11,7 +11,7 @@ import { StateTransforms } from '../../mol-plugin-state/transforms';
import { PluginContext } from '../../mol-plugin/context';
import { StateBuilder, StateObjectCell, StateSelection, StateTransform } from '../../mol-state';
import { StructureComponentRef } from '../manager/structure/hierarchy-state';
import { EmptyLoci, Loci } from '../../mol-model/loci';
import { EmptyLoci, isEmptyLoci, Loci } from '../../mol-model/loci';
import { Transparency } from '../../mol-theme/transparency';
type TransparencyEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, transparency?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.TransparencyStructureRepresentation3DFromBundle>>) => Promise<void>
......@@ -25,7 +25,7 @@ export async function setStructureTransparency(plugin: PluginContext, components
// always use the root structure to get the loci so the transparency
// stays applicable as long as the root structure does not change
const loci = await lociGetter(structure.root);
if (Loci.isEmpty(loci)) return;
if (Loci.isEmpty(loci) || isEmptyLoci(loci)) return;
const layer = {
bundle: StructureElement.Bundle.fromLoci(loci),
......
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
......@@ -74,7 +74,13 @@ namespace InteractivityManager {
export interface DragEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, pageStart: Vec2, pageEnd: Vec2 }
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, page?: Vec2, position?: Vec3 }
export type LociMarkProvider = (loci: Representation.Loci, action: MarkerAction) => void
/**
* The `noRender` argument indicates that the action should only update the internal
* data structure but not render anything user visible. For example 1) no drawing of
* the canvas3d scene or 2) no ui update of loci labels. This is useful because some
* actions require clearing any markings before they can be applied.
*/
export type LociMarkProvider = (loci: Representation.Loci, action: MarkerAction, /* test */ noRender?: boolean) => void
export abstract class LociMarkManager {
protected providers: LociMarkProvider[] = [];
......@@ -101,9 +107,9 @@ namespace InteractivityManager {
return { loci: Loci.normalize(loci, granularity), repr };
}
protected mark(current: Representation.Loci, action: MarkerAction) {
protected mark(current: Representation.Loci, action: MarkerAction, noRender = false) {
if (!Loci.isEmpty(current.loci)) {
for (let p of this.providers) p(current, action);
for (let p of this.providers) p(current, action, noRender);
}
}
......@@ -130,9 +136,9 @@ namespace InteractivityManager {
this.prev.push(loci);
}
clearHighlights = () => {
clearHighlights = (noRender = false) => {
for (const p of this.prev) {
this.mark(p, MarkerAction.RemoveHighlight);
this.mark(p, MarkerAction.RemoveHighlight, noRender);
}
this.prev.length = 0;
}
......@@ -147,21 +153,29 @@ namespace InteractivityManager {
highlightOnly(current: Representation.Loci, applyGranularity = true) {
const normalized = this.normalizedLoci(current, applyGranularity);
if (!this.isHighlighted(normalized)) {
if (Loci.isEmpty(normalized.loci)) {
this.clearHighlights();
} else {
this.clearHighlights(true);
this.addHighlight(normalized);
}
}
}
highlightOnlyExtend(current: Representation.Loci, applyGranularity = true) {
const normalized = this.normalizedLoci(current, applyGranularity);
if (StructureElement.Loci.is(normalized.loci)) {
const loci = {
const extended = {
loci: this.sel.tryGetRange(normalized.loci) || normalized.loci,
repr: normalized.repr
};
if (!this.isHighlighted(loci)) {
if (!this.isHighlighted(extended)) {
if (Loci.isEmpty(extended.loci)) {
this.clearHighlights();
this.addHighlight(loci);
} else {
this.clearHighlights(true);
this.addHighlight(extended);
}
}
}
}
......@@ -241,7 +255,7 @@ namespace InteractivityManager {
// do a full deselect/select for the current structure so visuals that are
// marked with granularity unequal to 'element' and join/intersect operations
// are handled properly
super.mark({ loci: Structure.Loci(loci.structure) }, MarkerAction.Deselect);
super.mark({ loci: Structure.Loci(loci.structure) }, MarkerAction.Deselect, true);
super.mark({ loci: this.sel.getLoci(loci.structure) }, MarkerAction.Select);
} else {
super.mark(current, action);
......
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
......@@ -92,9 +92,9 @@ export class LociLabelManager {
}
constructor(public ctx: PluginContext) {
ctx.managers.interactivity.lociHighlights.addProvider((loci, action) => {
ctx.managers.interactivity.lociHighlights.addProvider((loci, action, noRender) => {
this.mark(loci, action);
this.showLabels();
if (!noRender) this.showLabels();
});
}
}
\ No newline at end of file
......@@ -143,7 +143,7 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
historyEntry(e: StructureSelectionHistoryEntry, idx: number) {
const history = this.plugin.managers.structure.selection.additionsHistory;
return <div className='msp-flex-row' key={e.id}>
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={this.plugin.managers.interactivity.lociHighlights.clearHighlights}>
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={() => this.plugin.managers.interactivity.lociHighlights.clearHighlights()}>
{idx}. <span dangerouslySetInnerHTML={{ __html: e.label }} />
</Button>
{history.length > 1 && <IconButton svg={ArrowUpwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />}
......
......@@ -210,7 +210,7 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
lociEntry(e: LociEntry, idx: number) {
return <div className='msp-flex-row' key={idx}>
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={this.plugin.managers.interactivity.lociHighlights.clearHighlights}>
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={() => this.plugin.managers.interactivity.lociHighlights.clearHighlights()}>
<span dangerouslySetInnerHTML={{ __html: e.label }} />
</Button>
</div>;
......@@ -219,7 +219,7 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
historyEntry(e: StructureSelectionHistoryEntry, idx: number) {
const history = this.plugin.managers.structure.selection.additionsHistory;
return <div className='msp-flex-row' key={e.id}>
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={this.plugin.managers.interactivity.lociHighlights.clearHighlights}>
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={() => this.plugin.managers.interactivity.lociHighlights.clearHighlights()}>
{idx}. <span dangerouslySetInnerHTML={{ __html: e.label }} />
</Button>
{history.length > 1 && <IconButton svg={ArrowUpwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />}
......
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
......@@ -42,9 +42,9 @@ export const HighlightLoci = PluginBehavior.create({
name: 'representation-highlight-loci',
category: 'interaction',
ctor: class extends PluginBehavior.Handler<HighlightLociProps> {
private lociMarkProvider = (interactionLoci: Representation.Loci, action: MarkerAction) => {
private lociMarkProvider = (interactionLoci: Representation.Loci, action: MarkerAction, noRender?: boolean) => {
if (!this.ctx.canvas3d || !this.params.mark) return;
this.ctx.canvas3d.mark(interactionLoci, action);
this.ctx.canvas3d.mark(interactionLoci, action, noRender);
}
register() {
this.subscribeObservable(this.ctx.behaviors.interaction.hover, ({ current, buttons, modifiers }) => {
......@@ -104,9 +104,9 @@ export const SelectLoci = PluginBehavior.create({
category: 'interaction',
ctor: class extends PluginBehavior.Handler<SelectLociProps> {
private spine: StateTreeSpine.Impl
private lociMarkProvider = (reprLoci: Representation.Loci, action: MarkerAction) => {
private lociMarkProvider = (reprLoci: Representation.Loci, action: MarkerAction, noRender?: boolean) => {
if (!this.ctx.canvas3d || !this.params.mark) return;
this.ctx.canvas3d.mark({ loci: reprLoci.loci }, action);
this.ctx.canvas3d.mark({ loci: reprLoci.loci }, action, noRender);
}
private applySelectMark(ref: string, clear?: boolean) {
const cell = this.ctx.state.data.cells.get(ref);
......
......@@ -305,7 +305,7 @@ export namespace VolumeStreaming {
private _invTransform: Mat4 = Mat4();
private getBoxFromLoci(loci: StructureElement.Loci | EmptyLoci): Box3D {
if (Loci.isEmpty(loci)) {
if (Loci.isEmpty(loci) || isEmptyLoci(loci)) {
return Box3D();
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment