diff --git a/src/mol-model/location.ts b/src/mol-model/location.ts index 39546ddac69d0642e20e7f5d232b824d798e2c90..d5006c61710d8684b26c982e1529703a3cfcceda 100644 --- a/src/mol-model/location.ts +++ b/src/mol-model/location.ts @@ -1,12 +1,12 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { StructureElement } from './structure' import { Link } from './structure/structure/unit/links' -import { Shape } from './shape/shape'; +import { ShapeGroup } from './shape/shape'; /** A null value Location */ export const NullLocation = { kind: 'null-location' as 'null-location' } @@ -15,4 +15,4 @@ export function isNullLocation(x: any): x is NullLocation { return !!x && x.kind === 'null-location'; } -export type Location = StructureElement | Link.Location | Shape.Location | NullLocation \ No newline at end of file +export type Location = StructureElement | Link.Location | ShapeGroup.Location | NullLocation \ No newline at end of file diff --git a/src/mol-model/loci.ts b/src/mol-model/loci.ts index 35cad50ed5ecb596bf791d532455964f921db7af..517b0631ac0456006bc7fe62ec7a54690587980b 100644 --- a/src/mol-model/loci.ts +++ b/src/mol-model/loci.ts @@ -1,12 +1,12 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { StructureElement } from './structure' import { Link } from './structure/structure/unit/links' -import { Shape } from './shape'; +import { Shape, ShapeGroup } from './shape'; import { Sphere3D } from 'mol-math/geometry'; import { CentroidHelper } from 'mol-math/geometry/centroid-helper'; import { Vec3 } from 'mol-math/linear-algebra'; @@ -46,7 +46,7 @@ export function createDataLoci(data: any, tag: string, indices: OrderedSet<numbe export { Loci } -type Loci = StructureElement.Loci | Structure.Loci | Link.Loci | EveryLoci | EmptyLoci | DataLoci | Shape.Loci +type Loci = StructureElement.Loci | Structure.Loci | Link.Loci | EveryLoci | EmptyLoci | DataLoci | Shape.Loci | ShapeGroup.Loci namespace Loci { export function areEqual(lociA: Loci, lociB: Loci) { @@ -67,6 +67,9 @@ namespace Loci { if (Shape.isLoci(lociA) && Shape.isLoci(lociB)) { return Shape.areLociEqual(lociA, lociB) } + if (ShapeGroup.isLoci(lociA) && ShapeGroup.isLoci(lociB)) { + return ShapeGroup.areLociEqual(lociA, lociB) + } return false } @@ -96,6 +99,9 @@ namespace Loci { e.aUnit.conformation.position(e.bUnit.elements[e.bIndex], tempPos); sphereHelper.radiusStep(tempPos); } + } else if (loci.kind === 'shape-loci') { + // TODO + return void 0; } else if (loci.kind === 'group-loci') { // TODO return void 0; diff --git a/src/mol-model/shape/shape.ts b/src/mol-model/shape/shape.ts index 314af70fbfec3c4df1a7379c9359ead45f4fcc05..98188c58e360606021be9fa5ae25eb9e8421eeef 100644 --- a/src/mol-model/shape/shape.ts +++ b/src/mol-model/shape/shape.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -43,6 +43,13 @@ export namespace Shape { } } + export interface Loci { readonly kind: 'shape-loci', readonly shape: Shape } + export function Loci(shape: Shape): Loci { return { kind: 'shape-loci', shape } } + export function isLoci(x: any): x is Loci { return !!x && x.kind === 'shape-loci' } + export function areLociEqual(a: Loci, b: Loci) { return a.shape === b.shape } +} + +export namespace ShapeGroup { export interface Location { readonly kind: 'group-location' shape: Shape diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts index 2ad87f7ff3b7a676dcd14d4d2298f06a267a7ecb..e7b357d9b7e3987edd704fcdd98559c4eed7bd73 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts @@ -22,6 +22,7 @@ import { VolumeServerHeader, VolumeServerInfo } from './model'; import { ButtonsType } from 'mol-util/input/input-observer'; import { PluginCommands } from 'mol-plugin/command'; import { StateSelection } from 'mol-state'; +import { Representation } from 'mol-repr/representation'; export class VolumeStreaming extends PluginStateObject.CreateBehavior<VolumeStreaming.Behavior>({ name: 'Volume Streaming' }) { } @@ -172,7 +173,21 @@ export namespace VolumeStreaming { } register(ref: string): void { - // this.ref = ref; + let lastLoci: Representation.Loci = Representation.Loci.Empty; + + this.subscribeObservable(this.plugin.events.state.object.removed, o => { + if (!PluginStateObject.Molecule.Structure.is(o.obj) || lastLoci.loci.kind !== 'element-loci') return; + if (lastLoci.loci.structure === o.obj.data) { + lastLoci = Representation.Loci.Empty; + } + }); + + this.subscribeObservable(this.plugin.events.state.object.updated, o => { + if (!PluginStateObject.Molecule.Structure.is(o.oldObj) || lastLoci.loci.kind !== 'element-loci') return; + if (lastLoci.loci.structure === o.oldObj.data) { + lastLoci = Representation.Loci.Empty; + } + }); this.subscribeObservable(this.plugin.behaviors.canvas3d.click, ({ current, buttons, modifiers }) => { if (buttons !== ButtonsType.Flag.Secondary || this.params.view.name !== 'selection-box') return; @@ -193,6 +208,13 @@ export namespace VolumeStreaming { const root = this.getStructureRoot(ref); if (!root || !root.obj || root.obj !== parent.obj) return; + if (Representation.Loci.areEqual(lastLoci, current)) { + lastLoci = Representation.Loci.Empty; + this.updateDynamicBox(ref, Box3D.empty()); + return; + } + lastLoci = current; + const loci = StructureElement.Loci.extendToWholeResidues(current.loci); const box = StructureElement.Loci.getBoundary(loci).box; this.updateDynamicBox(ref, box); diff --git a/src/mol-plugin/skin/base/components/temp.scss b/src/mol-plugin/skin/base/components/temp.scss index 8913fd5d31ccf465be9388fe3aa63820d001eac7..9d20d320fa4b0ca6cc344b6decb9b8b65a1ef6db 100644 --- a/src/mol-plugin/skin/base/components/temp.scss +++ b/src/mol-plugin/skin/base/components/temp.scss @@ -180,11 +180,15 @@ line-height: $row-height; float: left; margin-right: $control-spacing; - background-color: $msp-form-control-background; + + > button { + background-color: $msp-form-control-background; + } > select { display: inline-block; width: 200px; + margin-right: $control-spacing; } } diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts index 83f1edb96e25a5ed817d4cba918fb1d2a9ec803b..ebd0f7afcd791e1cb62c1a482beac1c677212c31 100644 --- a/src/mol-repr/shape/representation.ts +++ b/src/mol-repr/shape/representation.ts @@ -9,7 +9,7 @@ import { createRenderObject, GraphicsRenderObject } from 'mol-gl/render-object'; import { Representation } from '../representation'; import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci'; import { ValueCell } from 'mol-util'; -import { Shape } from 'mol-model/shape'; +import { Shape, ShapeGroup } from 'mol-model/shape'; import { OrderedSet, Interval } from 'mol-data/int'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { createTransform, TransformData } from 'mol-geo/geometry/transform-data'; @@ -170,7 +170,7 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa getLoci(pickingId: PickingId) { const { objectId, groupId, instanceId } = pickingId if (_renderObject && _renderObject.id === objectId) { - return Shape.Loci(_shape, [{ ids: OrderedSet.ofSingleton(groupId) }], instanceId) + return ShapeGroup.Loci(_shape, [{ ids: OrderedSet.ofSingleton(groupId) }], instanceId) } return EmptyLoci }, @@ -210,7 +210,7 @@ function createShapeTransform(transforms: Mat4[], transformData?: TransformData) } function eachShapeGroup(loci: Loci, shape: Shape, apply: (interval: Interval) => boolean) { - if (!Shape.isLoci(loci)) return false + if (!ShapeGroup.isLoci(loci)) return false if (loci.shape !== shape) return false let changed = false const { groupCount } = shape @@ -233,7 +233,7 @@ function eachShapeGroup(loci: Loci, shape: Shape, apply: (interval: Interval) => export namespace ShapeGroupIterator { export function fromShape(shape: Shape): LocationIterator { const instanceCount = shape.transforms.length - const location = Shape.Location(shape) + const location = ShapeGroup.Location(shape) const getLocation = (groupIndex: number, instanceIndex: number) => { location.group = groupIndex location.instance = instanceIndex diff --git a/src/mol-repr/structure/visual/util/nucleotide.ts b/src/mol-repr/structure/visual/util/nucleotide.ts index 22fe970f1692264dadea9354d0b454aa44fea7bc..8e8ed71d1e3ad7de88cede0d4e6d5000bb6e77ef 100644 --- a/src/mol-repr/structure/visual/util/nucleotide.ts +++ b/src/mol-repr/structure/visual/util/nucleotide.ts @@ -35,7 +35,9 @@ export function getNucleotideElementLoci(pickingId: PickingId, structureGroup: S if (id === objectId) { const { structure, group } = structureGroup const unit = group.units[instanceId] - return getResidueLoci(structure, unit, unit.polymerElements[groupId]) + if (Unit.isAtomic(unit)) { + return getResidueLoci(structure, unit, unit.polymerElements[groupId]) + } } return EmptyLoci } diff --git a/src/mol-theme/color/shape-group.ts b/src/mol-theme/color/shape-group.ts index b352ecd50e0581296b9cd59a3ac9669214a670ac..905cce02c84062540b7465a00d98868e5ffb946a 100644 --- a/src/mol-theme/color/shape-group.ts +++ b/src/mol-theme/color/shape-group.ts @@ -7,7 +7,7 @@ import { ColorTheme } from '../color'; import { Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; -import { Shape } from 'mol-model/shape'; +import { ShapeGroup } from 'mol-model/shape'; import { ParamDefinition as PD } from 'mol-util/param-definition' import { ThemeDataContext } from 'mol-theme/theme'; @@ -25,7 +25,7 @@ export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: PD.Values<Sha factory: ShapeGroupColorTheme, granularity: 'groupInstance', color: (location: Location): Color => { - if (Shape.isLocation(location)) { + if (ShapeGroup.isLocation(location)) { return location.shape.getColor(location.group, location.instance) } return DefaultColor diff --git a/src/mol-theme/label.ts b/src/mol-theme/label.ts index 8aac229a59d8db1ca68eeab84eb03b921ec55f0c..69b67a6b9b4559b4e12c9e1bc5bd8c52313d6bb8 100644 --- a/src/mol-theme/label.ts +++ b/src/mol-theme/label.ts @@ -33,6 +33,8 @@ export function labelFirst(loci: Loci): string { case 'link-loci': const link = loci.links[0] return link ? linkLabel(link) : 'Unknown' + case 'shape-loci': + return loci.shape.name case 'group-loci': const g = loci.groups[0] if (g) { diff --git a/src/mol-theme/size/shape-group.ts b/src/mol-theme/size/shape-group.ts index 60fa329f3aacd2ee175aedbc8122e9d6398dc505..8bfcdf4f715519e1e91b88c2914795671857c955 100644 --- a/src/mol-theme/size/shape-group.ts +++ b/src/mol-theme/size/shape-group.ts @@ -5,7 +5,7 @@ */ import { Location } from 'mol-model/location'; -import { Shape } from 'mol-model/shape'; +import { ShapeGroup } from 'mol-model/shape'; import { ParamDefinition as PD } from 'mol-util/param-definition' import { ThemeDataContext } from 'mol-theme/theme'; import { SizeTheme } from 'mol-theme/size'; @@ -24,7 +24,7 @@ export function ShapeGroupSizeTheme(ctx: ThemeDataContext, props: PD.Values<Shap factory: ShapeGroupSizeTheme, granularity: 'groupInstance', size: (location: Location): number => { - if (Shape.isLocation(location)) { + if (ShapeGroup.isLocation(location)) { return location.shape.getSize(location.group, location.instance) } return DefaultSize diff --git a/src/tests/browser/render-shape.ts b/src/tests/browser/render-shape.ts index 943a50476785bf19aedb3bd7b959cc210ce54c75..ce513f8132403be892bd02ffa87fe06d5a6f936a 100644 --- a/src/tests/browser/render-shape.ts +++ b/src/tests/browser/render-shape.ts @@ -15,6 +15,9 @@ import { ColorNames } from 'mol-util/color/tables'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { labelFirst } from 'mol-theme/label'; import { RuntimeContext, Progress } from 'mol-task'; +import { Representation } from 'mol-repr/representation'; +import { MarkerAction } from 'mol-geo/geometry/marker-data'; +import { EveryLoci } from 'mol-model/loci'; const parent = document.getElementById('app')! parent.style.width = '100%' @@ -34,14 +37,23 @@ info.style.right = '20px' info.style.color = 'white' parent.appendChild(info) +let prevReprLoci = Representation.Loci.Empty const canvas3d = Canvas3D.create(canvas, parent) canvas3d.animate() canvas3d.input.move.subscribe(async ({x, y}) => { const pickingId = await canvas3d.identify(x, y) let label = '' if (pickingId) { - const { loci } = canvas3d.getLoci(pickingId) - label = labelFirst(loci) + const reprLoci = canvas3d.getLoci(pickingId) + label = labelFirst(reprLoci.loci) + if (!Representation.Loci.areEqual(prevReprLoci, reprLoci)) { + canvas3d.mark(prevReprLoci, MarkerAction.RemoveHighlight) + canvas3d.mark(reprLoci, MarkerAction.Highlight) + prevReprLoci = reprLoci + } + } else { + canvas3d.mark({ loci: EveryLoci }, MarkerAction.RemoveHighlight) + prevReprLoci = Representation.Loci.Empty } info.innerText = label })