From 934f5cfa77d339672275107195b0e206884c5a38 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 25 Jan 2019 14:12:50 -0800 Subject: [PATCH] wip, shape instance support --- src/mol-model/shape/shape.ts | 22 ++++++++++++++++------ src/mol-repr/shape/representation.ts | 19 +++++++++++-------- src/mol-theme/color/shape-group.ts | 4 ++-- src/mol-theme/label.ts | 2 +- src/tests/browser/render-shape.ts | 24 ++++++++++++++++-------- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/mol-model/shape/shape.ts b/src/mol-model/shape/shape.ts index 075af8922..4825815ff 100644 --- a/src/mol-model/shape/shape.ts +++ b/src/mol-model/shape/shape.ts @@ -11,13 +11,20 @@ import { Geometry } from 'mol-geo/geometry/geometry'; import { Mat4 } from 'mol-math/linear-algebra'; export interface Shape<G extends Geometry = Geometry> { + /** A uuid to identify a shape object */ readonly id: UUID + /** A name to describe the shape */ readonly name: string + /** The geometry of the shape, e.g. `Mesh` or `Lines` */ readonly geometry: G + /** An array of transformation matrices to describe multiple instances of the geometry */ readonly transforms: Mat4[] + /** Number of groups in the geometry */ readonly groupCount: number - getColor(groupId: number): Color - getLabel(groupId: number): string + /** Get color for a given group */ + getColor(groupId: number, instanceId: number): Color + /** Get color for a given group */ + getLabel(groupId: number, instanceId: number): string } export namespace Shape { @@ -37,10 +44,11 @@ export namespace Shape { readonly kind: 'group-location' shape: Shape group: number + instance: number } - export function Location(shape?: Shape, group?: number): Location { - return { kind: 'group-location', shape: shape!, group: group || 0 }; + export function Location(shape?: Shape, group?: number, instance?: number): Location { + return { kind: 'group-location', shape: shape!, group: group || 0, instance: instance || 0 }; } export function isLocation(x: any): x is Location { @@ -53,10 +61,11 @@ export namespace Shape { readonly groups: ReadonlyArray<{ ids: OrderedSet<number> }> + readonly instance: number } - export function Loci(shape: Shape, groups: ArrayLike<{ ids: OrderedSet<number> }>): Loci { - return { kind: 'group-loci', shape, groups: groups as Loci['groups'] }; + export function Loci(shape: Shape, groups: ArrayLike<{ ids: OrderedSet<number> }>, instance: number): Loci { + return { kind: 'group-loci', shape, groups: groups as Loci['groups'], instance }; } export function isLoci(x: any): x is Loci { @@ -66,6 +75,7 @@ export namespace Shape { export function areLociEqual(a: Loci, b: Loci) { if (a.shape !== b.shape) return false if (a.groups.length !== b.groups.length) return false + if (a.instance !== b.instance) return false for (let i = 0, il = a.groups.length; i < il; ++i) { const groupA = a.groups[i] const groupB = b.groups[i] diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts index 121b53dbf..6b98c2641 100644 --- a/src/mol-repr/shape/representation.ts +++ b/src/mol-repr/shape/representation.ts @@ -131,9 +131,9 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa updated, createOrUpdate, getLoci(pickingId: PickingId) { - const { objectId, groupId } = pickingId + const { objectId, groupId, instanceId } = pickingId if (_renderObject && _renderObject.id === objectId) { - return Shape.Loci(_shape, [{ ids: OrderedSet.ofSingleton(groupId) }]) + return Shape.Loci(_shape, [{ ids: OrderedSet.ofSingleton(groupId) }], instanceId) } return EmptyLoci }, @@ -141,17 +141,19 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa if (!_renderObject) return false const { tMarker } = _renderObject.values let changed = false + const { groupCount, count } = locationIt if (isEveryLoci(loci)) { - if (applyMarkerAction(tMarker.ref.value.array, 0, _shape.groupCount, action)) changed = true + if (applyMarkerAction(tMarker.ref.value.array, 0, count, action)) changed = true } else if (Shape.isLoci(loci)) { - for (const g of loci.groups) { + const { instance, groups } = loci + for (const g of groups) { if (Interval.is(g.ids)) { - const start = Interval.start(g.ids) - const end = Interval.end(g.ids) + const start = instance * groupCount + Interval.start(g.ids) + const end = instance * groupCount + Interval.end(g.ids) if (applyMarkerAction(tMarker.ref.value.array, start, end, action)) changed = true } else { for (let i = 0, _i = g.ids.length; i < _i; i++) { - const idx = g.ids[i]; + const idx = instance * groupCount + g.ids[i]; if (applyMarkerAction(tMarker.ref.value.array, idx, idx + 1, action)) changed = true } } @@ -194,8 +196,9 @@ export namespace ShapeGroupIterator { export function fromShape(shape: Shape): LocationIterator { const instanceCount = shape.transforms.length const location = Shape.Location(shape) - const getLocation = (groupIndex: number) => { + const getLocation = (groupIndex: number, instanceIndex: number) => { location.group = groupIndex + location.instance = instanceIndex return location } return LocationIterator(shape.groupCount, instanceCount, getLocation) diff --git a/src/mol-theme/color/shape-group.ts b/src/mol-theme/color/shape-group.ts index bec91fb43..b352ecd50 100644 --- a/src/mol-theme/color/shape-group.ts +++ b/src/mol-theme/color/shape-group.ts @@ -23,10 +23,10 @@ export function getShapeGroupColorThemeParams(ctx: ThemeDataContext) { export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: PD.Values<ShapeGroupColorThemeParams>): ColorTheme<ShapeGroupColorThemeParams> { return { factory: ShapeGroupColorTheme, - granularity: 'group', + granularity: 'groupInstance', color: (location: Location): Color => { if (Shape.isLocation(location)) { - return location.shape.getColor(location.group) + 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 926411df1..8aac229a5 100644 --- a/src/mol-theme/label.ts +++ b/src/mol-theme/label.ts @@ -36,7 +36,7 @@ export function labelFirst(loci: Loci): string { case 'group-loci': const g = loci.groups[0] if (g) { - return loci.shape.getLabel(OrderedSet.getAt(g.ids, 0)) + return loci.shape.getLabel(OrderedSet.getAt(g.ids, 0), loci.instance) } else { return 'Unknown' } diff --git a/src/tests/browser/render-shape.ts b/src/tests/browser/render-shape.ts index 9bb7587ad..f950cd8f8 100644 --- a/src/tests/browser/render-shape.ts +++ b/src/tests/browser/render-shape.ts @@ -8,7 +8,7 @@ import './index.html' import { Canvas3D } from 'mol-canvas3d/canvas3d'; import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; import { Sphere } from 'mol-geo/primitive/sphere'; -import { Mat4 } from 'mol-math/linear-algebra'; +import { Mat4, Vec3 } from 'mol-math/linear-algebra'; import { Shape } from 'mol-model/shape'; import { ShapeRepresentation } from 'mol-repr/shape/representation'; import { ColorNames } from 'mol-util/color/tables'; @@ -52,13 +52,21 @@ builderState.currentGroup = 0 MeshBuilder.addPrimitive(builderState, t, sphere) const mesh = MeshBuilder.getMesh(builderState) -const myData = { mesh, colors: [ColorNames.aquamarine], labels: ['FooBaz'] } +const myData = { + mesh, + groupCount: 1, + colors: [ColorNames.tomato, ColorNames.springgreen], + labels: ['FooBaz0', 'FooBaz1'], + transforms: [Mat4.identity(), Mat4.fromTranslation(Mat4.zero(), Vec3.create(3, 0, 0))] +} type MyData = typeof myData -function getShape(data: MyData) { - return Shape.create( - 'test', data.mesh, - (groupId: number) => data.colors[groupId], - (groupId: number) => data.labels[groupId] +function getShape(data: MyData, props: {}, shape?: Shape<Mesh>) { + const { mesh, colors, labels, transforms, groupCount } = data + return shape || Shape.create( + 'test', mesh, + (groupId: number, instanceId: number) => colors[instanceId * groupCount + groupId], + (groupId: number, instanceId: number) => labels[instanceId * groupCount + groupId], + transforms ) } @@ -75,4 +83,4 @@ add() setTimeout(async () => { myData.colors[0] = ColorNames.darkmagenta await repr.createOrUpdate({}, myData).run() -}, 2000) \ No newline at end of file +}, 1000) \ No newline at end of file -- GitLab