diff --git a/CHANGELOG.md b/CHANGELOG.md index e97d58488934f2d0b34e9f56056c619809bebb09..2298953a5bd3e94869f1410a929e947096484727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Note that since we don't clearly distinguish between a public and private interf - Fix rendering issues caused by VAO reuse - Add "Zoom All", "Orient Axes", "Reset Axes" buttons to the "Reset Camera" button - Improve trackball move-state handling when key bindings use modifiers +- Add `eachLocation` to representation/visual interface ## [v3.33.0] - 2023-04-02 diff --git a/src/mol-model/structure/structure/element/location.ts b/src/mol-model/structure/structure/element/location.ts index 2843e5a0d1a6d42e948d26ac0f01f71f5d886cf2..9181bfbec17af2f2b503d1e92ba39c05e86f618c 100644 --- a/src/mol-model/structure/structure/element/location.ts +++ b/src/mol-model/structure/structure/element/location.ts @@ -55,13 +55,17 @@ namespace Location { return a.unit === b.unit && a.element === b.element; } - const pA = Vec3.zero(), pB = Vec3.zero(); + const pA = Vec3(), pB = Vec3(); export function distance(a: Location, b: Location) { a.unit.conformation.position(a.element, pA); b.unit.conformation.position(b.element, pB); return Vec3.distance(pA, pB); } + export function position(out: Vec3, l: Location): Vec3 { + return l.unit.conformation.position(l.element, out); + } + export function residueIndex(l: Location) { return l.unit.model.atomicHierarchy.residueAtomSegments.index[l.element]; } diff --git a/src/mol-model/structure/structure/element/loci.ts b/src/mol-model/structure/structure/element/loci.ts index 60c7a9436449e38c46cd7877bb83b4947636e9b0..9fefd8d8f0c17592ce0c242e97c0b2fdc047b775 100644 --- a/src/mol-model/structure/structure/element/loci.ts +++ b/src/mol-model/structure/structure/element/loci.ts @@ -148,7 +148,7 @@ export namespace Loci { * The loc argument of the callback is mutable, use Location.clone() if you intend to keep * the value around. */ - export function forEachLocation(loci: Loci, f: (loc: Location) => any) { + export function forEachLocation(loci: Loci, f: (loc: Location) => void) { if (Loci.isEmpty(loci)) return; const location = Location.create(loci.structure); diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts index 4718fc6662f38afc276bd10aecfaaf80c696a2c7..6b604fa838ec7aefdb9db4e0cd674b0fc4e4aef9 100644 --- a/src/mol-repr/representation.ts +++ b/src/mol-repr/representation.ts @@ -18,7 +18,7 @@ import { Loci as ModelLoci, EmptyLoci, isEmptyLoci } from '../mol-model/loci'; import { Overpaint } from '../mol-theme/overpaint'; import { Transparency } from '../mol-theme/transparency'; import { Mat4 } from '../mol-math/linear-algebra'; -import { getQualityProps } from './util'; +import { LocationCallback, getQualityProps } from './util'; import { BaseGeometry } from '../mol-geo/geometry/base'; import { Visual } from './visual'; import { CustomProperty } from '../mol-model-props/common/custom-property'; @@ -162,6 +162,7 @@ interface Representation<D, P extends PD.Params = PD.Params, S extends Represent setTheme: (theme: Theme) => void getLoci: (pickingId: PickingId) => ModelLoci getAllLoci: () => ModelLoci[] + eachLocation: (cb: LocationCallback) => void mark: (loci: ModelLoci, action: MarkerAction) => boolean destroy: () => void } @@ -250,6 +251,7 @@ namespace Representation { setTheme: () => {}, getLoci: () => EmptyLoci, getAllLoci: () => [], + eachLocation: () => {}, mark: () => false, destroy: () => {} }; @@ -370,6 +372,14 @@ namespace Representation { } return loci; }, + eachLocation: (cb: LocationCallback) => { + const { visuals } = currentProps; + for (let i = 0, il = reprList.length; i < il; ++i) { + if (!visuals || visuals.includes(reprMap[i])) { + reprList[i].eachLocation(cb); + } + } + }, mark: (loci: ModelLoci, action: MarkerAction) => { let marked = false; for (let i = 0, il = reprList.length; i < il; ++i) { @@ -436,6 +446,9 @@ namespace Representation { // TODO return []; }, + eachLocation: () => { + // TODO + }, mark: (loci: ModelLoci, action: MarkerAction) => { // TODO return false; diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts index 45c4c68776db007b38a7e0568bd9c0f3c1581095..f5344c9582b0a500d96dfa725e0f96b5b92dd018 100644 --- a/src/mol-repr/shape/representation.ts +++ b/src/mol-repr/shape/representation.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -11,7 +11,7 @@ import { Subject } from 'rxjs'; import { getNextMaterialId, createRenderObject, GraphicsRenderObject } from '../../mol-gl/render-object'; import { Theme } from '../../mol-theme/theme'; import { LocationIterator } from '../../mol-geo/util/location-iterator'; -import { VisualUpdateState } from '../util'; +import { LocationCallback, VisualUpdateState } from '../util'; import { createMarkers } from '../../mol-geo/geometry/marker-data'; import { MarkerAction, MarkerActions } from '../../mol-util/marker-action'; import { ValueCell } from '../../mol-util'; @@ -223,6 +223,13 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa getAllLoci() { return [Shape.Loci(_shape)]; }, + eachLocation: (cb: LocationCallback) => { + locationIt.reset(); + while (locationIt.hasNext) { + const { location, isSecondary } = locationIt.move(); + cb(location, isSecondary); + } + }, mark(loci: Loci, action: MarkerAction) { if (!MarkerActions.is(_state.markerActions, action)) return false; if (ShapeGroup.isLoci(loci) || Shape.isLoci(loci)) { diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts index e959f918968aaaed200bac13a026db2b64dc6b1d..497aed6bf5c52a4d9822607c40e4c50fbe6b51fa 100644 --- a/src/mol-repr/structure/complex-representation.ts +++ b/src/mol-repr/structure/complex-representation.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2023 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> @@ -22,6 +22,7 @@ import { Clipping } from '../../mol-theme/clipping'; import { Transparency } from '../../mol-theme/transparency'; import { WebGLContext } from '../../mol-gl/webgl/context'; import { Substance } from '../../mol-theme/substance'; +import { LocationCallback } from '../util'; export function ComplexRepresentation<P extends StructureParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: (materialId: number, structure: Structure, props: PD.Values<P>, webgl?: WebGLContext) => ComplexVisual<P>): StructureRepresentation<P> { let version = 0; @@ -80,6 +81,10 @@ export function ComplexRepresentation<P extends StructureParams>(label: string, return [Structure.Loci(_structure.target)]; } + function eachLocation(cb: LocationCallback) { + visual?.eachLocation(cb); + } + function mark(loci: Loci, action: MarkerAction) { if (!_structure) return false; if (!MarkerActions.is(_state.markerActions, action)) return false; @@ -162,6 +167,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string, setTheme, getLoci, getAllLoci, + eachLocation, mark, destroy }; diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts index 526c5be397c93d512b344424b5015d7c8779a68c..99e8d1da36a2a9746e44f3839864bd652dc9b28e 100644 --- a/src/mol-repr/structure/complex-visual.ts +++ b/src/mol-repr/structure/complex-visual.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -15,7 +15,7 @@ import { createRenderObject, GraphicsRenderObject, RenderObjectValues } from '.. import { PickingId } from '../../mol-geo/geometry/picking'; import { Loci, isEveryLoci, EmptyLoci } from '../../mol-model/loci'; import { Interval } from '../../mol-data/int'; -import { VisualUpdateState } from '../util'; +import { LocationCallback, VisualUpdateState } from '../util'; import { ColorTheme } from '../../mol-theme/color'; import { ValueCell, deepEqual } from '../../mol-util'; import { createSizes, SizeData } from '../../mol-geo/geometry/size-data'; @@ -266,6 +266,13 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge getLoci(pickingId: PickingId) { return renderObject ? getLoci(pickingId, currentStructure, renderObject.id) : EmptyLoci; }, + eachLocation(cb: LocationCallback) { + locationIt.reset(); + while (locationIt.hasNext) { + const { location, isSecondary } = locationIt.move(); + cb(location, isSecondary); + } + }, mark(loci: Loci, action: MarkerAction) { return Visual.mark(renderObject, loci, action, lociApply, previousMark); }, diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts index a286188a54102892db8a07baaf137b747c0683ce..425b7194defd17ba6f3baf1d5ab6c2f4bed7440f 100644 --- a/src/mol-repr/structure/units-representation.ts +++ b/src/mol-repr/structure/units-representation.ts @@ -26,6 +26,7 @@ import { Clipping } from '../../mol-theme/clipping'; import { WebGLContext } from '../../mol-gl/webgl/context'; import { StructureGroup } from './visual/util/common'; import { Substance } from '../../mol-theme/substance'; +import { LocationCallback } from '../util'; export interface UnitsVisual<P extends StructureParams> extends Visual<StructureGroup, P> { } @@ -194,6 +195,12 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct return loci; } + function eachLocation(cb: LocationCallback) { + visuals.forEach(({ visual }) => { + visual.eachLocation(cb); + }); + } + function getAllLoci() { return [Structure.Loci(_structure.target)]; } @@ -312,6 +319,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct setTheme, getLoci, getAllLoci, + eachLocation, mark, destroy }; diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts index cd5e898e7501affaa640e1b083bf543ebc30cc23..66b6c19e99d14cf05d4dc20cc1d7360d5002b6d4 100644 --- a/src/mol-repr/structure/units-visual.ts +++ b/src/mol-repr/structure/units-visual.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -16,7 +16,7 @@ import { createRenderObject, GraphicsRenderObject, RenderObjectValues } from '.. import { PickingId } from '../../mol-geo/geometry/picking'; import { Loci, isEveryLoci, EmptyLoci } from '../../mol-model/loci'; import { Interval } from '../../mol-data/int'; -import { VisualUpdateState } from '../util'; +import { LocationCallback, VisualUpdateState } from '../util'; import { ColorTheme } from '../../mol-theme/color'; import { createMarkers } from '../../mol-geo/geometry/marker-data'; import { MarkerAction } from '../../mol-util/marker-action'; @@ -337,6 +337,13 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom getLoci(pickingId: PickingId) { return renderObject ? getLoci(pickingId, currentStructureGroup, renderObject.id) : EmptyLoci; }, + eachLocation(cb: LocationCallback) { + locationIt.reset(); + while (locationIt.hasNext) { + const { location, isSecondary } = locationIt.move(); + cb(location, isSecondary); + } + }, mark(loci: Loci, action: MarkerAction) { let hasInvariantId = true; if (StructureElement.Loci.is(loci)) { diff --git a/src/mol-repr/util.ts b/src/mol-repr/util.ts index b37e4954e457b02b0570d6df756f8fffa1912bcf..dfbb5af68a64968a5e5c57792d872d8814bfa994 100644 --- a/src/mol-repr/util.ts +++ b/src/mol-repr/util.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -10,6 +10,7 @@ import { VisualQuality } from '../mol-geo/geometry/base'; import { Box3D, SpacegroupCell } from '../mol-math/geometry'; import { ModelSymmetry } from '../mol-model-formats/structure/property/symmetry'; import { Volume } from '../mol-model/volume'; +import { Location } from '../mol-model/location'; export interface VisualUpdateState { updateTransform: boolean @@ -45,6 +46,8 @@ export namespace VisualUpdateState { } } +export type LocationCallback = (loc: Location, isSecondary: boolean) => void + // export interface QualityProps { diff --git a/src/mol-repr/visual.ts b/src/mol-repr/visual.ts index f6a5c0f1a8bf421f160d7648cc015e86ba4c61a5..2d0d06f9661a9556423b24442135d5059574c663 100644 --- a/src/mol-repr/visual.ts +++ b/src/mol-repr/visual.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -31,6 +31,7 @@ import { applyMeshOverpaintSmoothing, applyMeshSubstanceSmoothing, applyMeshTran import { applyTextureMeshOverpaintSmoothing, applyTextureMeshSubstanceSmoothing, applyTextureMeshTransparencySmoothing } from '../mol-geo/geometry/texture-mesh/color-smoothing'; import { Substance } from '../mol-theme/substance'; import { applySubstanceMaterial, clearSubstance, createSubstance } from '../mol-geo/geometry/substance-data'; +import { LocationCallback } from './util'; export interface VisualContext { readonly runtime: RuntimeContext @@ -45,6 +46,7 @@ interface Visual<D, P extends PD.Params> { readonly geometryVersion: number createOrUpdate: (ctx: VisualContext, theme: Theme, props: PD.Values<P>, data?: D) => Promise<void> | void getLoci: (pickingId: PickingId) => Loci + eachLocation: (cb: LocationCallback) => void mark: (loci: Loci, action: MarkerAction) => boolean setVisibility: (visible: boolean) => void setAlphaFactor: (alphaFactor: number) => void diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts index d83dfad49786bbba9cd611d68a47acc17d29fcac..d2921e734c883eeb29f015bf7c6cfce19bca95e6 100644 --- a/src/mol-repr/volume/representation.ts +++ b/src/mol-repr/volume/representation.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -15,7 +15,7 @@ import { createRenderObject, getNextMaterialId, GraphicsRenderObject } from '../ import { PickingId } from '../../mol-geo/geometry/picking'; import { Loci, isEveryLoci, EmptyLoci, isEmptyLoci } from '../../mol-model/loci'; import { Interval, SortedArray } from '../../mol-data/int'; -import { getQualityProps, VisualUpdateState } from '../util'; +import { getQualityProps, LocationCallback, VisualUpdateState } from '../util'; import { ColorTheme } from '../../mol-theme/color'; import { ValueCell } from '../../mol-util'; import { createSizes } from '../../mol-geo/geometry/size-data'; @@ -233,6 +233,13 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet getLoci(pickingId: PickingId) { return renderObject ? getLoci(pickingId, currentVolume, currentKey, currentProps, renderObject.id) : EmptyLoci; }, + eachLocation(cb: LocationCallback) { + locationIt.reset(); + while (locationIt.hasNext) { + const { location, isSecondary } = locationIt.move(); + cb(location, isSecondary); + } + }, mark(loci: Loci, action: MarkerAction) { return Visual.mark(renderObject, loci, action, lociApply); }, @@ -436,6 +443,11 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: getAllLoci: (): Loci[] => { return [getLoci(_volume, _props)]; }, + eachLocation: (cb: LocationCallback) => { + visuals.forEach(visual => { + visual.eachLocation(cb); + }); + }, mark, destroy };