Bladeren bron

wip, added shape object and re-named render 'element' to 'group'

Alexander Rose 6 jaren geleden
bovenliggende
commit
b8c79f25b1
70 gewijzigde bestanden met toevoegingen van 653 en 618 verwijderingen
  1. 36 18
      src/apps/canvas/index.ts
  2. 2 2
      src/mol-app/ui/visualization/viewport.tsx
  3. 13 22
      src/mol-geo/mesh/mesh-builder.ts
  4. 4 14
      src/mol-geo/mesh/mesh.ts
  5. 1 1
      src/mol-geo/primitive/pyramid.ts
  6. 1 1
      src/mol-geo/primitive/sheet.ts
  7. 1 1
      src/mol-geo/primitive/tube.ts
  8. 0 108
      src/mol-geo/representation/custom/index.ts
  9. 128 0
      src/mol-geo/representation/shape/index.ts
  10. 2 2
      src/mol-geo/representation/structure/complex-representation.ts
  11. 4 4
      src/mol-geo/representation/structure/complex-visual.ts
  12. 4 4
      src/mol-geo/representation/structure/units-visual.ts
  13. 8 8
      src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts
  14. 13 13
      src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
  15. 8 8
      src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
  16. 4 5
      src/mol-geo/representation/structure/visual/element-point.ts
  17. 1 2
      src/mol-geo/representation/structure/visual/element-sphere.ts
  18. 4 5
      src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
  19. 5 6
      src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
  20. 5 5
      src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
  21. 5 6
      src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
  22. 4 5
      src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
  23. 6 7
      src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
  24. 4 5
      src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
  25. 11 11
      src/mol-geo/representation/structure/visual/util/common.ts
  26. 20 5
      src/mol-geo/representation/structure/visual/util/element.ts
  27. 34 3
      src/mol-geo/representation/structure/visual/util/link.ts
  28. 0 141
      src/mol-geo/representation/structure/visual/util/location-iterator.ts
  29. 3 3
      src/mol-geo/representation/util.ts
  30. 3 3
      src/mol-geo/representation/volume/surface.ts
  31. 0 53
      src/mol-geo/shape/vertex-map.ts
  32. 18 18
      src/mol-geo/util/color-data.ts
  33. 97 0
      src/mol-geo/util/location-iterator.ts
  34. 5 8
      src/mol-geo/util/marching-cubes/algorithm.ts
  35. 6 6
      src/mol-geo/util/marker-data.ts
  36. 3 3
      src/mol-geo/util/mesh-data.ts
  37. 1 1
      src/mol-geo/util/picking.ts
  38. 17 17
      src/mol-geo/util/size-data.ts
  39. 5 5
      src/mol-gl/_spec/renderer.spec.ts
  40. 1 1
      src/mol-gl/renderable/point.ts
  41. 6 6
      src/mol-gl/renderable/schema.ts
  42. 8 8
      src/mol-gl/shader/chunks/assign-color-varying.glsl
  43. 1 1
      src/mol-gl/shader/chunks/assign-marker-varying.glsl
  44. 1 1
      src/mol-gl/shader/chunks/assign-material-color.glsl
  45. 1 1
      src/mol-gl/shader/chunks/color-frag-params.glsl
  46. 3 3
      src/mol-gl/shader/chunks/color-vert-params.glsl
  47. 1 1
      src/mol-gl/shader/chunks/common-frag-params.glsl
  48. 2 2
      src/mol-gl/shader/chunks/common-vert-params.glsl
  49. 1 1
      src/mol-gl/shader/mesh.frag
  50. 2 2
      src/mol-gl/shader/mesh.vert
  51. 7 7
      src/mol-gl/shader/point.vert
  52. 4 4
      src/mol-gl/shader/utils/read-from-texture.glsl
  53. 1 1
      src/mol-gl/webgl/render-item.ts
  54. 4 3
      src/mol-io/reader/obj/parser.ts
  55. 2 14
      src/mol-model/location.ts
  56. 2 14
      src/mol-model/loci.ts
  57. 7 0
      src/mol-model/shape.ts
  58. 54 0
      src/mol-model/shape/shape.ts
  59. 1 1
      src/mol-model/structure/structure/element.ts
  60. 8 0
      src/mol-util/array.ts
  61. 8 3
      src/mol-view/label.ts
  62. 4 1
      src/mol-view/theme/color.ts
  63. 1 1
      src/mol-view/theme/color/carbohydrate-symbol.ts
  64. 1 1
      src/mol-view/theme/color/chain-id.ts
  65. 1 1
      src/mol-view/theme/color/cross-link.ts
  66. 1 1
      src/mol-view/theme/color/element-index.ts
  67. 1 1
      src/mol-view/theme/color/element-symbol.ts
  68. 24 0
      src/mol-view/theme/color/shape-group.ts
  69. 1 1
      src/mol-view/theme/size/physical.ts
  70. 8 8
      src/mol-view/viewer.ts

+ 36 - 18
src/apps/canvas/index.ts

@@ -8,7 +8,7 @@ import './index.html'
 
 import Viewer from 'mol-view/viewer';
 import CIF, { CifBlock } from 'mol-io/reader/cif'
-import { parse as parseObj } from 'mol-io/reader/obj/parser'
+// import { parse as parseObj } from 'mol-io/reader/obj/parser'
 import { readUrlAs } from 'mol-util/read'
 import { Model, Format, Structure, StructureSymmetry } from 'mol-model/structure';
 import { CartoonRepresentation } from 'mol-geo/representation/structure/representation/cartoon';
@@ -17,9 +17,11 @@ import { EveryLoci } from 'mol-model/loci';
 import { MarkerAction } from 'mol-geo/util/marker-data';
 import { labelFirst } from 'mol-view/label';
 import { Queries as Q, StructureProperties as SP, StructureSelection, StructureQuery } from 'mol-model/structure';
-import { MeshBuilder } from 'mol-geo/shape/mesh-builder';
-import { CustomRepresentation } from 'mol-geo/representation/custom';
-import { Vec3 } from 'mol-math/linear-algebra';
+import { MeshBuilder } from 'mol-geo/mesh/mesh-builder';
+import { ShapeRepresentation } from 'mol-geo/representation/shape';
+import { Vec3, Mat4 } from 'mol-math/linear-algebra';
+import { Shape } from 'mol-model/shape';
+import { Color } from 'mol-util/color';
 
 const container = document.getElementById('container')
 if (!container) throw new Error('Can not find element with id "container".')
@@ -50,13 +52,13 @@ viewer.input.move.subscribe(({x, y, inside, buttons}) => {
 })
 
 
-async function getObjFromUrl(url: string) {
-    const data = await readUrlAs(url, false) as string
-    const comp = parseObj(data)
-    const parsed = await comp.run()
-    if (parsed.isError) throw parsed
-    return parsed.result
-}
+// async function getObjFromUrl(url: string) {
+//     const data = await readUrlAs(url, false) as string
+//     const comp = parseObj(data)
+//     const parsed = await comp.run()
+//     if (parsed.isError) throw parsed
+//     return parsed.result
+// }
 
 async function getCifFromUrl(url: string) {
     const data = await readUrlAs(url, false)
@@ -111,18 +113,34 @@ async function init() {
     }).run()
     viewer.add(ballStickRepr)
 
-    // create a custom mesh
+    // create a mesh
     const meshBuilder = MeshBuilder.create(256, 128)
-    meshBuilder.setId(0)
+    const groupColors: Color[] = []
+    const groupLabels: string[] = []
+    // red sphere
+    meshBuilder.setGroup(0)
+    groupColors[0] = Color(0xFF2233)
+    groupLabels[0] = 'red sphere'
     meshBuilder.addSphere(Vec3.create(0, 0, 0), 4, 2)
+    // green cube
+    meshBuilder.setGroup(1)
+    groupColors[1] = Color(0x2233FF)
+    groupLabels[1] = 'blue cube'
+    const t = Mat4.identity()
+    Mat4.setTranslation(t, Vec3.create(10, 0, 0))
+    Mat4.scale(t, t, Vec3.create(3, 3, 3))
+    meshBuilder.addBox(t)
     const mesh = meshBuilder.getMesh()
-    // Mesh.computeNormalsImmediate(mesh)
+    // const mesh = getObjFromUrl('mesh.obj')
 
-    // mesh = getObjFromUrl('mesh.obj')
+    // create shape from mesh
+    const shape = Shape.create(mesh, 'myShape', group => groupColors[group], group => groupLabels[group])
 
-    // add representation from custom mesh
-    const customRepr = CustomRepresentation()
-    await customRepr.create(mesh, {
+    // add representation from shape
+    const customRepr = ShapeRepresentation()
+    await customRepr.create(shape, {
+        colorTheme: { name: 'shape-group' },
+        // colorTheme: { name: 'uniform', value: Color(0xFFCC22) },
         useFog: false // TODO fog not working properly
     }).run()
     viewer.add(customRepr)

+ 2 - 2
src/mol-app/ui/visualization/viewport.tsx

@@ -151,7 +151,7 @@ export class Viewport extends View<ViewportController, ViewportState, { noWebGl?
                 images: {
                     'object': viewer.getImageData('pickObject'),
                     'instance': viewer.getImageData('pickInstance'),
-                    'element': viewer.getImageData('pickElement')
+                    'group': viewer.getImageData('pickGroup')
                 }
             })
         })
@@ -168,7 +168,7 @@ export class Viewport extends View<ViewportController, ViewportState, { noWebGl?
 
             // TODO use LabelLoci event and make configurable
             const label = labelFirst(loci)
-            const info = `Object: ${p.objectId}, Instance: ${p.instanceId}, Element: ${p.elementId}, Label: ${label}`
+            const info = `Object: ${p.objectId}, Instance: ${p.instanceId}, Group: ${p.groupId}, Label: ${label}`
             this.setState({ info })
         })
 

+ 13 - 22
src/mol-geo/shape/mesh-builder.ts → src/mol-geo/mesh/mesh-builder.ts

@@ -19,7 +19,7 @@ import { StarProps, Star } from '../primitive/star';
 import { Octahedron, PerforatedOctahedron } from '../primitive/octahedron';
 import { Primitive } from '../primitive/primitive';
 import { DiamondPrism, PentagonalPrism, HexagonalPrism } from '../primitive/prism';
-import { OctagonalPyramide, PerforatedOctagonalPyramide } from '../primitive/pyramid';
+import { OctagonalPyramide, PerforatedOctagonalPyramid } from '../primitive/pyramid';
 import { PerforatedBox, Box } from '../primitive/box';
 import { Wedge } from '../primitive/wedge';
 
@@ -49,7 +49,7 @@ export interface MeshBuilder {
     addSphere(center: Vec3, radius: number, detail: number): void
     addTube(centers: ArrayLike<number>, normals: ArrayLike<number>, binormals: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean): void
     addSheet(centers: ArrayLike<number>, normals: ArrayLike<number>, binormals: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean): void
-    setId(id: number): void
+    setGroup(id: number): void
     getMesh(): Mesh
 }
 
@@ -114,10 +114,9 @@ export namespace MeshBuilder {
         const indices = ChunkedArray.create(Uint32Array, 3, chunkSize * 3, mesh ? mesh.indexBuffer.ref.value : initialCount * 3);
         const state: MeshBuilderState = { vertices, normals, indices };
 
-        const ids = ChunkedArray.create(Float32Array, 1, chunkSize, mesh ? mesh.idBuffer.ref.value : initialCount);
-        const offsets = ChunkedArray.create(Uint32Array, 1, chunkSize, mesh ? mesh.offsetBuffer.ref.value : initialCount);
+        const groups = ChunkedArray.create(Float32Array, 1, chunkSize, mesh ? mesh.groupBuffer.ref.value : initialCount);
 
-        let currentId = -1
+        let currentGroup = -1
 
         function add(t: Mat4, _vertices: ArrayLike<number>, _normals: ArrayLike<number>, _indices: ArrayLike<number>) {
             const { elementCount } = vertices
@@ -131,8 +130,8 @@ export namespace MeshBuilder {
                 Vec3.fromArray(tmpV, _normals, i)
                 Vec3.transformMat3(tmpV, tmpV, n)
                 ChunkedArray.add3(normals, tmpV[0], tmpV[1], tmpV[2]);
-                // id
-                ChunkedArray.add(ids, currentId);
+                // group
+                ChunkedArray.add(groups, currentGroup);
             }
             for (let i = 0, il = _indices.length; i < il; i += 3) {
                 ChunkedArray.add3(indices, _indices[i] + elementCount, _indices[i + 1] + elementCount, _indices[i + 2] + elementCount);
@@ -174,7 +173,7 @@ export namespace MeshBuilder {
                 add(t, vertices, normals, indices)
             },
             addPerforatedOctagonalPyramid: (t: Mat4) => {
-                const { vertices, normals, indices } = PerforatedOctagonalPyramide()
+                const { vertices, normals, indices } = PerforatedOctagonalPyramid()
                 add(t, vertices, normals, indices)
             },
             addStar: (t: Mat4, props?: StarProps) => {
@@ -242,36 +241,28 @@ export namespace MeshBuilder {
             },
             addTube: (centers: ArrayLike<number>, normals: ArrayLike<number>, binormals: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean) => {
                 const addedVertexCount = addTube(centers, normals, binormals, linearSegments, radialSegments, width, height, waveFactor, startCap, endCap, state)
-                for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(ids, currentId);
+                for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup);
             },
             addSheet: (controls: ArrayLike<number>, normals: ArrayLike<number>, binormals: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean) => {
                 const addedVertexCount = addSheet(controls, normals, binormals, linearSegments, width, height, arrowHeight, startCap, endCap, state)
-                for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(ids, currentId);
+                for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup);
             },
-            setId: (id: number) => {
-                if (currentId !== id) {
-                    currentId = id
-                    ChunkedArray.add(offsets, vertices.elementCount)
-                }
+            setGroup: (group: number) => {
+                currentGroup = group
             },
             getMesh: () => {
-                ChunkedArray.add(offsets, vertices.elementCount)
                 const vb = ChunkedArray.compact(vertices, true) as Float32Array
                 const ib = ChunkedArray.compact(indices, true) as Uint32Array
                 const nb = ChunkedArray.compact(normals, true) as Float32Array
-                const idb = ChunkedArray.compact(ids, true) as Float32Array
-                const ob = ChunkedArray.compact(offsets, true) as Uint32Array
+                const idb = ChunkedArray.compact(groups, true) as Float32Array
                 return {
                     vertexCount: vertices.elementCount,
                     triangleCount: indices.elementCount,
-                    offsetCount: offsets.elementCount,
                     vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
                     indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
                     normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
-                    idBuffer: mesh ? ValueCell.update(mesh.idBuffer, idb) : ValueCell.create(idb),
-                    offsetBuffer: mesh ? ValueCell.update(mesh.offsetBuffer, ob) : ValueCell.create(ob),
+                    groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, idb) : ValueCell.create(idb),
                     normalsComputed: true,
-                    offsetsComputed: true,
                 }
             }
         }

+ 4 - 14
src/mol-geo/shape/mesh.ts → src/mol-geo/mesh/mesh.ts

@@ -15,8 +15,6 @@ export interface Mesh {
     vertexCount: number,
     /** Number of triangles in the mesh */
     triangleCount: number,
-    /** Number of offsets in the mesh */
-    offsetCount: number,
 
     /** Vertex buffer as array of xyz values wrapped in a value cell */
     vertexBuffer: ValueCell<Float32Array>,
@@ -24,15 +22,11 @@ export interface Mesh {
     indexBuffer: ValueCell<Uint32Array>,
     /** Normal buffer as array of xyz values for each vertex wrapped in a value cell */
     normalBuffer: ValueCell<Float32Array>,
-    /** Id buffer as array of ids for each vertex wrapped in a value cell */
-    idBuffer: ValueCell<Float32Array>,
-    /** Offset buffer as array of offsets for id ranges wrapped in a value cell */
-    offsetBuffer: ValueCell<Uint32Array>,
+    /** Group buffer as array of group ids for each vertex wrapped in a value cell */
+    groupBuffer: ValueCell<Float32Array>,
 
     /** Flag indicating if normals are computed for the current set of vertices */
     normalsComputed: boolean,
-    /** Flag indicating if offsets are computed for the current set of ids */
-    offsetsComputed: boolean,
 
     /** Bounding sphere of the mesh */
     boundingSphere?: Sphere3D
@@ -43,19 +37,15 @@ export namespace Mesh {
         const vb = mesh ? mesh.vertexBuffer.ref.value : new Float32Array(0)
         const ib = mesh ? mesh.indexBuffer.ref.value : new Uint32Array(0)
         const nb = mesh ? mesh.normalBuffer.ref.value : new Float32Array(0)
-        const idb = mesh ? mesh.idBuffer.ref.value : new Float32Array(0)
-        const ob = mesh ? mesh.offsetBuffer.ref.value : new Uint32Array(0)
+        const idb = mesh ? mesh.groupBuffer.ref.value : new Float32Array(0)
         return {
             vertexCount: 0,
             triangleCount: 0,
-            offsetCount: 0,
             vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
             indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
             normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
-            idBuffer: mesh ? ValueCell.update(mesh.idBuffer, idb) : ValueCell.create(idb),
-            offsetBuffer: mesh ? ValueCell.update(mesh.offsetBuffer, ob) : ValueCell.create(ob),
+            groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, idb) : ValueCell.create(idb),
             normalsComputed: true,
-            offsetsComputed: true,
         }
     }
 

+ 1 - 1
src/mol-geo/primitive/pyramid.ts

@@ -62,7 +62,7 @@ export function OctagonalPyramide() {
 //
 
 let perforatedOctagonalPyramide: Primitive
-export function PerforatedOctagonalPyramide() {
+export function PerforatedOctagonalPyramid() {
     if (!perforatedOctagonalPyramide) {
         const points = polygon(8, true)
         const vertices = new Float32Array(8 * 3 + 6)

+ 1 - 1
src/mol-geo/primitive/sheet.ts

@@ -7,7 +7,7 @@
 
 import { Vec3 } from 'mol-math/linear-algebra';
 import { ChunkedArray } from 'mol-data/util';
-import { MeshBuilderState } from '../shape/mesh-builder';
+import { MeshBuilderState } from '../mesh/mesh-builder';
 
 const tA = Vec3.zero()
 const tB = Vec3.zero()

+ 1 - 1
src/mol-geo/primitive/tube.ts

@@ -7,7 +7,7 @@
 
 import { Vec3 } from 'mol-math/linear-algebra';
 import { ChunkedArray } from 'mol-data/util';
-import { MeshBuilderState } from '../shape/mesh-builder';
+import { MeshBuilderState } from '../mesh/mesh-builder';
 
 const normalVector = Vec3.zero()
 const binormalVector = Vec3.zero()

+ 0 - 108
src/mol-geo/representation/custom/index.ts

@@ -1,108 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { Task } from 'mol-task'
-import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
-import { RepresentationProps, Representation } from '..';
-import { PickingId } from '../../util/picking';
-import { Loci, EmptyLoci, CustomLoci, isCustomLoci, isEveryLoci } from 'mol-model/loci';
-import { MarkerAction, applyMarkerAction, createMarkers } from '../../util/marker-data';
-import { createRenderableState, createMeshValues, createIdentityTransform, DefaultMeshProps } from '../util';
-import { Mesh } from '../../shape/mesh';
-import { getMeshData } from '../../util/mesh-data';
-import { MeshValues } from 'mol-gl/renderable';
-import { createValueColor } from '../../util/color-data';
-import { Color } from 'mol-util/color';
-import { CustomLocation } from 'mol-model/location';
-import { ValueCell } from 'mol-util';
-
-export interface CustomRepresentation<P extends RepresentationProps = {}> extends Representation<Mesh, P> { }
-
-export const DefaultCustomProps = {
-    ...DefaultMeshProps,
-}
-export type CustomProps = typeof DefaultCustomProps
-
-export function CustomRepresentation<P extends CustomProps>(): CustomRepresentation<P> {
-    const renderObjects: RenderObject[] = []
-    let _renderObject: MeshRenderObject
-    let _mesh: Mesh
-    let _props: P
-
-    function create(mesh: Mesh, props: Partial<P> = {}) {
-        _props = Object.assign({}, DefaultCustomProps, _props, props)
-        return Task.create('CustomRepresentation.create', async ctx => {
-            renderObjects.length = 0
-            _mesh = mesh
-
-            const elementCount = mesh.triangleCount
-            const instanceCount = 1
-
-            const color = createValueColor(Color(0x7ec0ee))
-            const marker = createMarkers(instanceCount * elementCount)
-            const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
-
-            const values: MeshValues = {
-                ...getMeshData(mesh),
-                ...createMeshValues(_props, counts),
-                aTransform: createIdentityTransform(),
-                ...color,
-                ...marker,
-
-                elements: mesh.indexBuffer,
-            }
-            const state = createRenderableState(_props)
-
-            _renderObject = createMeshRenderObject(values, state)
-            renderObjects.push(_renderObject)
-        });
-    }
-
-    function update(props: Partial<P>) {
-        return Task.create('CustomRepresentation.update', async ctx => {
-            // TODO
-        })
-    }
-
-    return {
-        get renderObjects () { return renderObjects },
-        get props () { return _props },
-        create,
-        update,
-        getLoci(pickingId: PickingId) {
-            const { objectId, elementId } = pickingId
-            if (_renderObject.id === objectId) {
-                return CustomLoci([ CustomLocation(_mesh, elementId) ])
-            }
-            return EmptyLoci
-        },
-        mark(loci: Loci, action: MarkerAction) {
-            const { tMarker } = _renderObject.values
-            let changed = false
-            if (isEveryLoci(loci)) {
-                if (applyMarkerAction(tMarker.ref.value.array, 0, _mesh.triangleCount, action)) changed = true
-                changed = true
-            } else if (isCustomLoci(loci)) {
-                for (const l of loci.locations) {
-                    if (l.data === _mesh) {
-                        if (applyMarkerAction(tMarker.ref.value.array, 0, _mesh.triangleCount, action)) changed = true
-                        // TODO
-                        // const idx = l.key
-                        // if (idx !== undefined) {
-                        //     if (applyMarkerAction(tMarker.ref.value.array, idx, idx + 1, action)) changed = true
-                        // }
-                    }
-                }
-            }
-            if (changed) {
-                ValueCell.update(tMarker, tMarker.ref.value)
-            }
-        },
-        destroy() {
-            // TODO
-        }
-    }
-}

+ 128 - 0
src/mol-geo/representation/shape/index.ts

@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Task } from 'mol-task'
+import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
+import { RepresentationProps, Representation } from '..';
+import { PickingId } from '../../util/picking';
+import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
+import { MarkerAction, applyMarkerAction, createMarkers } from '../../util/marker-data';
+import { createRenderableState, createMeshValues, createIdentityTransform, DefaultMeshProps } from '../util';
+import { getMeshData } from '../../util/mesh-data';
+import { MeshValues } from 'mol-gl/renderable';
+import { ValueCell } from 'mol-util';
+import { ColorThemeProps } from 'mol-view/theme/color';
+import { Shape } from 'mol-model/shape';
+import { LocationIterator } from '../../util/location-iterator';
+import { arrayMax } from 'mol-util/array';
+import { createColors } from '../structure/visual/util/common';
+import { OrderedSet, Interval } from 'mol-data/int';
+
+export interface ShapeRepresentation<P extends RepresentationProps = {}> extends Representation<Shape, P> { }
+
+export const DefaultShapeProps = {
+    ...DefaultMeshProps,
+    colorTheme: { name: 'shape-group' } as ColorThemeProps
+}
+export type ShapeProps = typeof DefaultShapeProps
+
+export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation<P> {
+    const renderObjects: RenderObject[] = []
+    let _renderObject: MeshRenderObject
+    let _shape: Shape
+    let _props: P
+
+    function create(shape: Shape, props: Partial<P> = {}) {
+        _props = Object.assign({}, DefaultShapeProps, _props, props)
+        _shape = shape
+
+        return Task.create('ShapeRepresentation.create', async ctx => {
+            renderObjects.length = 0
+
+            const mesh = shape.mesh
+            const locationIt = ShapeGroupIterator.fromShape(shape)
+            const { groupCount, instanceCount } = locationIt
+
+            const color = createColors(locationIt, _props.colorTheme)
+            const marker = createMarkers(instanceCount * groupCount)
+            const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }
+
+            const values: MeshValues = {
+                ...getMeshData(mesh),
+                ...createMeshValues(_props, counts),
+                aTransform: createIdentityTransform(),
+                ...color,
+                ...marker,
+
+                elements: mesh.indexBuffer,
+            }
+            const state = createRenderableState(_props)
+
+            _renderObject = createMeshRenderObject(values, state)
+            console.log(_renderObject)
+            renderObjects.push(_renderObject)
+        });
+    }
+
+    function update(props: Partial<P>) {
+        return Task.create('ShapeRepresentation.update', async ctx => {
+            // TODO
+        })
+    }
+
+    return {
+        get renderObjects () { return renderObjects },
+        get props () { return _props },
+        create,
+        update,
+        getLoci(pickingId: PickingId) {
+            const { objectId, groupId } = pickingId
+            if (_renderObject.id === objectId) {
+                return Shape.Loci([ { shape: _shape, ids: OrderedSet.ofSingleton(groupId) } ])
+            }
+            return EmptyLoci
+        },
+        mark(loci: Loci, action: MarkerAction) {
+            const { tMarker } = _renderObject.values
+            let changed = false
+            if (isEveryLoci(loci)) {
+                if (applyMarkerAction(tMarker.ref.value.array, 0, _shape.mesh.triangleCount, action)) changed = true
+            } else if (Shape.isLoci(loci)) {
+                for (const g of loci.groups) {
+                    if (Interval.is(g.ids)) {
+                        const start = Interval.start(g.ids)
+                        const end = 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];
+                            if (applyMarkerAction(tMarker.ref.value.array, idx, idx + 1, action)) changed = true
+                        }
+                    }
+                }
+            }
+            if (changed) {
+                ValueCell.update(tMarker, tMarker.ref.value)
+            }
+        },
+        destroy() {
+            // TODO
+        }
+    }
+}
+
+export namespace ShapeGroupIterator {
+    export function fromShape(shape: Shape): LocationIterator {
+        const groupCount = arrayMax(shape.mesh.groupBuffer.ref.value) + 1
+        const instanceCount = 1
+        const location = Shape.Location(shape)
+        const getLocation = (groupIndex: number) => {
+            location.group = groupIndex
+            return location
+        }
+        return LocationIterator(groupCount, instanceCount, getLocation)
+    }
+}

+ 2 - 2
src/mol-geo/representation/structure/complex-representation.ts

@@ -22,7 +22,7 @@ export function ComplexRepresentation<P extends StructureProps>(visualCtor: () =
 
     function create(structure: Structure, props: Partial<P> = {}) {
         _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, structure))
-        _props.colorTheme!.structure = structure
+        _props.colorTheme.structure = structure
 
         return Task.create('Creating StructureRepresentation', async ctx => {
             if (!_structure) {
@@ -44,7 +44,7 @@ export function ComplexRepresentation<P extends StructureProps>(visualCtor: () =
     function update(props: Partial<P>) {
         return Task.create('Updating StructureRepresentation', async ctx => {
             _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, _structure))
-            _props.colorTheme!.structure = _structure
+            _props.colorTheme.structure = _structure
 
             if (!await visual.update(ctx, _props)) {
                 await visual.create(ctx, _structure, _props)

+ 4 - 4
src/mol-geo/representation/structure/complex-visual.ts

@@ -7,9 +7,9 @@
 import { Structure } from 'mol-model/structure';
 import { Visual } from '..';
 import { MeshRenderObject } from 'mol-gl/render-object';
-import { Mesh } from '../../shape/mesh';
+import { Mesh } from '../../mesh/mesh';
 import { RuntimeContext } from 'mol-task';
-import { LocationIterator } from './visual/util/location-iterator';
+import { LocationIterator } from '../../util/location-iterator';
 import { createComplexMeshRenderObject, createColors } from './visual/util/common';
 import { StructureProps, DefaultStructureMeshProps, MeshUpdateState } from '.';
 import { deepEqual, ValueCell } from 'mol-util';
@@ -96,7 +96,7 @@ export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMe
         },
         mark(loci: Loci, action: MarkerAction) {
             const { tMarker } = renderObject.values
-            const { elementCount, instanceCount } = locationIt
+            const { groupCount, instanceCount } = locationIt
 
             function apply(interval: Interval) {
                 const start = Interval.start(interval)
@@ -106,7 +106,7 @@ export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMe
 
             let changed = false
             if (isEveryLoci(loci)) {
-                apply(Interval.ofBounds(0, elementCount * instanceCount))
+                apply(Interval.ofBounds(0, groupCount * instanceCount))
                 changed = true
             } else {
                 changed = mark(loci, currentStructure, apply)

+ 4 - 4
src/mol-geo/representation/structure/units-visual.ts

@@ -9,8 +9,8 @@ import { RepresentationProps, Visual } from '..';
 import { DefaultStructureMeshProps, MeshUpdateState } from '.';
 import { RuntimeContext } from 'mol-task';
 import { PickingId } from '../../util/picking';
-import { LocationIterator } from './visual/util/location-iterator';
-import { Mesh } from '../../shape/mesh';
+import { LocationIterator } from '../../util/location-iterator';
+import { Mesh } from '../../mesh/mesh';
 import { MarkerAction, applyMarkerAction } from '../../util/marker-data';
 import { Loci, isEveryLoci } from 'mol-model/loci';
 import { MeshRenderObject } from 'mol-gl/render-object';
@@ -101,7 +101,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
         },
         mark(loci: Loci, action: MarkerAction) {
             const { tMarker } = renderObject.values
-            const { elementCount, instanceCount } = locationIt
+            const { groupCount, instanceCount } = locationIt
 
             function apply(interval: Interval) {
                 const start = Interval.start(interval)
@@ -111,7 +111,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
 
             let changed = false
             if (isEveryLoci(loci)) {
-                apply(Interval.ofBounds(0, elementCount * instanceCount))
+                apply(Interval.ofBounds(0, groupCount * instanceCount))
                 changed = true
             } else {
                 changed = mark(loci, currentGroup, apply)

+ 8 - 8
src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts

@@ -7,12 +7,12 @@
 import { Unit, Structure, Link, StructureElement } from 'mol-model/structure';
 import { DefaultStructureProps, ComplexVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../shape/mesh';
+import { Mesh } from '../../../mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { DefaultMeshProps } from '../../util';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { LocationIterator } from './util/location-iterator';
+import { LocationIterator } from '../../../util/location-iterator';
 import { createLinkCylinderMesh, DefaultLinkCylinderProps, LinkCylinderProps } from './util/link';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { ComplexMeshVisual } from '../complex-visual';
@@ -85,11 +85,11 @@ export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkProps> {
 
 function CarbohydrateLinkIterator(structure: Structure): LocationIterator {
     const { elements, links } = structure.carbohydrates
-    const elementCount = links.length
+    const groupCount = links.length
     const instanceCount = 1
     const location = Link.Location()
-    const getLocation = (elementIndex: number, instanceIndex: number) => {
-        const link = links[elementIndex]
+    const getLocation = (groupIndex: number) => {
+        const link = links[groupIndex]
         const carbA = elements[link.carbohydrateIndexA]
         const carbB = elements[link.carbohydrateIndexB]
         const indexA = OrderedSet.findPredecessorIndex(carbA.unit.elements, carbA.anomericCarbon)
@@ -100,14 +100,14 @@ function CarbohydrateLinkIterator(structure: Structure): LocationIterator {
         location.bIndex = indexB as StructureElement.UnitIndex
         return location
     }
-    return LocationIterator(elementCount, instanceCount, getLocation)
+    return LocationIterator(groupCount, instanceCount, getLocation)
 }
 
 function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
-    const { objectId, elementId } = pickingId
+    const { objectId, groupId } = pickingId
     if (id === objectId) {
         const { links, elements } = structure.carbohydrates
-        const l = links[elementId]
+        const l = links[groupId]
         const carbA = elements[l.carbohydrateIndexA]
         const carbB = elements[l.carbohydrateIndexB]
         const indexA = OrderedSet.findPredecessorIndex(carbA.unit.elements, carbA.anomericCarbon)

+ 13 - 13
src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts

@@ -7,13 +7,13 @@
 import { Unit, Structure, StructureElement } from 'mol-model/structure';
 import { ComplexVisual } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../shape/mesh';
+import { Mesh } from '../../../mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Loci, EmptyLoci } from 'mol-model/loci';
-import { MeshBuilder } from '../../../shape/mesh-builder';
+import { MeshBuilder } from '../../../mesh/mesh-builder';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants';
-import { LocationIterator } from './util/location-iterator';
+import { LocationIterator } from '../../../util/location-iterator';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
@@ -49,7 +49,7 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru
         Mat4.targetTo(t, center, pd, normal)
         Mat4.setTranslation(t, center)
 
-        builder.setId(i * 2)
+        builder.setGroup(i * 2)
 
         switch (shapeType) {
             case SaccharideShapes.FilledSphere:
@@ -63,7 +63,7 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru
                 Mat4.scaleUniformly(t, t, side)
                 builder.addPerforatedBox(t)
                 Mat4.mul(t, t, Mat4.rotZ90X180)
-                builder.setId(i * 2 + 1)
+                builder.setGroup(i * 2 + 1)
                 builder.addPerforatedBox(t)
                 break;
             case SaccharideShapes.FilledCone:
@@ -74,7 +74,7 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru
                 Mat4.scaleUniformly(t, t, side * 1.2)
                 builder.addPerforatedOctagonalPyramid(t)
                 Mat4.mul(t, t, Mat4.rotZ90)
-                builder.setId(i * 2 + 1)
+                builder.setGroup(i * 2 + 1)
                 builder.addPerforatedOctagonalPyramid(t)
                 break
             case SaccharideShapes.FlatBox:
@@ -96,7 +96,7 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru
                 Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4))
                 builder.addPerforatedOctahedron(t)
                 Mat4.mul(t, t, Mat4.rotY90)
-                builder.setId(i * 2 + 1)
+                builder.setGroup(i * 2 + 1)
                 builder.addPerforatedOctahedron(t)
                 break
             case SaccharideShapes.FlatDiamond:
@@ -142,11 +142,11 @@ export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolProp
 
 function CarbohydrateElementIterator(structure: Structure): LocationIterator {
     const carbElements = structure.carbohydrates.elements
-    const elementCount = carbElements.length * 2
+    const groupCount = carbElements.length * 2
     const instanceCount = 1
     const location = StructureElement.create()
-    function getLocation (elementIndex: number, instanceIndex: number) {
-        const carb = carbElements[Math.floor(elementIndex / 2)]
+    function getLocation (groupIndex: number, instanceIndex: number) {
+        const carb = carbElements[Math.floor(groupIndex / 2)]
         location.unit = carb.unit
         location.element = carb.anomericCarbon
         return location
@@ -154,13 +154,13 @@ function CarbohydrateElementIterator(structure: Structure): LocationIterator {
     function isSecondary (elementIndex: number, instanceIndex: number) {
         return (elementIndex % 2) === 1
     }
-    return LocationIterator(elementCount, instanceCount, getLocation, isSecondary)
+    return LocationIterator(groupCount, instanceCount, getLocation, isSecondary)
 }
 
 function getCarbohydrateLoci(pickingId: PickingId, structure: Structure, id: number) {
-    const { objectId, elementId } = pickingId
+    const { objectId, groupId } = pickingId
     if (id === objectId) {
-        const carb = structure.carbohydrates.elements[Math.floor(elementId / 2)]
+        const carb = structure.carbohydrates.elements[Math.floor(groupId / 2)]
         const { unit } = carb
         const index = OrderedSet.findPredecessorIndex(unit.elements, carb.anomericCarbon)
         const indices = OrderedSet.ofSingleton(index as StructureElement.UnitIndex)

+ 8 - 8
src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts

@@ -8,12 +8,12 @@ import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { ComplexVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link';
-import { Mesh } from '../../../shape/mesh';
+import { Mesh } from '../../../mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual';
-import { LocationIterator } from './util/location-iterator';
+import { LocationIterator } from '../../../util/location-iterator';
 import { Interval } from 'mol-data/int';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { BitFlags } from 'mol-util';
@@ -73,24 +73,24 @@ export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintProp
 
 function CrossLinkRestraintIterator(structure: Structure): LocationIterator {
     const { pairs } = structure.crossLinkRestraints
-    const elementCount = pairs.length
+    const groupCount = pairs.length
     const instanceCount = 1
     const location = Link.Location()
-    const getLocation = (elementIndex: number) => {
-        const pair = pairs[elementIndex]
+    const getLocation = (groupIndex: number) => {
+        const pair = pairs[groupIndex]
         location.aUnit = pair.unitA
         location.aIndex = pair.indexA
         location.bUnit = pair.unitB
         location.bIndex = pair.indexB
         return location
     }
-    return LocationIterator(elementCount, instanceCount, getLocation)
+    return LocationIterator(groupCount, instanceCount, getLocation)
 }
 
 function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
-    const { objectId, elementId } = pickingId
+    const { objectId, groupId } = pickingId
     if (id === objectId) {
-        const pair = structure.crossLinkRestraints.pairs[elementId]
+        const pair = structure.crossLinkRestraints.pairs[groupId]
         if (pair) {
             return Link.Loci([ Link.Location(pair.unitA, pair.indexA, pair.unitB, pair.indexB) ])
         }

+ 4 - 5
src/mol-geo/representation/structure/visual/element-point.ts

@@ -11,7 +11,7 @@ import { Unit } from 'mol-model/structure';
 import { RuntimeContext } from 'mol-task'
 
 import { UnitsVisual, DefaultStructureProps } from '..';
-import { getElementLoci } from './util/element';
+import { getElementLoci, StructureElementIterator } from './util/element';
 import { createTransforms, createColors, createSizes } from './util/common';
 import { deepEqual, defaults } from 'mol-util';
 import { SortedArray } from 'mol-data/int';
@@ -21,7 +21,6 @@ import { Loci } from 'mol-model/loci';
 import { MarkerAction, createMarkers } from '../../../util/marker-data';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { fillSerial } from 'mol-util/array';
-import { StructureElementIterator } from './util/location-iterator';
 import { SizeThemeProps } from 'mol-view/theme/size';
 
 export const DefaultPointProps = {
@@ -79,16 +78,16 @@ export default function PointVisual(): UnitsVisual<PointProps> {
 
             const values: PointValues = {
                 aPosition: ValueCell.create(vertices),
-                aElementId: ValueCell.create(fillSerial(new Float32Array(elementCount))),
+                aGroup: ValueCell.create(fillSerial(new Float32Array(elementCount))),
                 aTransform: transforms,
-                aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
+                aInstance: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
                 ...color,
                 ...marker,
                 ...size,
 
                 uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
                 uInstanceCount: ValueCell.create(instanceCount),
-                uElementCount: ValueCell.create(group.elements.length),
+                uGroupCount: ValueCell.create(group.elements.length),
 
                 drawCount: ValueCell.create(vertices.length / 3),
                 instanceCount: ValueCell.create(instanceCount),

+ 1 - 2
src/mol-geo/representation/structure/visual/element-sphere.ts

@@ -6,8 +6,7 @@
  */
 
 import { UnitsVisual, MeshUpdateState } from '..';
-import { createElementSphereMesh, markElement, getElementLoci } from './util/element';
-import { StructureElementIterator } from './util/location-iterator';
+import { createElementSphereMesh, markElement, getElementLoci, StructureElementIterator } from './util/element';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 
 export const DefaultElementSphereProps = {

+ 4 - 5
src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts

@@ -7,12 +7,11 @@
 import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { ComplexVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link';
-import { Mesh } from '../../../shape/mesh';
+import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh, LinkIterator } from './util/link';
+import { Mesh } from '../../../mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
-import { LinkIterator } from './util/location-iterator';
 import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual';
 import { Interval } from 'mol-data/int';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
@@ -70,9 +69,9 @@ export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkProps> {
 }
 
 function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
-    const { objectId, elementId } = pickingId
+    const { objectId, groupId } = pickingId
     if (id === objectId) {
-        const bond = structure.links.bonds[elementId]
+        const bond = structure.links.bonds[groupId]
         return Link.Loci([
             Link.Location(
                 bond.unitA, bond.indexA as StructureElement.UnitIndex,

+ 5 - 6
src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts

@@ -8,12 +8,11 @@
 import { Unit, Link, StructureElement } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
-import { DefaultLinkCylinderProps, LinkCylinderProps, createLinkCylinderMesh } from './util/link';
-import { Mesh } from '../../../shape/mesh';
+import { DefaultLinkCylinderProps, LinkCylinderProps, createLinkCylinderMesh, LinkIterator } from './util/link';
+import { Mesh } from '../../../mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
-import { LinkIterator } from './util/location-iterator';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 import { Interval } from 'mol-data/int';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
@@ -82,13 +81,13 @@ export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> {
 }
 
 function getLinkLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number) {
-    const { objectId, instanceId, elementId } = pickingId
+    const { objectId, instanceId, groupId } = pickingId
     const unit = group.units[instanceId]
     if (id === objectId && Unit.isAtomic(unit)) {
         return Link.Loci([
             Link.Location(
-                unit, unit.links.a[elementId] as StructureElement.UnitIndex,
-                unit, unit.links.b[elementId] as StructureElement.UnitIndex
+                unit, unit.links.a[groupId] as StructureElement.UnitIndex,
+                unit, unit.links.b[groupId] as StructureElement.UnitIndex
             )
         ])
     }

+ 5 - 5
src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts

@@ -7,14 +7,13 @@
 import { Unit } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../shape/mesh';
-import { MeshBuilder } from '../../../shape/mesh-builder';
-import { getElementLoci, markElement } from './util/element';
+import { Mesh } from '../../../mesh/mesh';
+import { MeshBuilder } from '../../../mesh/mesh-builder';
+import { getElementLoci, markElement, StructureElementIterator } from './util/element';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { Segmentation, SortedArray } from 'mol-data/int';
 import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
 import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util';
-import { StructureElementIterator } from './util/location-iterator';
 import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual';
 
 const p1 = Vec3.zero()
@@ -30,6 +29,7 @@ const center = Vec3.zero()
 const t = Mat4.identity()
 const sVec = Vec3.zero()
 
+// TODO define props, should be scalable
 async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, props: {}, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
@@ -88,7 +88,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, props:
                     Vec3.scaleAndAdd(center, p1, v12, height / 2 - 0.2)
                     Mat4.scale(t, t, Vec3.set(sVec, width, depth, height))
                     Mat4.setTranslation(t, center)
-                    builder.setId(SortedArray.findPredecessorIndex(elements, idx6))
+                    builder.setGroup(SortedArray.findPredecessorIndex(elements, idx6))
                     builder.addBox(t)
                     builder.addCylinder(p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
                 }

+ 5 - 6
src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts

@@ -7,12 +7,11 @@
 import { Unit } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../shape/mesh';
-import { MeshBuilder } from '../../../shape/mesh-builder';
+import { Mesh } from '../../../mesh/mesh';
+import { MeshBuilder } from '../../../mesh/mesh-builder';
 import { getPolymerElementCount, PolymerBackboneIterator } from './util/polymer';
-import { getElementLoci, markElement } from './util/element';
+import { getElementLoci, markElement, StructureElementIterator } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { StructureElementIterator } from './util/location-iterator';
 import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { CylinderProps } from '../../../primitive/cylinder';
@@ -47,11 +46,11 @@ async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit
         pos(centerB.element, pB)
 
         cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerA)
-        builder.setId(OrderedSet.findPredecessorIndex(elements, centerA.element))
+        builder.setGroup(OrderedSet.findPredecessorIndex(elements, centerA.element))
         builder.addCylinder(pA, pB, 0.5, cylinderProps)
 
         cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerB)
-        builder.setId(OrderedSet.findPredecessorIndex(elements, centerB.element))
+        builder.setGroup(OrderedSet.findPredecessorIndex(elements, centerB.element))
         builder.addCylinder(pB, pA, 0.5, cylinderProps)
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {

+ 4 - 5
src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts

@@ -7,13 +7,12 @@
 import { Unit } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
-import { markElement, getElementLoci } from './util/element';
-import { Mesh } from '../../../shape/mesh';
-import { MeshBuilder } from '../../../shape/mesh-builder';
+import { markElement, getElementLoci, StructureElementIterator } from './util/element';
+import { Mesh } from '../../../mesh/mesh';
+import { MeshBuilder } from '../../../mesh/mesh-builder';
 import { getPolymerElementCount, PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment } from './util/polymer';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types';
-import { StructureElementIterator } from './util/location-iterator';
 import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { OrderedSet } from 'mol-data/int';
@@ -49,7 +48,7 @@ async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit,
     const polymerTraceIt = PolymerTraceIterator(unit)
     while (polymerTraceIt.hasNext) {
         const v = polymerTraceIt.move()
-        builder.setId(OrderedSet.findPredecessorIndex(unit.elements, v.center.element))
+        builder.setGroup(OrderedSet.findPredecessorIndex(unit.elements, v.center.element))
 
         const isNucleic = v.moleculeType === MoleculeType.DNA || v.moleculeType === MoleculeType.RNA
         const isSheet = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta)

+ 6 - 7
src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts

@@ -7,12 +7,11 @@
 import { Unit, StructureElement } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../shape/mesh';
-import { MeshBuilder } from '../../../shape/mesh-builder';
+import { Mesh } from '../../../mesh/mesh';
+import { MeshBuilder } from '../../../mesh/mesh-builder';
 import { getPolymerGapCount, PolymerGapIterator } from './util/polymer';
-import { getElementLoci, markElement } from './util/element';
+import { getElementLoci, markElement, StructureElementIterator } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { StructureElementIterator } from './util/location-iterator';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { CylinderProps } from '../../../primitive/cylinder';
@@ -48,7 +47,7 @@ async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, pro
     while (polymerGapIt.hasNext) {
         const { centerA, centerB } = polymerGapIt.move()
         if (centerA.element === centerB.element) {
-            builder.setId(centerA.element)
+            builder.setGroup(centerA.element)
             pos(elements[centerA.element], pA)
             builder.addSphere(pA, 0.6, 0)
         } else {
@@ -59,12 +58,12 @@ async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, pro
 
             l.element = elmA
             cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(l)
-            builder.setId(centerA.element)
+            builder.setGroup(centerA.element)
             builder.addFixedCountDashedCylinder(pA, pB, 0.5, segmentCount, cylinderProps)
 
             l.element = elmB
             cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(l)
-            builder.setId(centerB.element)
+            builder.setGroup(centerB.element)
             builder.addFixedCountDashedCylinder(pB, pA, 0.5, segmentCount, cylinderProps)
         }
 

+ 4 - 5
src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts

@@ -7,12 +7,11 @@
 import { Unit } from 'mol-model/structure';
 import { UnitsVisual, MeshUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { markElement, getElementLoci } from './util/element';
-import { Mesh } from '../../../shape/mesh';
-import { MeshBuilder } from '../../../shape/mesh-builder';
+import { markElement, getElementLoci, StructureElementIterator } from './util/element';
+import { Mesh } from '../../../mesh/mesh';
+import { MeshBuilder } from '../../../mesh/mesh-builder';
 import { getPolymerElementCount, PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment } from './util/polymer';
 import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types';
-import { StructureElementIterator } from './util/location-iterator';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { OrderedSet } from 'mol-data/int';
@@ -44,7 +43,7 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, props: Po
     const polymerTraceIt = PolymerTraceIterator(unit)
     while (polymerTraceIt.hasNext) {
         const v = polymerTraceIt.move()
-        builder.setId(OrderedSet.findPredecessorIndex(unit.elements, v.center.element))
+        builder.setGroup(OrderedSet.findPredecessorIndex(unit.elements, v.center.element))
 
         const isNucleic = v.moleculeType === MoleculeType.DNA || v.moleculeType === MoleculeType.RNA
         const isSheet = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta)

+ 11 - 11
src/mol-geo/representation/structure/visual/util/common.ts

@@ -8,11 +8,11 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { Mat4 } from 'mol-math/linear-algebra'
 
-import { createUniformColor, ColorData, createElementColor, createElementInstanceColor, createInstanceColor } from '../../../../util/color-data';
-import { createUniformSize, SizeData, createElementSize, createElementInstanceSize, createInstanceSize } from '../../../../util/size-data';
+import { createUniformColor, ColorData, createGroupColor, createGroupInstanceColor, createInstanceColor } from '../../../../util/color-data';
+import { createUniformSize, SizeData, createGroupSize, createGroupInstanceSize, createInstanceSize } from '../../../../util/size-data';
 import { ValueCell } from 'mol-util';
-import { LocationIterator } from './location-iterator';
-import { Mesh } from '../../../../shape/mesh';
+import { LocationIterator } from '../../../../util/location-iterator';
+import { Mesh } from '../../../../mesh/mesh';
 import { MeshValues } from 'mol-gl/renderable';
 import { getMeshData } from '../../../../util/mesh-data';
 import { MeshProps, createMeshValues, createRenderableState, createIdentityTransform } from '../../../util';
@@ -36,8 +36,8 @@ export function createColors(locationIt: LocationIterator, props: ColorThemeProp
     const colorTheme = ColorTheme(props)
     switch (colorTheme.kind) {
         case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData)
-        case 'element': return createElementColor(locationIt, colorTheme.color, colorData)
-        case 'elementInstance': return createElementInstanceColor(locationIt, colorTheme.color, colorData)
+        case 'group': return createGroupColor(locationIt, colorTheme.color, colorData)
+        case 'groupInstance': return createGroupInstanceColor(locationIt, colorTheme.color, colorData)
         case 'instance': return createInstanceColor(locationIt, colorTheme.color, colorData)
     }
 }
@@ -46,8 +46,8 @@ export function createSizes(locationIt: LocationIterator, props: SizeThemeProps,
     const sizeTheme = SizeTheme(props)
     switch (sizeTheme.kind) {
         case 'uniform': return createUniformSize(locationIt, sizeTheme.size, sizeData)
-        case 'element': return createElementSize(locationIt, sizeTheme.size, sizeData)
-        case 'elementInstance': return createElementInstanceSize(locationIt, sizeTheme.size, sizeData)
+        case 'group': return createGroupSize(locationIt, sizeTheme.size, sizeData)
+        case 'groupInstance': return createGroupInstanceSize(locationIt, sizeTheme.size, sizeData)
         case 'instance': return createInstanceSize(locationIt, sizeTheme.size, sizeData)
     }
 }
@@ -55,11 +55,11 @@ export function createSizes(locationIt: LocationIterator, props: SizeThemeProps,
 type StructureMeshProps = Required<MeshProps & StructureProps>
 
 function _createMeshValues(transforms: ValueCell<Float32Array>, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps): MeshValues {
-    const { instanceCount, elementCount } = locationIt
+    const { instanceCount, groupCount } = locationIt
     const color = createColors(locationIt, props.colorTheme)
-    const marker = createMarkers(instanceCount * elementCount)
+    const marker = createMarkers(instanceCount * groupCount)
 
-    const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
+    const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }
 
     return {
         ...getMeshData(mesh),

+ 20 - 5
src/mol-geo/representation/structure/visual/util/element.ts

@@ -8,12 +8,13 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { Unit, StructureElement } from 'mol-model/structure';
 import { RuntimeContext } from 'mol-task';
 import { sphereVertexCount } from '../../../../primitive/sphere';
-import { Mesh } from '../../../../shape/mesh';
-import { MeshBuilder } from '../../../../shape/mesh-builder';
+import { Mesh } from '../../../../mesh/mesh';
+import { MeshBuilder } from '../../../../mesh/mesh-builder';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { Interval, OrderedSet } from 'mol-data/int';
 import { PickingId } from '../../../../util/picking';
 import { SizeTheme, SizeThemeProps } from 'mol-view/theme/size';
+import { LocationIterator } from '../../../../util/location-iterator';
 
 export interface ElementSphereMeshProps {
     sizeTheme: SizeThemeProps,
@@ -38,7 +39,7 @@ export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, p
         l.element = elements[i]
         pos(elements[i], v)
 
-        meshBuilder.setId(i)
+        meshBuilder.setGroup(i)
         meshBuilder.addSphere(v, sizeTheme.size(l), detail)
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {
@@ -74,11 +75,25 @@ export function markElement(loci: Loci, group: Unit.SymmetryGroup, apply: (inter
 }
 
 export function getElementLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number) {
-    const { objectId, instanceId, elementId } = pickingId
+    const { objectId, instanceId, groupId } = pickingId
     if (id === objectId) {
         const unit = group.units[instanceId]
-        const indices = OrderedSet.ofSingleton(elementId as StructureElement.UnitIndex);
+        const indices = OrderedSet.ofSingleton(groupId as StructureElement.UnitIndex);
         return StructureElement.Loci([{ unit, indices }])
     }
     return EmptyLoci
+}
+
+export namespace StructureElementIterator {
+    export function fromGroup(group: Unit.SymmetryGroup): LocationIterator {
+        const unit = group.units[0]
+        const groupCount = group.elements.length
+        const instanceCount = group.units.length
+        const location = StructureElement.create(unit)
+        const getLocation = (groupIndex: number) => {
+            location.element = unit.elements[groupIndex]
+            return location
+        }
+        return LocationIterator(groupCount, instanceCount, getLocation)
+    }
 }

+ 34 - 3
src/mol-geo/representation/structure/visual/util/link.ts

@@ -6,12 +6,14 @@
 
 import { Vec3 } from 'mol-math/linear-algebra';
 import { RuntimeContext } from 'mol-task';
-import { Mesh } from '../../../../shape/mesh';
-import { MeshBuilder } from '../../../../shape/mesh-builder';
+import { Mesh } from '../../../../mesh/mesh';
+import { MeshBuilder } from '../../../../mesh/mesh-builder';
 import { LinkType } from 'mol-model/structure/model/types';
 import { DefaultMeshProps } from '../../../util';
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { CylinderProps } from '../../../../primitive/cylinder';
+import { LocationIterator } from '../../../../util/location-iterator';
+import { Unit, StructureElement, Structure, Link } from 'mol-model/structure';
 
 export const DefaultLinkCylinderProps = {
     ...DefaultMeshProps,
@@ -87,7 +89,7 @@ export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: L
         const linkRadius = radius(edgeIndex)
         const o = order(edgeIndex)
         const f = flags(edgeIndex)
-        meshBuilder.setId(edgeIndex)
+        meshBuilder.setGroup(edgeIndex)
 
         if (LinkType.is(f, LinkType.Flag.MetallicCoordination)) {
             // show metall coordinations with dashed cylinders
@@ -116,4 +118,33 @@ export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: L
     }
 
     return meshBuilder.getMesh()
+}
+
+export namespace LinkIterator {
+    export function fromGroup(group: Unit.SymmetryGroup): LocationIterator {
+        const unit = group.units[0]
+        const groupCount = Unit.isAtomic(unit) ? unit.links.edgeCount * 2 : 0
+        const instanceCount = group.units.length
+        const location = StructureElement.create(unit)
+        const getLocation = (groupIndex: number) => {
+            location.element = unit.elements[(unit as Unit.Atomic).links.a[groupIndex]]
+            return location
+        }
+        return LocationIterator(groupCount, instanceCount, getLocation)
+    }
+
+    export function fromStructure(structure: Structure): LocationIterator {
+        const groupCount = structure.links.bondCount
+        const instanceCount = 1
+        const location = Link.Location()
+        const getLocation = (groupIndex: number) => {
+            const bond = structure.links.bonds[groupIndex]
+            location.aUnit = bond.unitA
+            location.aIndex = bond.indexA as StructureElement.UnitIndex
+            location.bUnit = bond.unitB
+            location.bIndex = bond.indexB as StructureElement.UnitIndex
+            return location
+        }
+        return LocationIterator(groupCount, instanceCount, getLocation)
+    }
 }

+ 0 - 141
src/mol-geo/representation/structure/visual/util/location-iterator.ts

@@ -1,141 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { Iterator } from 'mol-data';
-import { Unit, StructureElement, Structure, Link } from 'mol-model/structure';
-import { NullLocation, Location } from 'mol-model/location';
-
-export interface LocationValue {
-    location: Location
-    index: number
-    elementIndex: number
-    instanceIndex: number
-    isSecondary: boolean
-}
-
-export const NullLocationValue: LocationValue = {
-    location: NullLocation,
-    index: 0,
-    elementIndex: 0,
-    instanceIndex: 0,
-    isSecondary: false
-}
-
-export interface LocationIterator extends Iterator<LocationValue> {
-    readonly hasNext: boolean
-    readonly isNextNewInstance: boolean
-    readonly elementCount: number
-    readonly instanceCount: number
-    move(): LocationValue
-    reset(): void
-    skipInstance(): void
-}
-
-type LocationGetter = (elementIndex: number, instanceIndex: number) => Location
-type IsSecondaryGetter = (elementIndex: number, instanceIndex: number) => boolean
-
-export function LocationIterator(elementCount: number, instanceCount: number, getLocation: LocationGetter, isSecondary: IsSecondaryGetter = () => false): LocationIterator {
-    const value: LocationValue = {
-        location: NullLocation as Location,
-        index: 0,
-        elementIndex: 0,
-        instanceIndex: 0,
-        isSecondary: false
-    }
-
-    let hasNext = value.elementIndex < elementCount
-    let isNextNewInstance = false
-    let elementIndex = 0
-    let instanceIndex = 0
-
-    return {
-        get hasNext () { return hasNext },
-        get isNextNewInstance () { return isNextNewInstance },
-        get elementCount () { return elementCount },
-        get instanceCount () { return instanceCount },
-        move() {
-            if (hasNext) {
-                value.elementIndex = elementIndex
-                value.instanceIndex = instanceIndex
-                value.index = instanceIndex * elementCount + elementIndex
-                value.location = getLocation(elementIndex, instanceIndex)
-                value.isSecondary = isSecondary(elementIndex, instanceIndex)
-                ++elementIndex
-                if (elementIndex === elementCount) {
-                    ++instanceIndex
-                    isNextNewInstance = true
-                    if (instanceIndex < instanceCount) elementIndex = 0
-                } else {
-                    isNextNewInstance = false
-                }
-                hasNext = elementIndex < elementCount
-            }
-            return value
-        },
-        reset() {
-            value.location = NullLocation
-            value.index = 0
-            value.elementIndex = 0
-            value.instanceIndex = 0
-            value.isSecondary = false
-
-            hasNext = value.elementIndex < elementCount
-            isNextNewInstance = false
-            elementIndex = 0
-            instanceIndex = 0
-        },
-        skipInstance() {
-            if (hasNext && value.instanceIndex === instanceIndex) {
-                ++instanceIndex
-                elementIndex = 0
-                hasNext = instanceIndex < instanceCount
-            }
-        }
-    }
-}
-
-export namespace StructureElementIterator {
-    export function fromGroup(group: Unit.SymmetryGroup): LocationIterator {
-        const unit = group.units[0]
-        const elementCount = group.elements.length
-        const instanceCount = group.units.length
-        const location = StructureElement.create(unit)
-        const getLocation = (elementIndex: number) => {
-            location.element = unit.elements[elementIndex]
-            return location
-        }
-        return LocationIterator(elementCount, instanceCount, getLocation)
-    }
-}
-
-export namespace LinkIterator {
-    export function fromGroup(group: Unit.SymmetryGroup): LocationIterator {
-        const unit = group.units[0]
-        const elementCount = Unit.isAtomic(unit) ? unit.links.edgeCount * 2 : 0
-        const instanceCount = group.units.length
-        const location = StructureElement.create(unit)
-        const getLocation = (elementIndex: number) => {
-            location.element = unit.elements[(unit as Unit.Atomic).links.a[elementIndex]]
-            return location
-        }
-        return LocationIterator(elementCount, instanceCount, getLocation)
-    }
-
-    export function fromStructure(structure: Structure): LocationIterator {
-        const elementCount = structure.links.bondCount
-        const instanceCount = 1
-        const location = Link.Location()
-        const getLocation = (elementIndex: number) => {
-            const bond = structure.links.bonds[elementIndex]
-            location.aUnit = bond.unitA
-            location.aIndex = bond.indexA as StructureElement.UnitIndex
-            location.bUnit = bond.unitB
-            location.bIndex = bond.indexB as StructureElement.UnitIndex
-            return location
-        }
-        return LocationIterator(elementCount, instanceCount, getLocation)
-    }
-}

+ 3 - 3
src/mol-geo/representation/util.ts

@@ -35,14 +35,14 @@ export function createIdentityTransform(transforms?: ValueCell<Float32Array>) {
     return transforms ? ValueCell.update(transforms, identityTransform) : ValueCell.create(identityTransform)
 }
 
-type Counts = { drawCount: number, elementCount: number, instanceCount: number }
+type Counts = { drawCount: number, groupCount: number, instanceCount: number }
 
 export function createBaseValues(props: Required<BaseProps>, counts: Counts) {
     return {
         uAlpha: ValueCell.create(props.alpha),
         uInstanceCount: ValueCell.create(counts.instanceCount),
-        uElementCount: ValueCell.create(counts.elementCount),
-        aInstanceId: ValueCell.create(fillSerial(new Float32Array(counts.instanceCount))),
+        uGroupCount: ValueCell.create(counts.groupCount),
+        aInstance: ValueCell.create(fillSerial(new Float32Array(counts.instanceCount))),
         drawCount: ValueCell.create(counts.drawCount),
         instanceCount: ValueCell.create(counts.instanceCount),
     }

+ 3 - 3
src/mol-geo/representation/volume/surface.ts

@@ -8,7 +8,7 @@
 import { VolumeData, VolumeIsoValue } from 'mol-model/volume'
 import { Task, RuntimeContext } from 'mol-task'
 import { computeMarchingCubes } from '../../util/marching-cubes/algorithm';
-import { Mesh } from '../../shape/mesh';
+import { Mesh } from '../../mesh/mesh';
 import { VolumeVisual } from '.';
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
 import { ValueCell, defaults } from 'mol-util';
@@ -72,13 +72,13 @@ export default function SurfaceVisual(): VolumeVisual<SurfaceProps> {
             const values: MeshValues = {
                 ...getMeshData(mesh),
                 aTransform: ValueCell.create(new Float32Array(Mat4.identity())),
-                aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
+                aInstance: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
                 ...color,
                 ...marker,
 
                 uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
                 uInstanceCount: ValueCell.create(instanceCount),
-                uElementCount: ValueCell.create(mesh.triangleCount),
+                uGroupCount: ValueCell.create(mesh.triangleCount),
 
                 elements: mesh.indexBuffer,
 

+ 0 - 53
src/mol-geo/shape/vertex-map.ts

@@ -1,53 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { ChunkedArray } from 'mol-data/util';
-import { Mesh } from './mesh';
-
-/** Mapping between vertices and ids */
-interface VertexMap {
-    idCount: number,
-    offsetCount: number,
-    ids: Helpers.NumberArray
-    offsets: Uint32Array,
-}
-
-function createOffsets(idCount: number, ids: Helpers.NumberArray) {
-    const offsets = ChunkedArray.create(Uint32Array, 1, 1024, 2048);
-    let prevId = ids[0]
-    ChunkedArray.add(offsets, 0)
-    for (let i = 1; i < idCount; ++i) {
-        if (prevId !== ids[i]) {
-            prevId = ids[i]
-            ChunkedArray.add(offsets, i)
-        }
-    }
-    ChunkedArray.add(offsets, idCount)
-    return ChunkedArray.compact(offsets, false) as Uint32Array
-}
-
-namespace VertexMap {
-    export function create(idCount: number, offsetCount: number, ids: Helpers.NumberArray, offsets: Uint32Array): VertexMap {
-        return {
-            idCount,
-            offsetCount,
-            ids,
-            offsets
-        }
-    }
-
-    export function fromMesh(mesh: Mesh) {
-        const ids = mesh.idBuffer.ref.value
-        const offsets = createOffsets(mesh.vertexCount, ids)
-        return create(mesh.vertexCount, offsets.length, ids, offsets)
-    }
-
-    export function rangeFromId (id: number, vertexMap: VertexMap) {
-        return [0, 0]
-    }
-}
-
-export default VertexMap

+ 18 - 18
src/mol-geo/util/color-data.ts

@@ -8,16 +8,16 @@ import { ValueCell } from 'mol-util';
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 import { Color } from 'mol-util/color';
 import { Vec2, Vec3 } from 'mol-math/linear-algebra';
-import { LocationIterator } from '../representation/structure/visual/util/location-iterator';
+import { LocationIterator } from './location-iterator';
 import { Location, NullLocation } from 'mol-model/location';
 
-export type ColorType = 'uniform' | 'instance' | 'element' | 'elementInstance'
+export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance'
 
 export type ColorData = {
     uColor: ValueCell<Vec3>,
     aColor: ValueCell<Float32Array>,
     tColor: ValueCell<TextureImage>,
-    uColorTexSize: ValueCell<Vec2>,
+    uColorTexDim: ValueCell<Vec2>,
     dColorType: ValueCell<string>,
 }
 
@@ -27,7 +27,7 @@ const emptyColorTexture = { array: new Uint8Array(3), width: 1, height: 1 }
 function createEmptyColorTexture() {
     return {
         tColor: ValueCell.create(emptyColorTexture),
-        uColorTexSize: ValueCell.create(Vec2.create(1, 1))
+        uColorTexDim: ValueCell.create(Vec2.create(1, 1))
     }
 }
 
@@ -56,7 +56,7 @@ export function createUniformColor(locationIt: LocationIterator, colorFn: Locati
 export function createTextureColor(colors: TextureImage, type: ColorType, colorData?: ColorData): ColorData {
     if (colorData) {
         ValueCell.update(colorData.tColor, colors)
-        ValueCell.update(colorData.uColorTexSize, Vec2.create(colors.width, colors.height))
+        ValueCell.update(colorData.uColorTexDim, Vec2.create(colors.width, colors.height))
         if (colorData.dColorType.ref.value !== type) {
             ValueCell.update(colorData.dColorType, type)
         }
@@ -66,7 +66,7 @@ export function createTextureColor(colors: TextureImage, type: ColorType, colorD
             uColor: ValueCell.create(Vec3.zero()),
             aColor: ValueCell.create(new Float32Array(0)),
             tColor: ValueCell.create(colors),
-            uColorTexSize: ValueCell.create(Vec2.create(colors.width, colors.height)),
+            uColorTexDim: ValueCell.create(Vec2.create(colors.width, colors.height)),
             dColorType: ValueCell.create(type),
         }
     }
@@ -84,25 +84,25 @@ export function createInstanceColor(locationIt: LocationIterator, colorFn: Locat
     return createTextureColor(colors, 'instance', colorData)
 }
 
-/** Creates color texture with color for each element (i.e. shared across instances/units) */
-export function createElementColor(locationIt: LocationIterator, colorFn: LocationColor, colorData?: ColorData): ColorData {
-    const { elementCount } = locationIt
-    const colors = colorData && colorData.tColor.ref.value.array.length >= elementCount * 3 ? colorData.tColor.ref.value : createTextureImage(elementCount, 3)
+/** Creates color texture with color for each group (i.e. shared across instances/units) */
+export function createGroupColor(locationIt: LocationIterator, colorFn: LocationColor, colorData?: ColorData): ColorData {
+    const { groupCount } = locationIt
+    const colors = colorData && colorData.tColor.ref.value.array.length >= groupCount * 3 ? colorData.tColor.ref.value : createTextureImage(groupCount, 3)
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
-        const { location, isSecondary, elementIndex } = locationIt.move()
-        Color.toArray(colorFn(location, isSecondary), colors.array, elementIndex * 3)
+        const { location, isSecondary, groupIndex } = locationIt.move()
+        Color.toArray(colorFn(location, isSecondary), colors.array, groupIndex * 3)
     }
-    return createTextureColor(colors, 'element', colorData)
+    return createTextureColor(colors, 'group', colorData)
 }
 
-/** Creates color texture with color for each element instance (i.e. for each unit) */
-export function createElementInstanceColor(locationIt: LocationIterator, colorFn: LocationColor, colorData?: ColorData): ColorData {
-    const { elementCount, instanceCount } = locationIt
-    const count = instanceCount * elementCount
+/** Creates color texture with color for each group in each instance (i.e. for each unit) */
+export function createGroupInstanceColor(locationIt: LocationIterator, colorFn: LocationColor, colorData?: ColorData): ColorData {
+    const { groupCount, instanceCount } = locationIt
+    const count = instanceCount * groupCount
     const colors = colorData && colorData.tColor.ref.value.array.length >= count * 3 ? colorData.tColor.ref.value : createTextureImage(count, 3)
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
         const { location, isSecondary, index } = locationIt.move()
         Color.toArray(colorFn(location, isSecondary), colors.array, index * 3)
     }
-    return createTextureColor(colors, 'elementInstance', colorData)
+    return createTextureColor(colors, 'groupInstance', colorData)
 }

+ 97 - 0
src/mol-geo/util/location-iterator.ts

@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Iterator } from 'mol-data';
+import { NullLocation, Location } from 'mol-model/location';
+
+export interface LocationValue {
+    location: Location
+    index: number
+    groupIndex: number
+    instanceIndex: number
+    isSecondary: boolean
+}
+
+export const NullLocationValue: LocationValue = {
+    location: NullLocation,
+    index: 0,
+    groupIndex: 0,
+    instanceIndex: 0,
+    isSecondary: false
+}
+
+export interface LocationIterator extends Iterator<LocationValue> {
+    readonly hasNext: boolean
+    readonly isNextNewInstance: boolean
+    readonly groupCount: number
+    readonly instanceCount: number
+    move(): LocationValue
+    reset(): void
+    skipInstance(): void
+}
+
+type LocationGetter = (groupIndex: number, instanceIndex: number) => Location
+type IsSecondaryGetter = (groupIndex: number, instanceIndex: number) => boolean
+
+export function LocationIterator(groupCount: number, instanceCount: number, getLocation: LocationGetter, isSecondary: IsSecondaryGetter = () => false): LocationIterator {
+    const value: LocationValue = {
+        location: NullLocation as Location,
+        index: 0,
+        groupIndex: 0,
+        instanceIndex: 0,
+        isSecondary: false
+    }
+
+    let hasNext = value.groupIndex < groupCount
+    let isNextNewInstance = false
+    let groupIndex = 0
+    let instanceIndex = 0
+
+    return {
+        get hasNext () { return hasNext },
+        get isNextNewInstance () { return isNextNewInstance },
+        get groupCount () { return groupCount },
+        get instanceCount () { return instanceCount },
+        move() {
+            if (hasNext) {
+                value.groupIndex = groupIndex
+                value.instanceIndex = instanceIndex
+                value.index = instanceIndex * groupCount + groupIndex
+                value.location = getLocation(groupIndex, instanceIndex)
+                value.isSecondary = isSecondary(groupIndex, instanceIndex)
+                ++groupIndex
+                if (groupIndex === groupCount) {
+                    ++instanceIndex
+                    isNextNewInstance = true
+                    if (instanceIndex < instanceCount) groupIndex = 0
+                } else {
+                    isNextNewInstance = false
+                }
+                hasNext = groupIndex < groupCount
+            }
+            return value
+        },
+        reset() {
+            value.location = NullLocation
+            value.index = 0
+            value.groupIndex = 0
+            value.instanceIndex = 0
+            value.isSecondary = false
+
+            hasNext = value.groupIndex < groupCount
+            isNextNewInstance = false
+            groupIndex = 0
+            instanceIndex = 0
+        },
+        skipInstance() {
+            if (hasNext && value.instanceIndex === instanceIndex) {
+                ++instanceIndex
+                groupIndex = 0
+                hasNext = instanceIndex < instanceCount
+            }
+        }
+    }
+}

+ 5 - 8
src/mol-geo/util/marching-cubes/algorithm.ts

@@ -7,7 +7,7 @@
 import { Task, RuntimeContext } from 'mol-task'
 import { ChunkedArray } from 'mol-data/util'
 import { Tensor } from 'mol-math/linear-algebra'
-import { Mesh } from '../../shape/mesh'
+import { Mesh } from '../../mesh/mesh'
 import { Index, EdgeIdInfo, CubeEdges, EdgeTable, TriTable } from './tables'
 import { ValueCell } from 'mol-util'
 
@@ -76,18 +76,15 @@ class MarchingCubesComputation {
         const ret: Mesh = {
             vertexCount:  this.state.vertexCount,
             triangleCount: this.state.triangleCount,
-            offsetCount: 0,
             vertexBuffer: os ? ValueCell.update(os.vertexBuffer, vb) : ValueCell.create(vb),
             indexBuffer: os ? ValueCell.update(os.indexBuffer, ib) : ValueCell.create(ib),
             normalBuffer: os ? os.normalBuffer : ValueCell.create(new Float32Array(0)),
-            idBuffer: this.state.assignIds
-                ? os && os.idBuffer
-                    ? ValueCell.update(os.idBuffer, ChunkedArray.compact(this.state.idBuffer) as Float32Array)
+            groupBuffer: this.state.assignIds
+                ? os && os.groupBuffer
+                    ? ValueCell.update(os.groupBuffer, ChunkedArray.compact(this.state.idBuffer) as Float32Array)
                     : ValueCell.create(ChunkedArray.compact(this.state.idBuffer) as Float32Array)
                 : ValueCell.create(new Float32Array(0)),
-            offsetBuffer: os ? os.offsetBuffer : ValueCell.create(new Uint32Array(0)),
-            normalsComputed: false,
-            offsetsComputed: false
+            normalsComputed: false
         }
 
         return ret;

+ 6 - 6
src/mol-geo/util/marker-data.ts

@@ -10,7 +10,7 @@ import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 
 export type MarkerData = {
     tMarker: ValueCell<TextureImage>
-    uMarkerTexSize: ValueCell<Vec2>
+    uMarkerTexDim: ValueCell<Vec2>
 }
 
 export enum MarkerAction {
@@ -77,26 +77,26 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
         : createTextureImage(count, 1)
     if (markerData) {
         ValueCell.update(markerData.tMarker, markers)
-        ValueCell.update(markerData.uMarkerTexSize, Vec2.create(markers.width, markers.height))
+        ValueCell.update(markerData.uMarkerTexDim, Vec2.create(markers.width, markers.height))
         return markerData
     } else {
         return {
             tMarker: ValueCell.create(markers),
-            uMarkerTexSize: ValueCell.create(Vec2.create(markers.width, markers.height)),
+            uMarkerTexDim: ValueCell.create(Vec2.create(markers.width, markers.height)),
         }
     }
 }
 
 const emptyMarkerTexture = { array: new Uint8Array(1), width: 1, height: 1 }
-export function createEmptyMarkers(markerData?: MarkerData) {
+export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
     if (markerData) {
         ValueCell.update(markerData.tMarker, emptyMarkerTexture)
-        ValueCell.update(markerData.uMarkerTexSize, Vec2.create(1, 1))
+        ValueCell.update(markerData.uMarkerTexDim, Vec2.create(1, 1))
         return markerData
     } else {
         return {
             tMarker: ValueCell.create(emptyMarkerTexture),
-            uMarkerTexSize: ValueCell.create(Vec2.create(1, 1)),
+            uMarkerTexDim: ValueCell.create(Vec2.create(1, 1)),
         }
     }
 }

+ 3 - 3
src/mol-geo/util/mesh-data.ts

@@ -5,18 +5,18 @@
  */
 
 import { ValueCell } from 'mol-util/value-cell'
-import { Mesh } from '../shape/mesh';
+import { Mesh } from '../mesh/mesh';
 
 type MeshData = {
     aPosition: ValueCell<Float32Array>,
     aNormal: ValueCell<Float32Array>,
-    aElementId: ValueCell<Float32Array>,
+    aGroup: ValueCell<Float32Array>,
 }
 
 export function getMeshData(mesh: Mesh): MeshData {
     return {
         aPosition: mesh.vertexBuffer,
         aNormal: mesh.normalBuffer,
-        aElementId: mesh.idBuffer,
+        aGroup: mesh.groupBuffer,
     }
 }

+ 1 - 1
src/mol-geo/util/picking.ts

@@ -18,7 +18,7 @@ export function decodeIdRGBA(r: number, g: number, b: number) {
 export interface PickingId {
     objectId: number
     instanceId: number
-    elementId: number
+    groupId: number
 }
 
 export interface PickingInfo {

+ 17 - 17
src/mol-geo/util/size-data.ts

@@ -7,16 +7,16 @@
 import { ValueCell } from 'mol-util';
 import { Vec2 } from 'mol-math/linear-algebra';
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
-import { LocationIterator } from '../representation/structure/visual/util/location-iterator';
+import { LocationIterator } from './location-iterator';
 import { Location, NullLocation } from 'mol-model/location';
 
-export type SizeType = 'uniform' | 'instance' | 'element' | 'elementInstance'
+export type SizeType = 'uniform' | 'instance' | 'group' | 'groupInstance'
 
 export type SizeData = {
     uSize: ValueCell<number>,
     aSize: ValueCell<Float32Array>,
     tSize: ValueCell<TextureImage>,
-    uSizeTexSize: ValueCell<Vec2>,
+    uSizeTexDim: ValueCell<Vec2>,
     dSizeType: ValueCell<string>,
 }
 
@@ -26,7 +26,7 @@ const emptySizeTexture = { array: new Uint8Array(1), width: 1, height: 1 }
 function createEmptySizeTexture() {
     return {
         tSize: ValueCell.create(emptySizeTexture),
-        uSizeTexSize: ValueCell.create(Vec2.create(1, 1))
+        uSizeTexDim: ValueCell.create(Vec2.create(1, 1))
     }
 }
 
@@ -55,7 +55,7 @@ export function createUniformSize(locationIt: LocationIterator, sizeFn: Location
 export function createTextureSize(sizes: TextureImage, type: SizeType, sizeData?: SizeData): SizeData {
     if (sizeData) {
         ValueCell.update(sizeData.tSize, sizes)
-        ValueCell.update(sizeData.uSizeTexSize, Vec2.create(sizes.width, sizes.height))
+        ValueCell.update(sizeData.uSizeTexDim, Vec2.create(sizes.width, sizes.height))
         if (sizeData.dSizeType.ref.value !== type) {
             ValueCell.update(sizeData.dSizeType, type)
         }
@@ -65,7 +65,7 @@ export function createTextureSize(sizes: TextureImage, type: SizeType, sizeData?
             uSize: ValueCell.create(0),
             aSize: ValueCell.create(new Float32Array(0)),
             tSize: ValueCell.create(sizes),
-            uSizeTexSize: ValueCell.create(Vec2.create(sizes.width, sizes.height)),
+            uSizeTexDim: ValueCell.create(Vec2.create(sizes.width, sizes.height)),
             dSizeType: ValueCell.create(type),
         }
     }
@@ -83,25 +83,25 @@ export function createInstanceSize(locationIt: LocationIterator, sizeFn: Locatio
     return createTextureSize(sizes, 'instance', sizeData)
 }
 
-/** Creates size texture with size for each element (i.e. shared across instances/units) */
-export function createElementSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
-    const { elementCount } = locationIt
-    const sizes = sizeData && sizeData.tSize.ref.value.array.length >= elementCount ? sizeData.tSize.ref.value : createTextureImage(elementCount, 1)
+/** Creates size texture with size for each group (i.e. shared across instances/units) */
+export function createGroupSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
+    const { groupCount } = locationIt
+    const sizes = sizeData && sizeData.tSize.ref.value.array.length >= groupCount ? sizeData.tSize.ref.value : createTextureImage(groupCount, 1)
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
         const v = locationIt.move()
-        sizes.array[v.elementIndex] = sizeFn(v.location)
+        sizes.array[v.groupIndex] = sizeFn(v.location)
     }
-    return createTextureSize(sizes, 'element', sizeData)
+    return createTextureSize(sizes, 'group', sizeData)
 }
 
-/** Creates size texture with size for each element instance (i.e. for each unit) */
-export function createElementInstanceSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
-    const { elementCount, instanceCount } = locationIt
-    const count = instanceCount * elementCount
+/** Creates size texture with size for each group in each instance (i.e. for each unit) */
+export function createGroupInstanceSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
+    const { groupCount, instanceCount } = locationIt
+    const count = instanceCount * groupCount
     const sizes = sizeData && sizeData.tSize.ref.value.array.length >= count ? sizeData.tSize.ref.value : createTextureImage(count, 1)
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
         const v = locationIt.move()
         sizes.array[v.index] = sizeFn(v.location)
     }
-    return createTextureSize(sizes, 'elementInstance', sizeData)
+    return createTextureSize(sizes, 'groupInstance', sizeData)
 }

+ 5 - 5
src/mol-gl/_spec/renderer.spec.ts

@@ -46,8 +46,8 @@ function createRenderer(gl: WebGLRenderingContext) {
 
 function createPoints() {
     const aPosition = ValueCell.create(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]))
-    const aElementId = ValueCell.create(fillSerial(new Float32Array(3)))
-    const aInstanceId = ValueCell.create(fillSerial(new Float32Array(1)))
+    const aGroup = ValueCell.create(fillSerial(new Float32Array(3)))
+    const aInstance = ValueCell.create(fillSerial(new Float32Array(1)))
     const color = createValueColor(Color(0xFF0000))
     const size = createValueSize(1)
     const marker = createEmptyMarkers()
@@ -58,16 +58,16 @@ function createPoints() {
 
     const values: PointValues = {
         aPosition,
-        aElementId,
+        aGroup,
         aTransform,
-        aInstanceId,
+        aInstance,
         ...color,
         ...marker,
         ...size,
 
         uAlpha: ValueCell.create(1.0),
         uInstanceCount: ValueCell.create(1),
-        uElementCount: ValueCell.create(3),
+        uGroupCount: ValueCell.create(3),
 
         drawCount: ValueCell.create(3),
         instanceCount: ValueCell.create(1),

+ 1 - 1
src/mol-gl/renderable/point.ts

@@ -15,7 +15,7 @@ export const PointSchema = {
     ...BaseSchema,
     aSize: AttributeSpec('float32', 1, 0),
     uSize: UniformSpec('f'),
-    uSizeTexSize: UniformSpec('v2'),
+    uSizeTexDim: UniformSpec('v2'),
     tSize: TextureSpec('alpha', 'ubyte'),
     dSizeType: DefineSpec('string', ['uniform', 'attribute']),
     dPointSizeAttenuation: DefineSpec('boolean'),

+ 6 - 6
src/mol-gl/renderable/schema.ts

@@ -142,18 +142,18 @@ export const InternalSchema = {
 }
 
 export const BaseSchema = {
-    aInstanceId: AttributeSpec('float32', 1, 1),
+    aInstance: AttributeSpec('float32', 1, 1),
     aPosition: AttributeSpec('float32', 3, 0),
-    aElementId: AttributeSpec('float32', 1, 0),
+    aGroup: AttributeSpec('float32', 1, 0),
     aTransform: AttributeSpec('float32', 16, 1),
     aColor: AttributeSpec('float32', 3, 0),
 
     uAlpha: UniformSpec('f'),
     uInstanceCount: UniformSpec('i'),
-    uElementCount: UniformSpec('i'),
+    uGroupCount: UniformSpec('i'),
     uColor: UniformSpec('v3'),
-    uColorTexSize: UniformSpec('v2'),
-    uMarkerTexSize: UniformSpec('v2'),
+    uColorTexDim: UniformSpec('v2'),
+    uMarkerTexDim: UniformSpec('v2'),
 
     tColor: TextureSpec('rgb', 'ubyte'),
     tMarker: TextureSpec('alpha', 'ubyte'),
@@ -161,7 +161,7 @@ export const BaseSchema = {
     drawCount: ValueSpec('number'),
     instanceCount: ValueSpec('number'),
 
-    dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'element', 'element_instance']),
+    dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']),
     dUseFog: DefineSpec('boolean'),
 }
 export type BaseSchema = typeof BaseSchema

+ 8 - 8
src/mol-gl/shader/chunks/assign-color-varying.glsl

@@ -1,15 +1,15 @@
 #if defined(dColorType_attribute)
     vColor.rgb = aColor;
 #elif defined(dColorType_instance)
-    vColor.rgb = readFromTexture(tColor, aInstanceId, uColorTexSize).rgb;
-#elif defined(dColorType_element)
-    vColor.rgb = readFromTexture(tColor, aElementId, uColorTexSize).rgb;
-#elif defined(dColorType_elementInstance)
-    vColor.rgb = readFromTexture(tColor, aInstanceId * float(uElementCount) + aElementId, uColorTexSize).rgb;
+    vColor.rgb = readFromTexture(tColor, aInstance, uColorTexDim).rgb;
+#elif defined(dColorType_group)
+    vColor.rgb = readFromTexture(tColor, aGroup, uColorTexDim).rgb;
+#elif defined(dColorType_groupInstance)
+    vColor.rgb = readFromTexture(tColor, aGroup * float(uGroupCount) + aGroup, uColorTexDim).rgb;
 #elif defined(dColorType_objectPicking)
     vColor = encodeIdRGBA(float(uObjectId));
 #elif defined(dColorType_instancePicking)
-    vColor = encodeIdRGBA(aInstanceId);
-#elif defined(dColorType_elementPicking)
-    vColor = encodeIdRGBA(aElementId);
+    vColor = encodeIdRGBA(aInstance);
+#elif defined(dColorType_groupPicking)
+    vColor = encodeIdRGBA(aGroup);
 #endif

+ 1 - 1
src/mol-gl/shader/chunks/assign-marker-varying.glsl

@@ -1 +1 @@
-vMarker = readFromTexture(tMarker, aInstanceId * float(uElementCount) + aElementId, uMarkerTexSize).a;
+vMarker = readFromTexture(tMarker, aInstance * float(uGroupCount) + aGroup, uMarkerTexDim).a;

+ 1 - 1
src/mol-gl/shader/chunks/assign-material-color.glsl

@@ -1,5 +1,5 @@
 #if defined(dColorType_uniform)
     vec4 material = vec4(uColor, 1.0);
-#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance) || defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
+#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance) || defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
     vec4 material = vColor;
 #endif

+ 1 - 1
src/mol-gl/shader/chunks/color-frag-params.glsl

@@ -1,5 +1,5 @@
 #if defined(dColorType_uniform)
     uniform vec3 uColor;
-#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance) || defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
+#elif defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance) || defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
     varying vec4 vColor;
 #endif

+ 3 - 3
src/mol-gl/shader/chunks/color-vert-params.glsl

@@ -3,11 +3,11 @@
 #elif defined(dColorType_attribute)
     varying vec4 vColor;
     attribute vec3 aColor;
-#elif defined(dColorType_instance) || defined(dColorType_element) || defined(dColorType_elementInstance)
+#elif defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance)
     varying vec4 vColor;
-    uniform vec2 uColorTexSize;
+    uniform vec2 uColorTexDim;
     uniform sampler2D tColor;
-#elif defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
+#elif defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
     varying vec4 vColor;
     #pragma glslify: encodeIdRGBA = require(../utils/encode-id-rgba.glsl)
 #endif

+ 1 - 1
src/mol-gl/shader/chunks/common-frag-params.glsl

@@ -1,6 +1,6 @@
 uniform int uObjectId;
 uniform int uInstanceCount;
-uniform int uElementCount;
+uniform int uGroupCount;
 
 uniform vec3 uHighlightColor;
 uniform vec3 uSelectColor;

+ 2 - 2
src/mol-gl/shader/chunks/common-vert-params.glsl

@@ -2,9 +2,9 @@ uniform mat4 uProjection, uModel, uView;
 
 uniform int uObjectId;
 uniform int uInstanceCount;
-uniform int uElementCount;
+uniform int uGroupCount;
 
-uniform vec2 uMarkerTexSize;
+uniform vec2 uMarkerTexDim;
 uniform sampler2D tMarker;
 varying float vMarker;
 #pragma glslify: readFromTexture = require(../utils/read-from-texture.glsl)

+ 1 - 1
src/mol-gl/shader/mesh.frag

@@ -38,7 +38,7 @@ void main() {
     // material color
     #pragma glslify: import('./chunks/assign-material-color.glsl')
 
-    #if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
+    #if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
         // gl_FragColor = vec4(material.r, material.g, material.a, 1.0);
         gl_FragColor = material;
     #else

+ 2 - 2
src/mol-gl/shader/mesh.vert

@@ -12,8 +12,8 @@ precision highp int;
 
 attribute vec3 aPosition;
 attribute mat4 aTransform;
-attribute float aInstanceId;
-attribute float aElementId;
+attribute float aInstance;
+attribute float aGroup;
 
 #ifndef dFlatShaded
     attribute vec3 aNormal;

+ 7 - 7
src/mol-gl/shader/point.vert

@@ -21,8 +21,8 @@ uniform float uViewportHeight;
 
 attribute vec3 aPosition;
 attribute mat4 aTransform;
-attribute float aInstanceId;
-attribute float aElementId;
+attribute float aInstance;
+attribute float aGroup;
 
 void main(){
     #pragma glslify: import('./chunks/assign-color-varying.glsl')
@@ -33,11 +33,11 @@ void main(){
     #elif defined(dSizeType_attribute)
         float size = aSize;
     #elif defined(dSizeType_instance)
-        float size = readFromTexture(tSize, aInstanceId, uSizeTexSize).r;
-    #elif defined(dSizeType_element)
-        float size = readFromTexture(tSize, aElementId, uSizeTexSize).r;
-    #elif defined(dSizeType_elementInstance)
-        float size = readFromTexture(tSize, aInstanceId * float(uElementCount) + aElementId, uSizeTexSize).r;
+        float size = readFromTexture(tSize, aInstance, uSizeTexDim).r;
+    #elif defined(dSizeType_group)
+        float size = readFromTexture(tSize, aGroup, uSizeTexDim).r;
+    #elif defined(dSizeType_groupInstance)
+        float size = readFromTexture(tSize, aInstance * float(uGroupCount) + aGroup, uSizeTexDim).r;
     #endif
 
     #ifdef dPointSizeAttenuation

+ 4 - 4
src/mol-gl/shader/utils/read-from-texture.glsl

@@ -4,10 +4,10 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-vec4 readFromTexture (const in sampler2D tex, const in float i, const in vec2 size) {
-    float x = mod(i, size.x);
-    float y = floor(i / size.x);
-    vec2 uv = (vec2(x, y) + 0.5) / size;
+vec4 readFromTexture (const in sampler2D tex, const in float i, const in vec2 dim) {
+    float x = mod(i, dim.x);
+    float y = floor(i / dim.x);
+    vec2 uv = (vec2(x, y) + 0.5) / dim;
     return texture2D(tex, uv);
 }
 #pragma glslify: export(readFromTexture)

+ 1 - 1
src/mol-gl/webgl/render-item.ts

@@ -45,7 +45,7 @@ const RenderVariantDefines = {
     'draw': {},
     'pickObject': { dColorType: ValueCell.create('objectPicking') },
     'pickInstance': { dColorType: ValueCell.create('instancePicking') },
-    'pickElement': { dColorType: ValueCell.create('elementPicking') }
+    'pickGroup': { dColorType: ValueCell.create('groupPicking') }
 }
 export type RenderVariant = keyof typeof RenderVariantDefines
 

+ 4 - 3
src/mol-io/reader/obj/parser.ts

@@ -6,12 +6,13 @@
 
 import Result from '../result'
 import { Task, RuntimeContext } from 'mol-task'
-import { Mesh } from 'mol-geo/shape/mesh';
+import { Mesh } from 'mol-geo/mesh/mesh';
 
 async function parseInternal(data: string, ctx: RuntimeContext): Promise<Result<Mesh>> {
     // TODO
-    const result: Mesh = Mesh.createEmpty();
-    return Result.success(result);
+    const mesh: Mesh = Mesh.createEmpty();
+    // Mesh.computeNormalsImmediate(mesh)
+    return Result.success(mesh);
 }
 
 export function parse(data: string) {

+ 2 - 14
src/mol-model/location.ts

@@ -6,6 +6,7 @@
 
 import { StructureElement } from './structure'
 import { Link } from './structure/structure/unit/links'
+import { Shape } from './shape/shape';
 
 /** A null value Location */
 export const NullLocation = { kind: 'null-location' as 'null-location' }
@@ -14,17 +15,4 @@ export function isNullLocation(x: any): x is NullLocation {
     return !!x && x.kind === 'null-location';
 }
 
-/** A custom Location */
-export interface CustomLocation<D = any, K = any> {
-    readonly kind: 'custom-location'
-    data: D
-    key: K
-}
-export function CustomLocation<D, K>(data: D, key: K): CustomLocation<D, K> {
-    return { kind: 'custom-location', data, key }
-}
-export function isCustomLocation(x: any): x is CustomLocation<any, any> {
-    return !!x && x.kind === 'custom-location';
-}
-
-export type Location = StructureElement | Link.Location | NullLocation | CustomLocation
+export type Location = StructureElement | Link.Location | Shape.Location | NullLocation

+ 2 - 14
src/mol-model/loci.ts

@@ -6,7 +6,7 @@
 
 import { StructureElement } from './structure'
 import { Link } from './structure/structure/unit/links'
-import { CustomLocation } from './location';
+import { Shape } from './shape';
 
 /** A Loci that includes every loci */
 export const EveryLoci = { kind: 'every-loci' as 'every-loci' }
@@ -22,16 +22,4 @@ export function isEmptyLoci(x: any): x is EmptyLoci {
     return !!x && x.kind === 'empty-loci';
 }
 
-/** A Loci of custom locations */
-export interface CustomLoci<D = any, K = any> {
-    readonly kind: 'custom-loci'
-    readonly locations: ReadonlyArray<CustomLocation>
-}
-export function CustomLoci<D, K>(locations: CustomLocation<D, K>[]): CustomLoci<D, K> {
-    return { kind: 'custom-loci', locations }
-}
-export function isCustomLoci(x: any): x is CustomLoci {
-    return !!x && x.kind === 'custom-loci';
-}
-
-export type Loci =  StructureElement.Loci | Link.Loci | EveryLoci | EmptyLoci | CustomLoci
+export type Loci =  StructureElement.Loci | Link.Loci | EveryLoci | EmptyLoci | Shape.Loci

+ 7 - 0
src/mol-model/shape.ts

@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+export * from './shape/shape'

+ 54 - 0
src/mol-model/shape/shape.ts

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Mesh } from 'mol-geo/mesh/mesh';
+import { Color } from 'mol-util/color';
+import { UUID } from 'mol-util';
+import { OrderedSet } from 'mol-data/int';
+
+export interface Shape {
+    readonly id: UUID
+    readonly name: string
+    readonly mesh: Mesh
+    getColor(group: number): Color
+    getLabel(group: number): string
+}
+
+export namespace Shape {
+    export function create(mesh: Mesh, name: string, getColor: (group: number) => Color, getLabel: (group: number) => string): Shape {
+        return { id: UUID.create(), name, mesh, getColor, getLabel }
+    }
+
+    export interface Location {
+        readonly kind: 'group-location'
+        shape: Shape
+        group: number
+    }
+
+    export function Location(shape?: Shape, group?: number): Location {
+        return { kind: 'group-location', shape: shape!, group: group || 0 };
+    }
+
+    export function isLocation(x: any): x is Location {
+        return !!x && x.kind === 'group-location';
+    }
+
+    export interface Loci {
+        readonly kind: 'group-loci',
+        readonly groups: ReadonlyArray<{
+            shape: Shape,
+            ids: OrderedSet<number>
+        }>
+    }
+
+    export function Loci(groups: ArrayLike<{ shape: Shape, ids: OrderedSet<number> }>): Loci {
+        return { kind: 'group-loci', groups: groups as Loci['groups'] };
+    }
+
+    export function isLoci(x: any): x is Loci {
+        return !!x && x.kind === 'group-loci';
+    }
+}

+ 1 - 1
src/mol-model/structure/structure/element.ts

@@ -18,7 +18,7 @@ interface StructureElement<U = Unit> {
 
 namespace StructureElement {
     export function create(unit?: Unit, element?: ElementIndex): StructureElement {
-        return { kind: 'element-location', unit: unit as any, element: element || (0 as ElementIndex) };
+        return { kind: 'element-location', unit: unit!, element: element || (0 as ElementIndex) };
     }
 
     // TODO: when nominal types are available, make this indexed by UnitIndex

+ 8 - 0
src/mol-util/array.ts

@@ -4,6 +4,9 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+// TODO move to mol-math as Vector???
+
+/** Get the maximum value in an array */
 export function arrayMax(array: Helpers.NumberArray) {
     let max = -Infinity
     for (let i = 0, il = array.length; i < il; ++i) {
@@ -12,6 +15,7 @@ export function arrayMax(array: Helpers.NumberArray) {
     return max
 }
 
+/** Get the minimum value in an array */
 export function arrayMin(array: Helpers.NumberArray) {
     let min = Infinity
     for (let i = 0, il = array.length; i < il; ++i) {
@@ -20,6 +24,7 @@ export function arrayMin(array: Helpers.NumberArray) {
     return min
 }
 
+/** Get the sum of values in an array */
 export function arraySum(array: Helpers.NumberArray, stride = 1, offset = 0) {
     const n = array.length
     let sum = 0
@@ -29,10 +34,12 @@ export function arraySum(array: Helpers.NumberArray, stride = 1, offset = 0) {
     return sum
 }
 
+/** Get the mean of values in an array */
 export function arrayMean(array: Helpers.NumberArray, stride = 1, offset = 0) {
     return arraySum(array, stride, offset) / (array.length / stride)
 }
 
+/** Get the root mean square of values in an array */
 export function arrayRms(array: Helpers.NumberArray) {
     const n = array.length
     let sumSq = 0
@@ -43,6 +50,7 @@ export function arrayRms(array: Helpers.NumberArray) {
     return Math.sqrt(sumSq / n)
 }
 
+/** Fill an array with serial numbers starting from 0 */
 export function fillSerial<T extends Helpers.NumberArray> (array: T) {
     const n = array.length
     for (let i = 0; i < n; ++i) array[ i ] = i

+ 8 - 3
src/mol-view/label.ts

@@ -12,7 +12,7 @@ import { OrderedSet } from 'mol-data/int';
 const elementLocA = StructureElement.create()
 const elementLocB = StructureElement.create()
 
-function setElementLocation(loc: StructureElement, unit: Unit, index: number) {
+function setElementLocation(loc: StructureElement, unit: Unit, index: StructureElement.UnitIndex) {
     loc.unit = unit
     loc.element = unit.elements[index]
 }
@@ -36,8 +36,13 @@ export function labelFirst(loci: Loci): string {
             } else {
                 return 'Unknown'
             }
-        case 'custom-loci':
-            return 'Custom'
+        case 'group-loci':
+            const g = loci.groups[0]
+            if (g) {
+                return g.shape.getLabel(OrderedSet.getAt(g.ids, 0))
+            } else {
+                return 'Unknown'
+            }
         case 'every-loci':
             return 'Everything'
         case 'empty-loci':

+ 4 - 1
src/mol-view/theme/color.ts

@@ -15,6 +15,7 @@ import { ElementSymbolColorTheme } from './color/element-symbol';
 import { UnitIndexColorTheme } from './color/unit-index';
 import { UniformColorTheme } from './color/uniform';
 import { CrossLinkColorTheme } from './color/cross-link';
+import { ShapeGroupColorTheme } from './color/shape-group';
 
 export interface ColorTheme {
     kind: ColorType
@@ -30,6 +31,7 @@ export function ColorTheme(props: ColorThemeProps): ColorTheme {
         case 'element-symbol': return ElementSymbolColorTheme(props)
         case 'unit-index': return UnitIndexColorTheme(props)
         case 'uniform': return UniformColorTheme(props)
+        case 'shape-group': return ShapeGroupColorTheme(props)
     }
 }
 
@@ -47,7 +49,8 @@ export const ColorThemeInfo = {
     'chain-id': {},
     'element-symbol': {},
     'unit-index': {},
-    'uniform': {}
+    'uniform': {},
+    'shape-group': {}
 }
 export type ColorThemeName = keyof typeof ColorThemeInfo
 export const ColorThemeNames = Object.keys(ColorThemeInfo)

+ 1 - 1
src/mol-view/theme/color/carbohydrate-symbol.ts

@@ -47,7 +47,7 @@ export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme
     }
 
     return {
-        kind: 'element',
+        kind: 'group',
         color: colorFn
     }
 }

+ 1 - 1
src/mol-view/theme/color/chain-id.ts

@@ -43,7 +43,7 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        kind: 'element',
+        kind: 'group',
         color: colorFn
     }
 }

+ 1 - 1
src/mol-view/theme/color/cross-link.ts

@@ -42,7 +42,7 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        kind: 'element',
+        kind: 'group',
         color: colorFn
     }
 }

+ 1 - 1
src/mol-view/theme/color/element-index.ts

@@ -44,7 +44,7 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        kind: 'elementInstance',
+        kind: 'groupInstance',
         color: colorFn
     }
 }

+ 1 - 1
src/mol-view/theme/color/element-symbol.ts

@@ -39,7 +39,7 @@ export function ElementSymbolColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        kind: 'element',
+        kind: 'group',
         color: colorFn
     }
 }

+ 24 - 0
src/mol-view/theme/color/shape-group.ts

@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ColorTheme, ColorThemeProps } from '../color';
+import { Color } from 'mol-util/color';
+import { Location } from 'mol-model/location';
+import { Shape } from 'mol-model/shape';
+
+const DefaultColor = 0xCCCCCC as Color
+
+export function ShapeGroupColorTheme(props: ColorThemeProps): ColorTheme {
+    return {
+        kind: 'group',
+        color: (location: Location): Color => {
+            if (Shape.isLocation(location)) {
+                return location.shape.getColor(location.group)
+            }
+            return DefaultColor
+        }
+    }
+}

+ 1 - 1
src/mol-view/theme/size/physical.ts

@@ -42,7 +42,7 @@ export function PhysicalSizeTheme(props: SizeThemeProps): SizeTheme {
     }
 
     return {
-        kind: 'element',
+        kind: 'group',
         size: sizeFn
     }
 }

+ 8 - 8
src/mol-view/viewer.ts

@@ -109,7 +109,7 @@ namespace Viewer {
         const pickHeight = Math.round(canvas.height * pickScale)
         const objectPickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
         const instancePickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
-        const elementPickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
+        const groupPickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
 
         let pickDirty = true
         let drawPending = false
@@ -165,7 +165,7 @@ namespace Viewer {
             switch (variant) {
                 case 'pickObject': objectPickTarget.bind(); break;
                 case 'pickInstance': instancePickTarget.bind(); break;
-                case 'pickElement': elementPickTarget.bind(); break;
+                case 'pickGroup': groupPickTarget.bind(); break;
                 case 'draw':
                     ctx.unbindFramebuffer();
                     renderer.setViewport(0, 0, canvas.width, canvas.height);
@@ -210,7 +210,7 @@ namespace Viewer {
         function pick() {
             render('pickObject', pickDirty)
             render('pickInstance', pickDirty)
-            render('pickElement', pickDirty)
+            render('pickGroup', pickDirty)
 
             pickDirty = false
         }
@@ -232,11 +232,11 @@ namespace Viewer {
             ctx.readPixels(xp, yp, 1, 1, buffer)
             const instanceId = decodeIdRGBA(buffer[0], buffer[1], buffer[2])
 
-            elementPickTarget.bind()
+            groupPickTarget.bind()
             ctx.readPixels(xp, yp, 1, 1, buffer)
-            const elementId = decodeIdRGBA(buffer[0], buffer[1], buffer[2])
+            const groupId = decodeIdRGBA(buffer[0], buffer[1], buffer[2])
 
-            return { objectId, instanceId, elementId }
+            return { objectId, instanceId, groupId }
         }
 
         handleResize()
@@ -303,7 +303,7 @@ namespace Viewer {
                     case 'draw': return renderer.getImageData()
                     case 'pickObject': return objectPickTarget.getImageData()
                     case 'pickInstance': return instancePickTarget.getImageData()
-                    case 'pickElement': return elementPickTarget.getImageData()
+                    case 'pickGroup': return groupPickTarget.getImageData()
                 }
             },
             reprCount,
@@ -334,7 +334,7 @@ namespace Viewer {
             const pickHeight = Math.round(canvas.height * pickScale)
             objectPickTarget.setSize(pickWidth, pickHeight)
             instancePickTarget.setSize(pickWidth, pickHeight)
-            elementPickTarget.setSize(pickWidth, pickHeight)
+            groupPickTarget.setSize(pickWidth, pickHeight)
         }
     }
 }