Browse Source

wip, shape instance support

Alexander Rose 6 years ago
parent
commit
934f5cfa77

+ 16 - 6
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]

+ 11 - 8
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)

+ 2 - 2
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
         },

+ 1 - 1
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'
             }

+ 16 - 8
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)
+}, 1000)