Browse Source

Merge branch 'master' of https://github.com/molstar/molstar-proto into mol-model

David Sehnal 6 years ago
parent
commit
e878e285a0
97 changed files with 2069 additions and 1172 deletions
  1. 21 0
      README.md
  2. 283 249
      package-lock.json
  3. 10 8
      package.json
  4. 15 0
      src/apps/canvas/index.html
  5. 154 0
      src/apps/canvas/index.ts
  6. 0 0
      src/apps/schema-generator/schema-from-cif-dic.ts
  7. 44 3
      src/apps/schema-generator/util/cif-dic.ts
  8. 42 19
      src/apps/schema-generator/util/generate.ts
  9. 5 1
      src/apps/schema-generator/util/schema.ts
  10. 1 1
      src/apps/viewer/index.html
  11. 2 2
      src/mol-app/ui/visualization/viewport.tsx
  12. 89 0
      src/mol-geo/mesh/builder/cylinder.ts
  13. 5 4
      src/mol-geo/mesh/builder/sheet.ts
  14. 30 0
      src/mol-geo/mesh/builder/sphere.ts
  15. 6 5
      src/mol-geo/mesh/builder/tube.ts
  16. 81 0
      src/mol-geo/mesh/mesh-builder.ts
  17. 8 18
      src/mol-geo/mesh/mesh.ts
  18. 2 2
      src/mol-geo/primitive/box.ts
  19. 19 25
      src/mol-geo/primitive/plane.ts
  20. 3 3
      src/mol-geo/primitive/prism.ts
  21. 15 15
      src/mol-geo/primitive/pyramid.ts
  22. 2 8
      src/mol-geo/primitive/sphere.ts
  23. 2 2
      src/mol-geo/primitive/star.ts
  24. 2 2
      src/mol-geo/primitive/wedge.ts
  25. 128 0
      src/mol-geo/representation/shape/index.ts
  26. 2 2
      src/mol-geo/representation/structure/complex-representation.ts
  27. 4 4
      src/mol-geo/representation/structure/complex-visual.ts
  28. 2 1
      src/mol-geo/representation/structure/representation/cartoon.ts
  29. 4 4
      src/mol-geo/representation/structure/units-visual.ts
  30. 8 8
      src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts
  31. 46 28
      src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
  32. 8 8
      src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
  33. 4 5
      src/mol-geo/representation/structure/visual/element-point.ts
  34. 1 2
      src/mol-geo/representation/structure/visual/element-sphere.ts
  35. 4 5
      src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
  36. 5 6
      src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
  37. 10 7
      src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
  38. 8 8
      src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
  39. 8 6
      src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
  40. 11 10
      src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
  41. 12 11
      src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
  42. 12 18
      src/mol-geo/representation/structure/visual/util/common.ts
  43. 22 6
      src/mol-geo/representation/structure/visual/util/element.ts
  44. 41 9
      src/mol-geo/representation/structure/visual/util/link.ts
  45. 0 141
      src/mol-geo/representation/structure/visual/util/location-iterator.ts
  46. 10 3
      src/mol-geo/representation/util.ts
  47. 3 3
      src/mol-geo/representation/volume/surface.ts
  48. 0 279
      src/mol-geo/shape/mesh-builder.ts
  49. 0 53
      src/mol-geo/shape/vertex-map.ts
  50. 18 18
      src/mol-geo/util/color-data.ts
  51. 97 0
      src/mol-geo/util/location-iterator.ts
  52. 5 8
      src/mol-geo/util/marching-cubes/algorithm.ts
  53. 6 6
      src/mol-geo/util/marker-data.ts
  54. 3 3
      src/mol-geo/util/mesh-data.ts
  55. 1 1
      src/mol-geo/util/picking.ts
  56. 17 17
      src/mol-geo/util/size-data.ts
  57. 5 5
      src/mol-gl/_spec/renderer.spec.ts
  58. 1 1
      src/mol-gl/renderable/point.ts
  59. 6 6
      src/mol-gl/renderable/schema.ts
  60. 8 8
      src/mol-gl/shader/chunks/assign-color-varying.glsl
  61. 1 1
      src/mol-gl/shader/chunks/assign-marker-varying.glsl
  62. 1 1
      src/mol-gl/shader/chunks/assign-material-color.glsl
  63. 1 1
      src/mol-gl/shader/chunks/color-frag-params.glsl
  64. 3 3
      src/mol-gl/shader/chunks/color-vert-params.glsl
  65. 1 1
      src/mol-gl/shader/chunks/common-frag-params.glsl
  66. 2 2
      src/mol-gl/shader/chunks/common-vert-params.glsl
  67. 1 1
      src/mol-gl/shader/mesh.frag
  68. 2 2
      src/mol-gl/shader/mesh.vert
  69. 7 7
      src/mol-gl/shader/point.vert
  70. 4 4
      src/mol-gl/shader/utils/read-from-texture.glsl
  71. 1 1
      src/mol-gl/webgl/render-item.ts
  72. 42 6
      src/mol-io/reader/cif/schema/bird.ts
  73. 34 6
      src/mol-io/reader/cif/schema/ccd.ts
  74. 411 25
      src/mol-io/reader/cif/schema/mmcif.ts
  75. 24 0
      src/mol-io/reader/obj/parser.ts
  76. 1 1
      src/mol-math/linear-algebra/3d/vec3.ts
  77. 2 2
      src/mol-model-props/rcsb/graphql/types.ts
  78. 2 1
      src/mol-model/location.ts
  79. 2 1
      src/mol-model/loci.ts
  80. 7 0
      src/mol-model/shape.ts
  81. 71 0
      src/mol-model/shape/shape.ts
  82. 3 13
      src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts
  83. 18 7
      src/mol-model/structure/model/types.ts
  84. 4 0
      src/mol-model/structure/query/queries/generators.ts
  85. 1 1
      src/mol-model/structure/structure/element.ts
  86. 8 0
      src/mol-util/array.ts
  87. 8 1
      src/mol-view/label.ts
  88. 4 2
      src/mol-view/stage.ts
  89. 4 1
      src/mol-view/theme/color.ts
  90. 1 1
      src/mol-view/theme/color/carbohydrate-symbol.ts
  91. 1 1
      src/mol-view/theme/color/chain-id.ts
  92. 1 1
      src/mol-view/theme/color/cross-link.ts
  93. 1 1
      src/mol-view/theme/color/element-index.ts
  94. 1 1
      src/mol-view/theme/color/element-symbol.ts
  95. 24 0
      src/mol-view/theme/color/shape-group.ts
  96. 1 1
      src/mol-view/theme/size/physical.ts
  97. 8 8
      src/mol-view/viewer.ts

+ 21 - 0
README.md

@@ -78,8 +78,29 @@ From the root of the project:
 
 and navigate to `build/viewer`
 
+### Code generation
+**CIF schemas**
 
+    node build/node_modules/apps/schema-generator/schema-from-cif-dic.js -ts -o src/mol-io/reader/cif/schema/mmcif.ts --fieldNamesPath data/mmcif-field-names.csv --name mmCIF
 
+    node build/node_modules/apps/schema-generator/schema-from-cif-dic.js -ts -o src/mol-io/reader/cif/schema/ccd.ts --fieldNamesPath data/ccd-field-names.csv --name CCD
+
+    node build/node_modules/apps/schema-generator/schema-from-cif-dic.js -ts -o src/mol-io/reader/cif/schema/bird.ts --fieldNamesPath data/bird-field-names.csv --name BIRD
+
+    node --max-old-space-size=8192 build/node_modules/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
+
+**GraphQL schemas**
+
+    node data/rcsb-graphql/codegen.js
+
+### Other scripts
+**Create chem comp bond table**
+
+    node --max-old-space-size=8192 build/node_modules/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
+
+**Test model server**
+
+    node build/node_modules/servers/model/test.js
 
 ## Contributing
 Just open an issue or make a pull request. All contributions are welcome.

+ 283 - 249
package-lock.json

@@ -138,9 +138,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "10.7.0",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.7.0.tgz",
-      "integrity": "sha512-dmYIvoQEZWnyQfgrwPCoxztv/93NYQGEiOoQhuI56rJahv9de6Q2apZl3bufV46YJ0OAXdaktIuw4RIRl4DTeA==",
+      "version": "10.7.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.7.1.tgz",
+      "integrity": "sha512-EGoI4ylB/lPOaqXqtzAyL8HcgOuCtH2hkEaLmkueOYufsTFWBn4VCvlCDC2HW8Q+9iF+QVC3sxjDKQYjHQeZ9w==",
       "dev": true
     },
     "@types/node-fetch": {
@@ -174,9 +174,9 @@
       "dev": true
     },
     "@types/react": {
-      "version": "16.4.10",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.10.tgz",
-      "integrity": "sha512-lnJeNJL5KJefTpemubc1HULbH+O1laDeUOJXlP2G12faTKmyPo3wRzbuBoKCli9mCW4hRUKM14PbadGx5rILgg==",
+      "version": "16.4.11",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.11.tgz",
+      "integrity": "sha512-1DQnmwO8u8N3ucvRX2ZLDEjQ2VctkAvL/rpbm2ev4uaZA0z4ysU+I0tk+K8ZLblC6p7MCgFyF+cQlSNIPUHzeQ==",
       "dev": true,
       "requires": {
         "@types/prop-types": "*",
@@ -468,90 +468,6 @@
         "long": "^3.2.0"
       }
     },
-    "@webpack-contrib/schema-utils": {
-      "version": "1.0.0-beta.0",
-      "resolved": "https://registry.npmjs.org/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz",
-      "integrity": "sha512-LonryJP+FxQQHsjGBi6W786TQB1Oym+agTpY0c+Kj8alnIw+DLUJb6SI8Y1GHGhLCH1yPRrucjObUmxNICQ1pg==",
-      "dev": true,
-      "requires": {
-        "ajv": "^6.1.0",
-        "ajv-keywords": "^3.1.0",
-        "chalk": "^2.3.2",
-        "strip-ansi": "^4.0.0",
-        "text-table": "^0.2.0",
-        "webpack-log": "^1.1.2"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "6.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz",
-          "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==",
-          "dev": true,
-          "requires": {
-            "fast-deep-equal": "^2.0.1",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.4.1",
-            "uri-js": "^4.2.1"
-          }
-        },
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
-        "json-schema-traverse": {
-          "version": "0.4.1",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        },
-        "uri-js": {
-          "version": "4.2.2",
-          "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
-          "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
-          "dev": true,
-          "requires": {
-            "punycode": "^2.1.0"
-          }
-        }
-      }
-    },
     "abab": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
@@ -649,6 +565,12 @@
         "json-schema-traverse": "^0.3.0"
       }
     },
+    "ajv-errors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz",
+      "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=",
+      "dev": true
+    },
     "ajv-keywords": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz",
@@ -715,9 +637,9 @@
       }
     },
     "apollo-utilities": {
-      "version": "1.0.17",
-      "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.17.tgz",
-      "integrity": "sha512-Mm8pS48QVV/CUbcUFN5TmbkdjIJtYw99qVqbBHnIwL8x0H2RW0NpzASpC+5+qSxD1sKNLUl9pA6F5Et/0bbaVg==",
+      "version": "1.0.19",
+      "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.19.tgz",
+      "integrity": "sha512-pyVxizjIevHFfKhtc9FLEsGHmqiK0kHx1aBdJRUXDt+X+yjoVa/fVeCEo9t0NddGximemxxrQnq6lSkbIQvDlA==",
       "dev": true,
       "requires": {
         "fast-json-stable-stringify": "^2.0.0"
@@ -1913,9 +1835,9 @@
       "dev": true
     },
     "colors": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.1.tgz",
-      "integrity": "sha512-jg/vxRmv430jixZrC+La5kMbUWqIg32/JsYNZb94+JEmzceYbWKTsv1OuTp+7EaqiaWRR2tPcykibwCRgclIsw==",
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz",
+      "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ==",
       "dev": true
     },
     "colorspace": {
@@ -2308,15 +2230,6 @@
       "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
       "dev": true
     },
-    "d": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
-      "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
-      "dev": true,
-      "requires": {
-        "es5-ext": "^0.10.9"
-      }
-    },
     "dashdash": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -2623,9 +2536,9 @@
       "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
     },
     "elliptic": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
-      "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
+      "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
       "dev": true,
       "requires": {
         "bn.js": "^4.4.0",
@@ -2733,38 +2646,6 @@
         "is-symbol": "^1.0.1"
       }
     },
-    "es5-ext": {
-      "version": "0.10.45",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz",
-      "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==",
-      "dev": true,
-      "requires": {
-        "es6-iterator": "~2.0.3",
-        "es6-symbol": "~3.1.1",
-        "next-tick": "1"
-      }
-    },
-    "es6-iterator": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
-      "dev": true,
-      "requires": {
-        "d": "1",
-        "es5-ext": "^0.10.35",
-        "es6-symbol": "^3.1.1"
-      }
-    },
-    "es6-symbol": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
-      "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
-      "dev": true,
-      "requires": {
-        "d": "1",
-        "es5-ext": "~0.10.14"
-      }
-    },
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -3093,9 +2974,9 @@
       "dev": true
     },
     "fast-safe-stringify": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.5.tgz",
-      "integrity": "sha512-QHbbCj2PmRSMNL9P7EuNBCeNXO06/E3t3XyQgb32AZul8wLmRa1Wbt2cm7GeUsX9OZGyXTQxMYcPOEBqARyhNw==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz",
+      "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==",
       "dev": true
     },
     "fastparse": {
@@ -3150,13 +3031,59 @@
       }
     },
     "file-loader": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
-      "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz",
+      "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==",
       "dev": true,
       "requires": {
         "loader-utils": "^1.0.2",
-        "schema-utils": "^0.4.5"
+        "schema-utils": "^1.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
+          "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+          "dev": true
+        },
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        },
+        "uri-js": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+          "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+          "dev": true,
+          "requires": {
+            "punycode": "^2.1.0"
+          }
+        }
       }
     },
     "filename-regex": {
@@ -4321,9 +4248,9 @@
       }
     },
     "graphql-code-generator": {
-      "version": "0.10.7",
-      "resolved": "https://registry.npmjs.org/graphql-code-generator/-/graphql-code-generator-0.10.7.tgz",
-      "integrity": "sha512-uqdnf5y42dylQM7V7EnhV9LC9wSRqyz1hqBUGsNps0+04T/asgOMTYL4pTC8hqQuwjkAdnaOYPBGuXNvSXpByg==",
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/graphql-code-generator/-/graphql-code-generator-0.11.0.tgz",
+      "integrity": "sha512-O9t96xR4lpXWqGVahz3lXK427pP7s0by+JIJw775M4yy2FqHmfPPWUFKdA+SOXcmgwfGsnsJmGNn3UftysFEow==",
       "dev": true,
       "requires": {
         "@types/babylon": "6.16.3",
@@ -4335,17 +4262,24 @@
         "commander": "2.17.1",
         "fb-watchman": "2.0.0",
         "glob": "7.1.2",
-        "graphql-codegen-compiler": "0.10.7",
-        "graphql-codegen-core": "0.10.7",
+        "graphql-codegen-compiler": "0.11.0",
+        "graphql-codegen-core": "0.11.0",
+        "graphql-import": "0.6.0",
         "is-glob": "4.0.0",
         "is-valid-path": "0.1.1",
         "mkdirp": "0.5.1",
-        "pify": "3.0.0",
-        "prettier": "1.14.0",
-        "request": "2.87.0",
+        "pify": "4.0.0",
+        "prettier": "1.14.2",
+        "request": "2.88.0",
         "valid-url": "1.0.9"
       },
       "dependencies": {
+        "aws4": {
+          "version": "1.8.0",
+          "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+          "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+          "dev": true
+        },
         "babel-types": {
           "version": "7.0.0-beta.3",
           "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-7.0.0-beta.3.tgz",
@@ -4369,6 +4303,22 @@
           "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
           "dev": true
         },
+        "extend": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+          "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+          "dev": true
+        },
+        "har-validator": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz",
+          "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
+          "dev": true,
+          "requires": {
+            "ajv": "^5.3.0",
+            "har-schema": "^2.0.0"
+          }
+        },
         "is-extglob": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -4384,10 +4334,77 @@
             "is-extglob": "^2.1.1"
           }
         },
+        "mime-db": {
+          "version": "1.35.0",
+          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
+          "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==",
+          "dev": true
+        },
+        "mime-types": {
+          "version": "2.1.19",
+          "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
+          "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
+          "dev": true,
+          "requires": {
+            "mime-db": "~1.35.0"
+          }
+        },
+        "oauth-sign": {
+          "version": "0.9.0",
+          "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+          "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+          "dev": true
+        },
         "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.0.tgz",
+          "integrity": "sha512-zrSP/KDf9DH3K3VePONoCstgPiYJy9z0SCatZuTpOc7YdnWIqwkWdXOuwlr4uDc7em8QZRsFWsT/685x5InjYg==",
+          "dev": true
+        },
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        },
+        "qs": {
+          "version": "6.5.2",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+          "dev": true
+        },
+        "request": {
+          "version": "2.88.0",
+          "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+          "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+          "dev": true,
+          "requires": {
+            "aws-sign2": "~0.7.0",
+            "aws4": "^1.8.0",
+            "caseless": "~0.12.0",
+            "combined-stream": "~1.0.6",
+            "extend": "~3.0.2",
+            "forever-agent": "~0.6.1",
+            "form-data": "~2.3.2",
+            "har-validator": "~5.1.0",
+            "http-signature": "~1.2.0",
+            "is-typedarray": "~1.0.0",
+            "isstream": "~0.1.2",
+            "json-stringify-safe": "~5.0.1",
+            "mime-types": "~2.1.19",
+            "oauth-sign": "~0.9.0",
+            "performance-now": "^2.1.0",
+            "qs": "~6.5.2",
+            "safe-buffer": "^5.1.2",
+            "tough-cookie": "~2.4.3",
+            "tunnel-agent": "^0.6.0",
+            "uuid": "^3.3.2"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
           "dev": true
         },
         "to-fast-properties": {
@@ -4395,27 +4412,43 @@
           "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
           "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
           "dev": true
+        },
+        "tough-cookie": {
+          "version": "2.4.3",
+          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+          "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+          "dev": true,
+          "requires": {
+            "psl": "^1.1.24",
+            "punycode": "^1.4.1"
+          }
+        },
+        "uuid": {
+          "version": "3.3.2",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+          "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+          "dev": true
         }
       }
     },
     "graphql-codegen-compiler": {
-      "version": "0.10.7",
-      "resolved": "https://registry.npmjs.org/graphql-codegen-compiler/-/graphql-codegen-compiler-0.10.7.tgz",
-      "integrity": "sha512-9tTBDecsMnxiuOLAbybJwU+5ZTu8sdrh2dMMDfioVinnM8Yy3WBVy5sp6LV/rudWEcG9MEWXoPzZPrx+ZxtN0A==",
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/graphql-codegen-compiler/-/graphql-codegen-compiler-0.11.0.tgz",
+      "integrity": "sha512-0XgfC1OAm54atLqfpjl0d2yHyNkGY3CpdiWhNp4o5RlACxQQjWRoHS2SQr26eRShlWwjkk5Bq7LZZ46DhH2nLw==",
       "dev": true,
       "requires": {
         "@types/handlebars": "4.0.39",
         "change-case": "3.0.2",
         "common-tags": "1.8.0",
-        "graphql-codegen-core": "0.10.7",
+        "graphql-codegen-core": "0.11.0",
         "handlebars": "4.0.11",
         "moment": "2.22.2"
       }
     },
     "graphql-codegen-core": {
-      "version": "0.10.7",
-      "resolved": "https://registry.npmjs.org/graphql-codegen-core/-/graphql-codegen-core-0.10.7.tgz",
-      "integrity": "sha512-9o+alXnxzwqitDDp79NyaG9nj/61fHHucQpdp5Lxm5pOoCmCNstcS3NYRBf/rCiEIzDN5/lt78pJiTN0vaVXKg==",
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/graphql-codegen-core/-/graphql-codegen-core-0.11.0.tgz",
+      "integrity": "sha512-QvrPEgdk7szuVKfdgdmqJppNGw7fG/EQ1O3YzD/ySHI+Lm2I0OiGJZQO+J6SImSPBfPaKc+4wjoeUq7JgANoFg==",
       "dev": true,
       "requires": {
         "graphql-tag": "2.9.2",
@@ -4424,11 +4457,20 @@
       }
     },
     "graphql-codegen-typescript-template": {
-      "version": "0.10.7",
-      "resolved": "https://registry.npmjs.org/graphql-codegen-typescript-template/-/graphql-codegen-typescript-template-0.10.7.tgz",
-      "integrity": "sha512-D3b4ND9X9XZiC9G9REbmFxSaY91XUTm44lGHixmHS9SGKCiuhfTrqFMR8z1FcRRuvRJBXx2qeXtMsI3BTC3rig==",
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/graphql-codegen-typescript-template/-/graphql-codegen-typescript-template-0.11.0.tgz",
+      "integrity": "sha512-WulEWyORcEMNSLnUw5LrWrlFDb8PyKA46JcYGgVC9pZSC+b2G4GS9jybSdKXiWVxLrah2er07SILJYiunjUNPQ==",
       "dev": true
     },
+    "graphql-import": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/graphql-import/-/graphql-import-0.6.0.tgz",
+      "integrity": "sha512-S6Aj4pTzehCwyh7hKUKlWLgE6soDdW/T6JPePC16exzFDNdZHXRj7lqv75yMWJCkBY0pBtWicMLinb4ni6QRyg==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.4"
+      }
+    },
     "graphql-request": {
       "version": "1.8.2",
       "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-1.8.2.tgz",
@@ -4565,12 +4607,6 @@
       "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
       "dev": true
     },
-    "has-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
-      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
-      "dev": true
-    },
     "has-unicode": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -6083,15 +6119,6 @@
       "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
       "dev": true
     },
-    "log-symbols": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
-      "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.0.1"
-      }
-    },
     "logform": {
       "version": "1.9.1",
       "resolved": "https://registry.npmjs.org/logform/-/logform-1.9.1.tgz",
@@ -6113,16 +6140,6 @@
         }
       }
     },
-    "loglevelnext": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz",
-      "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==",
-      "dev": true,
-      "requires": {
-        "es6-symbol": "^3.1.1",
-        "object.assign": "^4.1.0"
-      }
-    },
     "long": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
@@ -6398,14 +6415,60 @@
       "dev": true
     },
     "mini-css-extract-plugin": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.1.tgz",
-      "integrity": "sha512-XWuB3G61Rtasq/gLe7cp5cuozehE6hN+E4sxCamRR/WDiHTg+f7ZIAS024r8UJQffY+e2gGELXQZgQoFDfNDCg==",
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.2.tgz",
+      "integrity": "sha512-ots7URQH4wccfJq9Ssrzu2+qupbncAce4TmTzunI9CIwlQMp2XI+WNUw6xWF6MMAGAm1cbUVINrSjATaVMyKXg==",
       "dev": true,
       "requires": {
-        "@webpack-contrib/schema-utils": "^1.0.0-beta.0",
         "loader-utils": "^1.1.0",
+        "schema-utils": "^1.0.0",
         "webpack-sources": "^1.1.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
+          "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+          "dev": true
+        },
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        },
+        "uri-js": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+          "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+          "dev": true,
+          "requires": {
+            "punycode": "^2.1.0"
+          }
+        }
       }
     },
     "minimalistic-assert": {
@@ -6594,12 +6657,6 @@
       "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==",
       "dev": true
     },
-    "next-tick": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
-      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
-      "dev": true
-    },
     "nice-try": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz",
@@ -6909,18 +6966,6 @@
         }
       }
     },
-    "object.assign": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
-      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "function-bind": "^1.1.1",
-        "has-symbols": "^1.0.0",
-        "object-keys": "^1.0.11"
-      }
-    },
     "object.getownpropertydescriptors": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
@@ -7408,9 +7453,9 @@
       "dev": true
     },
     "prettier": {
-      "version": "1.14.0",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.0.tgz",
-      "integrity": "sha512-KtQ2EGaUwf2EyDfp1fxyEb0PqGKakVm0WyXwDt6u+cAoxbO2Z2CwKvOe3+b4+F2IlO9lYHi1kqFuRM70ddBnow==",
+      "version": "1.14.2",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz",
+      "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==",
       "dev": true
     },
     "pretty-format": {
@@ -7503,6 +7548,12 @@
       "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
       "dev": true
     },
+    "psl": {
+      "version": "1.1.29",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
+      "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==",
+      "dev": true
+    },
     "public-encrypt": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz",
@@ -9772,12 +9823,6 @@
       "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
       "dev": true
     },
-    "text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
-      "dev": true
-    },
     "throat": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz",
@@ -9957,13 +10002,14 @@
       }
     },
     "ts-jest": {
-      "version": "23.1.3",
-      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-23.1.3.tgz",
-      "integrity": "sha512-nb0wF7zBsmjQUmumrxiW7HQLfYdFosdZfozh+JRLgDcIjOTKe3Vpf1T9Jlp8JBi7OvZ7OFKjpXIwjL7tyliU9Q==",
+      "version": "23.1.4",
+      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-23.1.4.tgz",
+      "integrity": "sha512-9rCSxbWfoZxxeXnSoEIzRNr9hDIQ8iEJAWmSRsWhDHDT8OeuGfURhJQUE8jtJlkyEygs6rngH8RYtHz9cfjmEA==",
       "dev": true,
       "requires": {
         "closest-file-data": "^0.1.4",
         "fs-extra": "6.0.1",
+        "json5": "^0.5.0",
         "lodash": "^4.17.10"
       }
     },
@@ -10095,9 +10141,9 @@
       "optional": true
     },
     "uglifyjs-webpack-plugin": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz",
-      "integrity": "sha512-1VicfKhCYHLS8m1DCApqBhoulnASsEoJ/BvpUpP4zoNAPpKzdH+ghk0olGJMmwX2/jprK2j3hAHdUbczBSy2FA==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz",
+      "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==",
       "dev": true,
       "requires": {
         "cacache": "^10.0.4",
@@ -10800,9 +10846,9 @@
       "dev": true
     },
     "webpack": {
-      "version": "4.16.5",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.5.tgz",
-      "integrity": "sha512-i5cHYHonzSc1zBuwB5MSzW4v9cScZFbprkHK8ZgzPDCRkQXGGpYzPmJhbus5bOrZ0tXTcQp+xyImRSvKb0b+Kw==",
+      "version": "4.17.1",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.17.1.tgz",
+      "integrity": "sha512-vdPYogljzWPhFKDj3Gcp01Vqgu7K3IQlybc3XIdKSQHelK1C3eIQuysEUR7MxKJmdandZlQB/9BG2Jb1leJHaw==",
       "dev": true,
       "requires": {
         "@webassemblyjs/ast": "1.5.13",
@@ -10839,15 +10885,15 @@
           "dev": true
         },
         "ajv": {
-          "version": "6.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz",
-          "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==",
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
+          "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
           "dev": true,
           "requires": {
             "fast-deep-equal": "^2.0.1",
             "fast-json-stable-stringify": "^2.0.0",
             "json-schema-traverse": "^0.4.1",
-            "uri-js": "^4.2.1"
+            "uri-js": "^4.2.2"
           }
         },
         "arr-diff": {
@@ -11305,18 +11351,6 @@
         }
       }
     },
-    "webpack-log": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz",
-      "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.1.0",
-        "log-symbols": "^2.1.0",
-        "loglevelnext": "^1.0.1",
-        "uuid": "^3.1.0"
-      }
-    },
     "webpack-sources": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz",

+ 10 - 8
package.json

@@ -19,6 +19,8 @@
     "test": "jest",
     "build-viewer": "webpack build/node_modules/apps/viewer/index.js --mode development -o build/viewer/index.js",
     "watch-viewer": "webpack build/node_modules/apps/viewer/index.js -w --mode development -o build/viewer/index.js",
+    "build-canvas": "webpack build/node_modules/apps/canvas/index.js --mode development -o build/canvas/index.js",
+    "watch-canvas": "webpack build/node_modules/apps/canvas/index.js -w --mode development -o build/canvas/index.js",
     "model-server": "node build/node_modules/servers/model/server.js",
     "model-server-watch": "nodemon --watch build/node_modules build/node_modules/servers/model/server.js"
   },
@@ -74,34 +76,34 @@
     "@types/compression": "0.0.36",
     "@types/express": "^4.16.0",
     "@types/jest": "^23.3.1",
-    "@types/node": "^10.7.0",
+    "@types/node": "^10.7.1",
     "@types/node-fetch": "^2.1.2",
-    "@types/react": "^16.4.10",
+    "@types/react": "^16.4.11",
     "@types/react-dom": "^16.0.7",
     "benchmark": "^2.1.4",
     "cpx": "^1.5.0",
     "css-loader": "^1.0.0",
     "extra-watch-webpack-plugin": "^1.0.3",
-    "file-loader": "^1.1.11",
+    "file-loader": "^2.0.0",
     "glslify-import": "^3.1.0",
     "glslify-loader": "^1.0.2",
-    "graphql-code-generator": "^0.10.7",
-    "graphql-codegen-typescript-template": "^0.10.7",
+    "graphql-code-generator": "^0.11.0",
+    "graphql-codegen-typescript-template": "^0.11.0",
     "graphql-tag": "^2.9.2",
     "jest": "^23.5.0",
     "jest-raw-loader": "^1.0.1",
-    "mini-css-extract-plugin": "^0.4.1",
+    "mini-css-extract-plugin": "^0.4.2",
     "node-sass": "^4.9.3",
     "raw-loader": "^0.5.1",
     "resolve-url-loader": "^2.3.0",
     "sass-loader": "^7.1.0",
     "style-loader": "^0.22.1",
-    "ts-jest": "^23.1.3",
+    "ts-jest": "^23.1.4",
     "tslint": "^5.11.0",
     "typescript": "^3.0.1",
     "uglify-js": "^3.4.7",
     "util.promisify": "^1.0.0",
-    "webpack": "^4.16.5",
+    "webpack": "^4.17.1",
     "webpack-cli": "^3.1.0"
   },
   "dependencies": {

+ 15 - 0
src/apps/canvas/index.html

@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8" />
+        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+        <title>Mol* Canvas</title>
+    </head>
+    <body>
+        <div id="container" style="width:800px; height: 600px;">
+            <canvas id="canvas"></canvas>
+        </div>
+        <span id="info"></span>
+        <script type="text/javascript" src="./index.js"></script>
+    </body>
+</html>

+ 154 - 0
src/apps/canvas/index.ts

@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+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 { readUrlAs } from 'mol-util/read'
+import { Model, Format, Structure, StructureSymmetry } from 'mol-model/structure';
+import { CartoonRepresentation } from 'mol-geo/representation/structure/representation/cartoon';
+import { BallAndStickRepresentation } from 'mol-geo/representation/structure/representation/ball-and-stick';
+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/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';
+import { addSphere } from 'mol-geo/mesh/builder/sphere';
+import { Box } from 'mol-geo/primitive/box';
+
+const container = document.getElementById('container')
+if (!container) throw new Error('Can not find element with id "container".')
+
+const canvas = document.getElementById('canvas') as HTMLCanvasElement
+if (!canvas) throw new Error('Can not find element with id "canvas".')
+
+const info = document.getElementById('info') as HTMLCanvasElement
+if (!info) throw new Error('Can not find element with id "info".')
+
+const viewer = Viewer.create(canvas, container)
+viewer.animate()
+
+viewer.input.resize.subscribe(() => {
+    // do whatever appropriate
+})
+
+viewer.input.move.subscribe(({x, y, inside, buttons}) => {
+    if (!inside || buttons) return
+    const p = viewer.identify(x, y)
+    const loci = viewer.getLoci(p)
+
+    viewer.mark(EveryLoci, MarkerAction.RemoveHighlight)
+    viewer.mark(loci, MarkerAction.Highlight)
+
+    const label = labelFirst(loci)
+    info.innerText = `${label}`
+})
+
+
+// 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)
+    const comp = CIF.parse(data)
+    const parsed = await comp.run()
+    if (parsed.isError) throw parsed
+    return parsed.result.blocks[0]
+}
+
+async function getModelFromMmcif(cif: CifBlock) {
+    const models = await Model.create(Format.mmCIF(cif)).run()
+    return models[0]
+}
+
+async function getStructureFromModel(model: Model, assembly = '1') {
+    const assemblies = model.symmetry.assemblies
+    if (assemblies.length) {
+        return await StructureSymmetry.buildAssembly(Structure.ofModel(model), assembly).run()
+    } else {
+        return Structure.ofModel(model)
+    }
+}
+
+async function init() {
+    const cif = await getCifFromUrl('https://files.rcsb.org/download/1crn.cif')
+    const model = await getModelFromMmcif(cif)
+    const structure = await getStructureFromModel(model)
+
+    viewer.center(structure.boundary.sphere.center)
+
+    // cartoon for whole structure
+    const cartoonRepr = CartoonRepresentation()
+    await cartoonRepr.create(structure, {
+        colorTheme: { name: 'chain-id' },
+        sizeTheme: { name: 'uniform', value: 0.2 },
+        useFog: false // TODO fog not working properly
+    }).run()
+    viewer.add(cartoonRepr)
+
+    // create new structure via query
+    const q1 = Q.generators.atoms({
+        residueTest: qtx => SP.residue.label_seq_id(qtx.element) < 7
+    });
+    const newStructure = StructureSelection.unionStructure(await StructureQuery.run(q1, structure));
+
+    // ball+stick for new structure
+    const ballStickRepr = BallAndStickRepresentation()
+    await ballStickRepr.create(newStructure, {
+        colorTheme: { name: 'element-symbol' },
+        sizeTheme: { name: 'uniform', value: 0.1 },
+        useFog: false // TODO fog not working properly
+    }).run()
+    viewer.add(ballStickRepr)
+
+    // create a mesh
+    const meshBuilder = MeshBuilder.create(256, 128)
+    const colors: Color[] = []
+    const labels: string[] = []
+    // red sphere
+    meshBuilder.setGroup(0)
+    colors[0] = Color(0xFF2233)
+    labels[0] = 'red sphere'
+    addSphere(meshBuilder, Vec3.create(0, 0, 0), 4, 2)
+    // green cube
+    meshBuilder.setGroup(1)
+    colors[1] = Color(0x2233FF)
+    labels[1] = 'blue cube'
+    const t = Mat4.identity()
+    Mat4.fromTranslation(t, Vec3.create(10, 0, 0))
+    Mat4.scale(t, t, Vec3.create(3, 3, 3))
+    meshBuilder.add(t, Box())
+    const mesh = meshBuilder.getMesh()
+    // const mesh = getObjFromUrl('mesh.obj')
+
+    // create shape from mesh
+    const shape = Shape.create('myShape', mesh, colors, labels)
+
+    // 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)
+
+    // ensure the added representations get rendered, i.e. without mouse input
+    viewer.requestDraw()
+}
+
+init()

+ 0 - 0
src/apps/schema-generator/schema-from-mmcif-dic.ts → src/apps/schema-generator/schema-from-cif-dic.ts


+ 44 - 3
src/apps/schema-generator/util/cif-dic.ts

@@ -77,7 +77,7 @@ interface FrameData {
 }
 
 // get field from given or linked category
-function getField ( category: string, field: string, d: Data.CifFrame, ctx: FrameData): Data.CifField|undefined {
+function getField (category: string, field: string, d: Data.CifFrame, ctx: FrameData): Data.CifField|undefined {
     const { categories, links } = ctx
 
     const cat = d.categories[category]
@@ -130,6 +130,7 @@ function getSubCategory (d: Data.CifFrame, ctx: FrameData): string|undefined {
 function getDescription (d: Data.CifFrame, ctx: FrameData): string|undefined {
     const value = getField('item_description', 'description', d, ctx)
     if (value) {
+        // trim (after newlines) and remove references to square brackets
         return value.str(0).trim()
             .replace(/(\r\n|\r|\n)([ \t]+)/g, '\n')
             .replace(/(\[[1-3]\])+ element/, 'elements')
@@ -195,6 +196,40 @@ export function generateSchema (frames: CifFrame[]) {
     const links: FrameLinks = {}
     const ctx = { categories, links }
 
+    // get category metadata
+    frames.forEach(d => {
+        if (d.header[0] === '_') return
+        const categoryKeyNames = new Set<string>()
+        const categoryKey = d.categories['category_key']
+        if (categoryKey) {
+            const categoryKey_names = categoryKey.getField('name')
+            if (categoryKey_names) {
+                for (let i = 0, il = categoryKey_names.rowCount; i < il; ++i) {
+                    categoryKeyNames.add(categoryKey_names.str(i))
+                }
+            }
+        }
+        let description = ''
+        const category = d.categories['category']
+        if (category) {
+            const category_description = category.getField('description')
+            if (category_description) {
+                description = category_description.str(0).trim()
+                    .replace(/(\r\n|\r|\n)([ \t]+)/g, '\n') // remove padding after newlines
+            } else {
+                console.log(`no description given for category '${category}'`)
+            }
+        }
+        if (categoryKeyNames.size === 0) {
+            console.log(`no key given for category '${category}'`)
+        }
+        schema[d.header] = { description, key: categoryKeyNames, columns: {} }
+        // console.log('++++++++++++++++++++++++++++++++++++++++++')
+        // console.log('name', d.header)
+        // console.log('desc', description)
+        // console.log('key', categoryKeyNames)
+    })
+
     // build list of links between categories
     frames.forEach(d => {
         if (d.header[0] !== '_') return
@@ -216,6 +251,7 @@ export function generateSchema (frames: CifFrame[]) {
         }
     })
 
+    // get field data
     Object.keys(categories).forEach(fullName => {
         const d = categories[fullName]
         if (!d) {
@@ -226,10 +262,15 @@ export function generateSchema (frames: CifFrame[]) {
         const itemName = d.header.substring(d.header.indexOf('.') + 1)
         let fields: { [k: string]: Column }
         if (categoryName in schema) {
-            fields = schema[categoryName]
+            fields = schema[categoryName].columns
         } else {
+            console.log(`category '${categoryName}' has no metadata`)
             fields = {}
-            schema[categoryName] = fields
+            schema[categoryName] = {
+                description: '',
+                key: new Set(),
+                columns: fields
+            }
         }
 
         const description = getDescription(d, ctx) || ''

+ 42 - 19
src/apps/schema-generator/util/generate.ts

@@ -18,17 +18,7 @@ function header (name: string, info: string, importDatabasePath = 'mol-data/db')
 
 import { Database, Column } from '${importDatabasePath}'
 
-import Schema = Column.Schema
-
-const str = Schema.str;
-const int = Schema.int;
-const float = Schema.float;
-const coord = Schema.coord;
-
-const Aliased = Schema.Aliased;
-const Matrix = Schema.Matrix;
-const Vector = Schema.Vector;
-const List = Schema.List;`
+import Schema = Column.Schema`
 }
 
 function footer (name: string) {
@@ -37,6 +27,32 @@ export type ${name}_Schema = typeof ${name}_Schema;
 export interface ${name}_Database extends Database<${name}_Schema> {}`
 }
 
+function getTypeShorthands(schema: Database, fields?: Filter) {
+    const types = new Set<string>()
+    Object.keys(schema).forEach(table => {
+        if (fields && !fields[table]) return
+        const { columns} = schema[table]
+        Object.keys(columns).forEach(columnName => {
+            if (fields && !fields[table][columnName]) return
+            types.add(schema[table].columns[columnName].type)
+        })
+    })
+    const shorthands: string[] = []
+    types.forEach(type => {
+        switch (type) {
+            case 'str': shorthands.push('const str = Schema.str;'); break
+            case 'int': shorthands.push('const int = Schema.int;'); break
+            case 'float': shorthands.push('const float = Schema.float;'); break
+            case 'coord': shorthands.push('const coord = Schema.coord;'); break
+            case 'enum': shorthands.push('const Aliased = Schema.Aliased;'); break
+            case 'matrix': shorthands.push('const Matrix = Schema.Matrix;'); break
+            case 'vector': shorthands.push('const Vector = Schema.Vector;'); break
+            case 'list': shorthands.push('const List = Schema.List;'); break
+        }
+    })
+    return shorthands.join('\n')
+}
+
 function getTypeDef(c: Column): string {
     switch (c.type) {
         case 'str': return 'str'
@@ -63,27 +79,34 @@ function getTypeDef(c: Column): string {
 const reSafePropertyName = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/
 function safePropertyString(name: string) { return name.match(reSafePropertyName) ? name : `'${name}'` }
 
+function doc(description: string, spacesCount: number) {
+    const spaces = ' '.repeat(spacesCount)
+    return [
+        `${spaces}/**`,
+        `${indentString(description, 1, `${spaces} * `)}`.replace(/ +\n/g, '\n'),
+        `${spaces} */`
+    ].join('\n')
+}
+
 export function generate (name: string, info: string, schema: Database, fields?: Filter, importDatabasePath?: string) {
     const codeLines: string[] = []
 
     codeLines.push(`export const ${name}_Schema = {`)
     Object.keys(schema).forEach(table => {
         if (fields && !fields[table]) return
+        const { description, columns} = schema[table]
+        if (description) codeLines.push(doc(description, 4))
         codeLines.push(`    ${safePropertyString(table)}: {`)
-        const columns = schema[table]
         Object.keys(columns).forEach(columnName => {
             if (fields && !fields[table][columnName]) return
-            const typeDef = getTypeDef(columns[columnName])
-            if (columns[columnName].description) {
-                codeLines.push(`        /**`)
-                codeLines.push(`${indentString(columns[columnName].description, 1, '         * ')}`)
-                codeLines.push(`         */`)
-            }
+            const c = columns[columnName]
+            const typeDef = getTypeDef(c)
+            if (c.description) codeLines.push(doc(c.description, 8))
             codeLines.push(`        ${safePropertyString(columnName)}: ${typeDef},`)
         })
         codeLines.push('    },')
     })
     codeLines.push('}')
 
-    return `${header(name, info, importDatabasePath)}\n\n${codeLines.join('\n')}\n${footer(name)}`
+    return `${header(name, info, importDatabasePath)}\n\n${getTypeShorthands(schema, fields)}\n\n${codeLines.join('\n')}\n${footer(name)}`
 }

+ 5 - 1
src/apps/schema-generator/util/schema.ts

@@ -5,7 +5,11 @@
  */
 
 export interface Database { [ tableName: string ]: Table }
-export interface Table { [ columnName: string ]: Column }
+export interface Table {
+    description: string
+    key: Set<string>
+    columns: { [ columnName: string ]: Column }
+}
 export type Column = IntCol | StrCol | FloatCol | CoordCol | EnumCol | VectorCol | MatrixCol | ListCol
 
 type BaseCol = { description: string }

+ 1 - 1
src/apps/viewer/index.html

@@ -3,7 +3,7 @@
     <head>
         <meta charset="utf-8" />
         <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-        <title>Mol* Render Test</title>
+        <title>Mol* Viewer</title>
         <link href='./app.css', rel="stylesheet">
     </head>
     <body>

+ 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 })
         })
 

+ 89 - 0
src/mol-geo/mesh/builder/cylinder.ts

@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Vec3, Mat4 } from 'mol-math/linear-algebra';
+import { MeshBuilder } from '../mesh-builder';
+import { Primitive } from '../../primitive/primitive';
+import { Cylinder, CylinderProps } from '../../primitive/cylinder';
+
+const cylinderMap = new Map<string, Primitive>()
+const up = Vec3.create(0, 1, 0)
+
+const tmpCylinderDir = Vec3.zero()
+const tmpCylinderMatDir = Vec3.zero()
+const tmpCylinderCenter = Vec3.zero()
+const tmpCylinderMat = Mat4.zero()
+const tmpCylinderStart = Vec3.zero()
+const tmpUp = Vec3.zero()
+
+function setCylinderMat(m: Mat4, start: Vec3, dir: Vec3, length: number) {
+    Vec3.setMagnitude(tmpCylinderMatDir, dir, length / 2)
+    Vec3.add(tmpCylinderCenter, start, tmpCylinderMatDir)
+    // ensure the direction used to create the rotation is always pointing in the same
+    // direction so the triangles of adjacent cylinder will line up
+    Vec3.copy(tmpUp, up)
+    if (Vec3.dot(tmpCylinderMatDir, tmpUp) < 0) Vec3.scale(tmpUp, tmpUp, -1)
+    Vec3.makeRotation(m, tmpUp, tmpCylinderMatDir)
+    return Mat4.setTranslation(m, tmpCylinderCenter)
+}
+
+function getCylinder(props: CylinderProps) {
+    const key = JSON.stringify(props)
+    let cylinder = cylinderMap.get(key)
+    if (cylinder === undefined) {
+        cylinder = Cylinder(props)
+        cylinderMap.set(key, cylinder)
+    }
+    return cylinder
+}
+
+export function addCylinder(builder: MeshBuilder, start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps) {
+    const d = Vec3.distance(start, end) * lengthScale
+    props.height = d
+    Vec3.sub(tmpCylinderDir, end, start)
+    setCylinderMat(tmpCylinderMat, start, tmpCylinderDir, d)
+    builder.add(tmpCylinderMat, getCylinder(props))
+}
+
+export function addDoubleCylinder(builder: MeshBuilder, start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps) {
+    const d = Vec3.distance(start, end) * lengthScale
+    props.height = d
+    const cylinder = getCylinder(props)
+    Vec3.sub(tmpCylinderDir, end, start)
+    // positivly shifted cylinder
+    Vec3.add(tmpCylinderStart, start, shift)
+    setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d)
+    builder.add(tmpCylinderMat, cylinder)
+    // negativly shifted cylinder
+    Vec3.sub(tmpCylinderStart, start, shift)
+    setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d)
+    builder.add(tmpCylinderMat, cylinder)
+}
+
+export function addFixedCountDashedCylinder(builder: MeshBuilder, start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: CylinderProps) {
+    const s = Math.floor(segmentCount / 2)
+    const step = 1 / segmentCount
+
+    // automatically adjust length so links/bonds that are rendered as two half cylinders
+    // have evenly spaced dashed cylinders
+    if (lengthScale < 1) {
+        const bias = lengthScale / 2 / segmentCount
+        lengthScale += segmentCount % 2 === 1 ? bias : -bias
+    }
+
+    const d = Vec3.distance(start, end) * lengthScale
+    props.height = d * step
+    const cylinder = getCylinder(props)
+    Vec3.sub(tmpCylinderDir, end, start)
+
+    for (let j = 0; j < s; ++j) {
+        const f = step * (j * 2 + 1)
+        Vec3.setMagnitude(tmpCylinderDir, tmpCylinderDir, d * f)
+        Vec3.add(tmpCylinderStart, start, tmpCylinderDir)
+        setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d * step)
+        builder.add(tmpCylinderMat, cylinder)
+    }
+}

+ 5 - 4
src/mol-geo/primitive/sheet.ts → src/mol-geo/mesh/builder/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 { MeshBuilder } from '../mesh-builder';
 
 const tA = Vec3.zero()
 const tB = Vec3.zero()
@@ -25,8 +25,8 @@ const p2 = Vec3.zero()
 const p3 = Vec3.zero()
 const p4 = Vec3.zero()
 
-export function addSheet(controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean, state: MeshBuilderState) {
-    const { vertices, normals, indices } = state
+export function addSheet(builder: MeshBuilder, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean) {
+    const { currentGroup, vertices, normals, indices, groups } = builder.state
 
     let vertexCount = vertices.elementCount
     let offsetLength = 0
@@ -173,5 +173,6 @@ export function addSheet(controlPoints: ArrayLike<number>, normalVectors: ArrayL
         ChunkedArray.add3(indices, vertexCount, vertexCount + 3, vertexCount + 2);
     }
 
-    return (linearSegments + 1) * 8 + (startCap ? 4 : 0) + (endCap && arrowHeight === 0 ? 4 : 0)
+    const addedVertexCount = (linearSegments + 1) * 8 + (startCap ? 4 : 0) + (endCap && arrowHeight === 0 ? 4 : 0)
+    for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup)
 }

+ 30 - 0
src/mol-geo/mesh/builder/sphere.ts

@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Vec3, Mat4 } from 'mol-math/linear-algebra';
+import { MeshBuilder } from '../mesh-builder';
+import { Primitive } from '../../primitive/primitive';
+import { Sphere } from '../../primitive/sphere';
+
+const sphereMap = new Map<number, Primitive>()
+const tmpSphereMat = Mat4.identity()
+
+function setSphereMat(m: Mat4, center: Vec3, radius: number) {
+    return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius)
+}
+
+function getSphere(detail: number) {
+    let sphere = sphereMap.get(detail)
+    if (sphere === undefined) {
+        sphere = Sphere(detail)
+        sphereMap.set(detail, sphere)
+    }
+    return sphere
+}
+
+export function addSphere(builder: MeshBuilder, center: Vec3, radius: number, detail: number) {
+    builder.add(setSphereMat(tmpSphereMat, center, radius), getSphere(detail))
+}

+ 6 - 5
src/mol-geo/primitive/tube.ts → src/mol-geo/mesh/builder/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 { MeshBuilder } from '../mesh-builder';
 
 const normalVector = Vec3.zero()
 const binormalVector = Vec3.zero()
@@ -18,8 +18,8 @@ const b = Vec3.zero()
 const u = Vec3.zero()
 const v = Vec3.zero()
 
-export function addTube(controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean, state: MeshBuilderState) {
-    const { vertices, normals, indices } = state
+export function addTube(builder: MeshBuilder, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean) {
+    const { currentGroup, vertices, normals, indices, groups } = builder.state
 
     let vertexCount = vertices.elementCount
     const di = 1 / linearSegments
@@ -127,7 +127,7 @@ export function addTube(controlPoints: ArrayLike<number>, normalVectors: ArrayLi
 
         vertexCount = vertices.elementCount
         for (let i = 0; i < radialSegments; ++i) {
-            const t = 2 * Math.PI * i / radialSegments;
+            const t = 2 * Math.PI * i / radialSegments
 
             Vec3.copy(a, u)
             Vec3.copy(b, v)
@@ -150,5 +150,6 @@ export function addTube(controlPoints: ArrayLike<number>, normalVectors: ArrayLi
         }
     }
 
-    return (linearSegments + 1) * radialSegments + (startCap ? radialSegments + 1 : 0) + (endCap ? radialSegments + 1 : 0)
+    const addedVertexCount = (linearSegments + 1) * radialSegments + (startCap ? radialSegments + 1 : 0) + (endCap ? radialSegments + 1 : 0)
+    for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup)
 }

+ 81 - 0
src/mol-geo/mesh/mesh-builder.ts

@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ValueCell } from 'mol-util/value-cell'
+import { Vec3, Mat4, Mat3 } from 'mol-math/linear-algebra';
+import { ChunkedArray } from 'mol-data/util';
+import { Mesh } from './mesh';
+import { getNormalMatrix } from '../util';
+import { Primitive } from '../primitive/primitive';
+
+export interface MeshBuilderState {
+    readonly currentGroup: number
+    readonly vertices: ChunkedArray<number, 3>
+    readonly normals: ChunkedArray<number, 3>
+    readonly indices: ChunkedArray<number, 3>
+    readonly groups: ChunkedArray<number, 1>
+}
+
+export interface MeshBuilder {
+    state: MeshBuilderState
+    add(t: Mat4, primitive: Primitive): void
+    setGroup(id: number): void
+    getMesh(): Mesh
+}
+
+const tmpV = Vec3.zero()
+const tmpMat3 = Mat3.zero()
+
+export namespace MeshBuilder {
+    export function create(initialCount = 2048, chunkSize = 1024, mesh?: Mesh): MeshBuilder {
+        const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.vertexBuffer.ref.value : initialCount);
+        const normals = ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.normalBuffer.ref.value : initialCount);
+        const indices = ChunkedArray.create(Uint32Array, 3, chunkSize * 3, mesh ? mesh.indexBuffer.ref.value : initialCount * 3);
+        const groups = ChunkedArray.create(Float32Array, 1, chunkSize, mesh ? mesh.groupBuffer.ref.value : initialCount);
+
+        let currentGroup = -1
+
+        function add(t: Mat4, primitive: Primitive) {
+            const { vertices: va, normals: na, indices: ia } = primitive
+            const offset = vertices.elementCount
+            const n = getNormalMatrix(tmpMat3, t)
+            for (let i = 0, il = va.length; i < il; i += 3) {
+                // position
+                Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, va, i), t)
+                ChunkedArray.add3(vertices, tmpV[0], tmpV[1], tmpV[2]);
+                // normal
+                Vec3.transformMat3(tmpV, Vec3.fromArray(tmpV, na, i), n)
+                ChunkedArray.add3(normals, tmpV[0], tmpV[1], tmpV[2]);
+                // group
+                ChunkedArray.add(groups, currentGroup);
+            }
+            for (let i = 0, il = ia.length; i < il; i += 3) {
+                ChunkedArray.add3(indices, ia[i] + offset, ia[i + 1] + offset, ia[i + 2] + offset);
+            }
+        }
+
+        return {
+            state: { get currentGroup() { return currentGroup }, vertices, normals, indices, groups },
+            add,
+            setGroup: (group: number) => { currentGroup = group },
+            getMesh: () => {
+                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 gb = ChunkedArray.compact(groups, true) as Float32Array
+                return {
+                    vertexCount: vertices.elementCount,
+                    triangleCount: indices.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),
+                    groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
+                    normalsComputed: true,
+                }
+            }
+        }
+    }
+}

+ 8 - 18
src/mol-geo/shape/mesh.ts → src/mol-geo/mesh/mesh.ts

@@ -15,24 +15,18 @@ 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>,
+    readonly vertexBuffer: ValueCell<Float32Array>,
     /** Index buffer as array of vertex index triplets wrapped in a value cell */
-    indexBuffer: ValueCell<Uint32Array>,
+    readonly 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>,
+    readonly normalBuffer: ValueCell<Float32Array>,
+    /** Group buffer as array of group ids for each vertex wrapped in a value cell */
+    readonly 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,
         }
     }
 
@@ -92,7 +82,7 @@ export namespace Mesh {
 
             // console.log([normals[i], normals[i + 1], normals[i + 2]], [v[i], v[i + 1], v[i + 2]])
         }
-        surface.normalBuffer = ValueCell.update(surface.normalBuffer, normals);
+        ValueCell.update(surface.normalBuffer, normals);
         surface.normalsComputed = true;
     }
 

+ 2 - 2
src/mol-geo/primitive/box.ts

@@ -33,8 +33,8 @@ function createBox(perforated: boolean): Primitive {
     Vec3.set(b, points[2], points[3], -0.5)
     Vec3.set(c, points[4], points[5], -0.5)
     Vec3.set(d, points[6], points[7], -0.5)
-    builder.add(a, b, c)
-    if (!perforated) builder.add(c, d, a)
+    builder.add(c, b, a)
+    if (!perforated) builder.add(a, d, c)
     Vec3.set(a, points[0], points[1], 0.5)
     Vec3.set(b, points[2], points[3], 0.5)
     Vec3.set(c, points[4], points[5], 0.5)

+ 19 - 25
src/mol-geo/primitive/plane.ts

@@ -6,31 +6,25 @@ import { Primitive } from './primitive';
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-export const DefaultPlaneProps = {
-    width: 1,
-    height: 1
+const plane: Primitive = {
+    vertices: new Float32Array([
+        -0.5, 0.5, 0,
+        0.5, 0.5, 0,
+        -0.5, -0.5, 0,
+        0.5, -0.5, 0
+    ]),
+    normals: new Float32Array([
+        0, 0, 1,
+        0, 0, 1,
+        0, 0, 1,
+        0, 0, 1
+    ]),
+    indices: new Uint32Array([
+        0, 2, 1,
+        1, 2, 3
+    ])
 }
-export type PlaneProps = Partial<typeof DefaultPlaneProps>
 
-export function Plane(props?: PlaneProps): Primitive {
-    const { width, height } = { ...DefaultPlaneProps, ...props }
-
-    return {
-        vertices: new Float32Array([
-            -width / 2, height / 2, 0,
-            width / 2, height / 2, 0,
-            -width / 2, -height / 2, 0,
-            width / 2, -height / 2, 0
-        ]),
-        normals: new Float32Array([
-            0, 0, 1,
-            0, 0, 1,
-            0, 0, 1,
-            0, 0, 1
-        ]),
-        indices: new Uint32Array([
-            0, 2, 1,
-            1, 2, 3
-        ])
-    }
+export function Plane(): Primitive {
+    return plane
 }

+ 3 - 3
src/mol-geo/primitive/prism.ts

@@ -16,7 +16,7 @@ const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero(), d = Vec3.zero()
  */
 export function Prism(points: ArrayLike<number>): Primitive {
     const sideCount = points.length / 2
-    if (sideCount < 5) throw new Error('need at least 5 points to build a prism')
+    if (sideCount < 4) throw new Error('need at least 5 points to build a prism')
 
     const count = 4 * sideCount
     const builder = PrimitiveBuilder(count)
@@ -37,10 +37,10 @@ export function Prism(points: ArrayLike<number>): Primitive {
         const ni = (i + 1) % sideCount
         Vec3.set(a, points[i * 2], points[i * 2 + 1], -0.5)
         Vec3.set(b, points[ni * 2], points[ni * 2 + 1], -0.5)
-        builder.add(a, b, on)
+        builder.add(on, b, a)
         Vec3.set(a, points[i * 2], points[i * 2 + 1], 0.5)
         Vec3.set(b, points[ni * 2], points[ni * 2 + 1], 0.5)
-        builder.add(op, b, a)
+        builder.add(a, b, op)
     }
 
     return builder.getPrimitive()

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

@@ -12,9 +12,9 @@ const on = Vec3.create(0, 0, -0.5), op = Vec3.create(0, 0, 0.5)
 const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero(), d = Vec3.zero()
 
 /**
- * Create a pyramide with a poligonal base
+ * Create a pyramid with a poligonal base
  */
-export function Pyramide(points: ArrayLike<number>): Primitive {
+export function Pyramid(points: ArrayLike<number>): Primitive {
     const sideCount = points.length / 2
     const baseCount = sideCount === 3 ? 1 : sideCount === 4 ? 2 : sideCount
     const count = 2 * baseCount + 2 * sideCount
@@ -33,37 +33,37 @@ export function Pyramide(points: ArrayLike<number>): Primitive {
         Vec3.set(a, points[0], points[1], -0.5)
         Vec3.set(b, points[2], points[3], -0.5)
         Vec3.set(c, points[4], points[5], -0.5)
-        builder.add(a, b, c)
+        builder.add(c, b, a)
     } else if (sideCount === 4) {
         Vec3.set(a, points[0], points[1], -0.5)
         Vec3.set(b, points[2], points[3], -0.5)
         Vec3.set(c, points[4], points[5], -0.5)
         Vec3.set(d, points[6], points[7], -0.5)
-        builder.add(a, b, c)
-        builder.add(c, d, a)
+        builder.add(c, b, a)
+        builder.add(a, d, c)
     } else {
         for (let i = 0; i < sideCount; ++i) {
             const ni = (i + 1) % sideCount
             Vec3.set(a, points[i * 2], points[i * 2 + 1], -0.5)
             Vec3.set(b, points[ni * 2], points[ni * 2 + 1], -0.5)
-            builder.add(a, b, on)
+            builder.add(on, b, a)
         }
     }
 
     return builder.getPrimitive()
 }
 
-let octagonalPyramide: Primitive
-export function OctagonalPyramide() {
-    if (!octagonalPyramide) octagonalPyramide = Pyramide(polygon(8, true))
-    return octagonalPyramide
+let octagonalPyramid: Primitive
+export function OctagonalPyramid() {
+    if (!octagonalPyramid) octagonalPyramid = Pyramid(polygon(8, true))
+    return octagonalPyramid
 }
 
 //
 
-let perforatedOctagonalPyramide: Primitive
-export function PerforatedOctagonalPyramide() {
-    if (!perforatedOctagonalPyramide) {
+let perforatedOctagonalPyramid: Primitive
+export function PerforatedOctagonalPyramid() {
+    if (!perforatedOctagonalPyramid) {
         const points = polygon(8, true)
         const vertices = new Float32Array(8 * 3 + 6)
         for (let i = 0; i < 8; ++i) {
@@ -81,7 +81,7 @@ export function PerforatedOctagonalPyramide() {
             0, 1, 8,  1, 2, 8,  4, 5, 8,  5, 6, 8,
             2, 3, 9,  3, 4, 9,  6, 7, 9,  7, 0, 9
         ];
-        perforatedOctagonalPyramide = createPrimitive(vertices, indices)
+        perforatedOctagonalPyramid = createPrimitive(vertices, indices)
     }
-    return perforatedOctagonalPyramide
+    return perforatedOctagonalPyramid
 }

+ 2 - 8
src/mol-geo/primitive/sphere.ts

@@ -15,13 +15,7 @@ export function sphereVertexCount(detail: number) {
     return 10 * Math.pow(Math.pow(2, detail), 2) + 2
 }
 
-export const DefaultSphereProps = {
-    radius: 1,
-    detail: 0
-}
-export type SphereProps = Partial<typeof DefaultSphereProps>
-
 /** Create sphere by subdividing an icosahedron */
-export function Sphere(props?: SphereProps): Primitive {
-    return Polyhedron(vertices, indices, { ...DefaultSphereProps, ...props })
+export function Sphere(detail: number): Primitive {
+    return Polyhedron(vertices, indices, { detail, radius: 1 })
 }

+ 2 - 2
src/mol-geo/primitive/star.ts

@@ -46,9 +46,9 @@ export function Star(props?: StarProps): Primitive {
         Vec3.set(c, outerPoints[ni * 2], outerPoints[ni * 2 + 1], 0)
 
         builder.add(op, a, b)
-        builder.add(on, a, b)
+        builder.add(b, a, on)
         builder.add(op, b, c)
-        builder.add(on, b, c)
+        builder.add(c, b, on)
     }
 
     return builder.getPrimitive()

+ 2 - 2
src/mol-geo/primitive/wedge.ts

@@ -32,11 +32,11 @@ export function createWedge(): Primitive {
     Vec3.set(a, points[0], points[1], -0.5)
     Vec3.set(b, points[2], points[3], -0.5)
     Vec3.set(c, points[4], points[5], -0.5)
-    builder.add(a, b, c)
+    builder.add(c, b, a)
     Vec3.set(a, points[0], points[1], 0.5)
     Vec3.set(b, points[2], points[3], 0.5)
     Vec3.set(c, points[4], points[5], 0.5)
-    builder.add(c, b, a)
+    builder.add(a, b, c)
 
     return builder.getPrimitive()
 }

+ 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 { 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 handle general update
+            // TODO check shape.colors.ref.version
+        })
+    }
+
+    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 } = shape
+        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)

+ 2 - 1
src/mol-geo/representation/structure/representation/cartoon.ts

@@ -33,7 +33,8 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
     return {
         get renderObjects() {
             return [ ...traceRepr.renderObjects, ...gapRepr.renderObjects,
-                ...blockRepr.renderObjects, ...directionRepr.renderObjects ]
+                ...blockRepr.renderObjects // , ...directionRepr.renderObjects
+            ]
         },
         get props() {
             return { ...traceRepr.props, ...gapRepr.props, ...blockRepr.props }

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

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

@@ -7,16 +7,22 @@
 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';
+import { addSphere } from '../../../mesh/builder/sphere';
+import { Box, PerforatedBox } from '../../../primitive/box';
+import { OctagonalPyramid, PerforatedOctagonalPyramid } from '../../../primitive/pyramid';
+import { Star } from '../../../primitive/star';
+import { Octahedron, PerforatedOctahedron } from '../../../primitive/octahedron';
+import { DiamondPrism, PentagonalPrism, HexagonalPrism } from '../../../primitive/prism';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -25,6 +31,17 @@ const pd = Vec3.zero()
 const sideFactor = 1.75 * 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4)
 const radiusFactor = 1.75
 
+const box = Box()
+const perforatedBox = PerforatedBox()
+const octagonalPyramid = OctagonalPyramid()
+const perforatedOctagonalPyramid = PerforatedOctagonalPyramid()
+const star = Star({ outerRadius: 1, innerRadius: 0.5, thickness: 0.5, pointCount: 5 })
+const octahedron = Octahedron()
+const perforatedOctahedron = PerforatedOctahedron()
+const diamondPrism = DiamondPrism()
+const pentagonalPrism = PentagonalPrism()
+const hexagonalPrism = HexagonalPrism()
+
 async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Structure, props: CarbohydrateSymbolProps, mesh?: Mesh) {
     const builder = MeshBuilder.create(256, 128, mesh)
 
@@ -49,71 +66,72 @@ 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:
-                builder.addSphere(center, radius, detail)
+                addSphere(builder, center, radius, detail)
                 break;
             case SaccharideShapes.FilledCube:
                 Mat4.scaleUniformly(t, t, side)
-                builder.addBox(t)
+                builder.add(t, box)
                 break;
             case SaccharideShapes.CrossedCube:
                 Mat4.scaleUniformly(t, t, side)
-                builder.addPerforatedBox(t)
+                builder.add(t, perforatedBox)
                 Mat4.mul(t, t, Mat4.rotZ90X180)
-                builder.setId(i * 2 + 1)
-                builder.addPerforatedBox(t)
+                builder.setGroup(i * 2 + 1)
+                builder.add(t, perforatedBox)
                 break;
             case SaccharideShapes.FilledCone:
                 Mat4.scaleUniformly(t, t, side * 1.2)
-                builder.addOctagonalPyramid(t)
+                builder.add(t, octagonalPyramid)
                 break
             case SaccharideShapes.DevidedCone:
                 Mat4.scaleUniformly(t, t, side * 1.2)
-                builder.addPerforatedOctagonalPyramid(t)
+                builder.add(t, perforatedOctagonalPyramid)
                 Mat4.mul(t, t, Mat4.rotZ90)
-                builder.setId(i * 2 + 1)
-                builder.addPerforatedOctagonalPyramid(t)
+                builder.setGroup(i * 2 + 1)
+                builder.add(t, perforatedOctagonalPyramid)
                 break
             case SaccharideShapes.FlatBox:
                 Mat4.mul(t, t, Mat4.rotZY90)
                 Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2))
-                builder.addBox(t)
+                builder.add(t, box)
                 break
             case SaccharideShapes.FilledStar:
+                Mat4.scaleUniformly(t, t, side)
                 Mat4.mul(t, t, Mat4.rotZY90)
-                builder.addStar(t, { outerRadius: side, innerRadius: side / 2, thickness: side / 2, pointCount: 5 })
+                builder.add(t, star)
                 break
             case SaccharideShapes.FilledDiamond:
                 Mat4.mul(t, t, Mat4.rotZY90)
                 Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4))
-                builder.addOctahedron(t)
+                builder.add(t, octahedron)
                 break
             case SaccharideShapes.DividedDiamond:
                 Mat4.mul(t, t, Mat4.rotZY90)
                 Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4))
-                builder.addPerforatedOctahedron(t)
+                builder.add(t, perforatedOctahedron)
                 Mat4.mul(t, t, Mat4.rotY90)
-                builder.setId(i * 2 + 1)
-                builder.addPerforatedOctahedron(t)
+                builder.setGroup(i * 2 + 1)
+                builder.add(t, perforatedOctahedron)
                 break
             case SaccharideShapes.FlatDiamond:
                 Mat4.mul(t, t, Mat4.rotZY90)
                 Mat4.scale(t, t, Vec3.set(sVec, side, side / 2, side / 2))
-                builder.addDiamondPrism(t)
+                builder.add(t, diamondPrism)
                 break
             case SaccharideShapes.Pentagon:
                 Mat4.mul(t, t, Mat4.rotZY90)
                 Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2))
-                builder.addPentagonalPrism(t)
+                builder.add(t, pentagonalPrism)
                 break
             case SaccharideShapes.FlatHexagon:
             default:
                 Mat4.mul(t, t, Mat4.rotZYZ90)
                 Mat4.scale(t, t, Vec3.set(sVec, side / 1.5, side , side / 2))
-                builder.addHexagonalPrism(t)
+                builder.add(t, hexagonalPrism)
                 break
         }
     }
@@ -142,11 +160,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 +172,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
             )
         ])
     }

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

@@ -7,15 +7,16 @@
 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';
+import { addCylinder } from '../../../mesh/builder/cylinder';
+import { Box } from '../../../primitive/box';
 
 const p1 = Vec3.zero()
 const p2 = Vec3.zero()
@@ -29,7 +30,9 @@ const vC = Vec3.zero()
 const center = Vec3.zero()
 const t = Mat4.identity()
 const sVec = Vec3.zero()
+const box = Box()
 
+// 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,9 +91,9 @@ 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.addBox(t)
-                    builder.addCylinder(p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
+                    builder.setGroup(SortedArray.findPredecessorIndex(elements, idx6))
+                    builder.add(t, box)
+                    addCylinder(builder, p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
                 }
             }
 

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

@@ -7,16 +7,16 @@
 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';
 import { OrderedSet } from 'mol-data/int';
+import { addCylinder } from '../../../mesh/builder/cylinder';
 
 export interface PolymerBackboneCylinderProps {
     sizeTheme: SizeThemeProps
@@ -47,12 +47,12 @@ 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.addCylinder(pA, pB, 0.5, cylinderProps)
+        builder.setGroup(OrderedSet.findPredecessorIndex(elements, centerA.element))
+        addCylinder(builder, pA, pB, 0.5, cylinderProps)
 
         cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerB)
-        builder.setId(OrderedSet.findPredecessorIndex(elements, centerB.element))
-        builder.addCylinder(pB, pA, 0.5, cylinderProps)
+        builder.setGroup(OrderedSet.findPredecessorIndex(elements, centerB.element))
+        addCylinder(builder, pB, pA, 0.5, cylinderProps)
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {
             await ctx.update({ message: 'Backbone mesh', current: i, max: polymerElementCount });

+ 8 - 6
src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts

@@ -7,16 +7,16 @@
 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';
+import { Wedge } from '../../../primitive/wedge';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -28,6 +28,8 @@ const depthFactor = 4
 const widthFactor = 4
 const heightFactor = 6
 
+const wedge = Wedge()
+
 export interface PolymerDirectionWedgeProps {
     sizeTheme: SizeThemeProps
 }
@@ -49,7 +51,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)
@@ -72,7 +74,7 @@ async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit,
             Mat4.mul(t, t, Mat4.rotY90Z180)
             Mat4.scale(t, t, Vec3.set(sVec, height, width, depth))
             Mat4.setTranslation(t, v.p2)
-            builder.addWedge(t)
+            builder.add(t, wedge)
         }
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {

+ 11 - 10
src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts

@@ -7,15 +7,16 @@
 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';
+import { addSphere } from '../../../mesh/builder/sphere';
+import { addFixedCountDashedCylinder } from '../../../mesh/builder/cylinder';
 
 const segmentCount = 10
 
@@ -48,9 +49,9 @@ 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)
+            addSphere(builder, pA, 0.6, 0)
         } else {
             const elmA = elements[centerA.element]
             const elmB = elements[centerB.element]
@@ -59,13 +60,13 @@ async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, pro
 
             l.element = elmA
             cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(l)
-            builder.setId(centerA.element)
-            builder.addFixedCountDashedCylinder(pA, pB, 0.5, segmentCount, cylinderProps)
+            builder.setGroup(centerA.element)
+            addFixedCountDashedCylinder(builder, pA, pB, 0.5, segmentCount, cylinderProps)
 
             l.element = elmB
             cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(l)
-            builder.setId(centerB.element)
-            builder.addFixedCountDashedCylinder(pB, pA, 0.5, segmentCount, cylinderProps)
+            builder.setGroup(centerB.element)
+            addFixedCountDashedCylinder(builder, pB, pA, 0.5, segmentCount, cylinderProps)
         }
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {

+ 12 - 11
src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts

@@ -7,15 +7,16 @@
 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 { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { OrderedSet } from 'mol-data/int';
+import { addSheet } from '../../../mesh/builder/sheet';
+import { addTube } from '../../../mesh/builder/tube';
 
 export interface PolymerTraceMeshProps {
     sizeTheme: SizeThemeProps
@@ -44,12 +45,12 @@ 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 isNucleicType = isNucleic(v.moleculeType)
         const isSheet = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta)
         const isHelix = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Helix)
-        const tension = (isNucleic || isSheet) ? 0.5 : 0.9
+        const tension = (isNucleicType || isSheet) ? 0.5 : 0.9
 
         interpolateCurveSegment(state, v, tension)
 
@@ -58,18 +59,18 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, props: Po
         if (isSheet) {
             const height = width * aspectRatio
             const arrowHeight = v.secStrucChange ? height * arrowFactor : 0
-            builder.addSheet(curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, true, true)
+            addSheet(builder, curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, true, true)
         } else {
             let height: number
             if (isHelix) {
                 height = width * aspectRatio
-            } else if (isNucleic) {
+            } else if (isNucleicType) {
                 height = width * aspectRatio;
                 [width, height] = [height, width]
             } else {
                 height = width
             }
-            builder.addTube(curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true)
+            addTube(builder, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true)
         }
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {

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

@@ -8,14 +8,14 @@
 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 } from '../../../util';
+import { MeshProps, createMeshValues, createRenderableState, createIdentityTransform } from '../../../util';
 import { StructureProps } from '../..';
 import { createMarkers } from '../../../../util/marker-data';
 import { createMeshRenderObject } from 'mol-gl/render-object';
@@ -32,18 +32,12 @@ export function createTransforms({ units }: Unit.SymmetryGroup, transforms?: Val
     return transforms ? ValueCell.update(transforms, array) : ValueCell.create(array)
 }
 
-const identityTransform = new Float32Array(16)
-Mat4.toArray(Mat4.identity(), identityTransform, 0)
-export function createIdentityTransform(transforms?: ValueCell<Float32Array>) {
-    return transforms ? ValueCell.update(transforms, identityTransform) : ValueCell.create(identityTransform)
-}
-
 export function createColors(locationIt: LocationIterator, props: ColorThemeProps, colorData?: ColorData) {
     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)
     }
 }
@@ -52,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)
     }
 }
@@ -61,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),

+ 22 - 6
src/mol-geo/representation/structure/visual/util/element.ts

@@ -8,12 +8,14 @@ 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';
+import { addSphere } from '../../../../mesh/builder/sphere';
 
 export interface ElementSphereMeshProps {
     sizeTheme: SizeThemeProps,
@@ -38,8 +40,8 @@ export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, p
         l.element = elements[i]
         pos(elements[i], v)
 
-        meshBuilder.setId(i)
-        meshBuilder.addSphere(v, sizeTheme.size(l), detail)
+        meshBuilder.setGroup(i)
+        addSphere(meshBuilder, v, sizeTheme.size(l), detail)
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {
             await ctx.update({ message: 'Sphere mesh', current: i, max: elementCount });
@@ -74,11 +76,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)
+    }
 }

+ 41 - 9
src/mol-geo/representation/structure/visual/util/link.ts

@@ -6,12 +6,15 @@
 
 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';
+import { addFixedCountDashedCylinder, addCylinder, addDoubleCylinder } from '../../../../mesh/builder/cylinder';
 
 export const DefaultLinkCylinderProps = {
     ...DefaultMeshProps,
@@ -87,12 +90,12 @@ 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
+        if (LinkType.is(f, LinkType.Flag.MetallicCoordination) || LinkType.is(f, LinkType.Flag.Hydrogen)) {
+            // show metall coordinations and hydrogen bonds with dashed cylinders
             cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius / 3
-            meshBuilder.addFixedCountDashedCylinder(va, vb, 0.5, 7, cylinderProps)
+            addFixedCountDashedCylinder(meshBuilder, va, vb, 0.5, 7, cylinderProps)
         } else if (o === 2 || o === 3) {
             // show bonds with order 2 or 3 using 2 or 3 parallel cylinders
             const multiRadius = linkRadius * (linkScale / (0.5 * o))
@@ -103,11 +106,11 @@ export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: L
 
             cylinderProps.radiusTop = cylinderProps.radiusBottom = multiRadius
 
-            if (o === 3) meshBuilder.addCylinder(va, vb, 0.5, cylinderProps)
-            meshBuilder.addDoubleCylinder(va, vb, 0.5, vShift, cylinderProps)
+            if (o === 3) addCylinder(meshBuilder, va, vb, 0.5, cylinderProps)
+            addDoubleCylinder(meshBuilder, va, vb, 0.5, vShift, cylinderProps)
         } else {
             cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius
-            meshBuilder.addCylinder(va, vb, 0.5, cylinderProps)
+            addCylinder(meshBuilder, va, vb, 0.5, cylinderProps)
         }
 
         if (edgeIndex % 10000 === 0 && ctx.shouldUpdate) {
@@ -116,4 +119,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)
-    }
-}

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

@@ -10,6 +10,7 @@ import { MeshValues, RenderableState } from 'mol-gl/renderable';
 import { defaults } from 'mol-util';
 import { Structure } from 'mol-model/structure';
 import { fillSerial } from 'mol-util/array';
+import { Mat4 } from 'mol-math/linear-algebra';
 
 export const DefaultBaseProps = {
     alpha: 1,
@@ -28,14 +29,20 @@ export const DefaultMeshProps = {
 }
 export type MeshProps = typeof DefaultMeshProps
 
-type Counts = { drawCount: number, elementCount: number, instanceCount: number }
+const identityTransform = new Float32Array(16)
+Mat4.toArray(Mat4.identity(), identityTransform, 0)
+export function createIdentityTransform(transforms?: ValueCell<Float32Array>) {
+    return transforms ? ValueCell.update(transforms, identityTransform) : ValueCell.create(identityTransform)
+}
+
+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 - 279
src/mol-geo/shape/mesh-builder.ts

@@ -1,279 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { ValueCell } from 'mol-util/value-cell'
-import { Vec3, Mat4, Mat3 } from 'mol-math/linear-algebra';
-import { ChunkedArray } from 'mol-data/util';
-
-import { Plane, PlaneProps } from '../primitive/plane';
-import { Cylinder, CylinderProps } from '../primitive/cylinder';
-import { Sphere, SphereProps } from '../primitive/sphere';
-import { Mesh } from './mesh';
-import { getNormalMatrix } from '../util';
-import { addSheet } from '../primitive/sheet';
-import { addTube } from '../primitive/tube';
-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 { PerforatedBox, Box } from '../primitive/box';
-import { Wedge } from '../primitive/wedge';
-
-export interface MeshBuilderState {
-    vertices: ChunkedArray<number, 3>
-    normals: ChunkedArray<number, 3>
-    indices: ChunkedArray<number, 3>
-}
-
-export interface MeshBuilder {
-    add(t: Mat4, _vertices: ArrayLike<number>, _normals: ArrayLike<number>, _indices?: ArrayLike<number>): void
-    addBox(t: Mat4): void
-    addPerforatedBox(t: Mat4): void
-    addPlane(t: Mat4, props?: PlaneProps): void
-    addWedge(t: Mat4): void
-    addDiamondPrism(t: Mat4): void
-    addPentagonalPrism(t: Mat4): void
-    addHexagonalPrism(t: Mat4): void
-    addOctagonalPyramid(t: Mat4): void
-    addPerforatedOctagonalPyramid(t: Mat4): void
-    addStar(t: Mat4, props?: StarProps): void
-    addOctahedron(t: Mat4): void
-    addPerforatedOctahedron(t: Mat4): void
-    addCylinder(start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps): void
-    addDoubleCylinder(start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps): void
-    addFixedCountDashedCylinder(start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: CylinderProps): void
-    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
-    getMesh(): Mesh
-}
-
-const cylinderMap = new Map<string, Primitive>()
-const sphereMap = new Map<string, Primitive>()
-
-const up = Vec3.create(0, 1, 0)
-const tmpV = Vec3.zero()
-const tmpMat3 = Mat3.zero()
-
-const tmpCylinderDir = Vec3.zero()
-const tmpCylinderMatDir = Vec3.zero()
-const tmpCylinderCenter = Vec3.zero()
-const tmpCylinderMat = Mat4.zero()
-// const tmpCylinderMatTrans = Mat4.zero()
-const tmpCylinderStart = Vec3.zero()
-const tmpUp = Vec3.zero()
-
-function setCylinderMat(m: Mat4, start: Vec3, dir: Vec3, length: number) {
-    Vec3.setMagnitude(tmpCylinderMatDir, dir, length / 2)
-    Vec3.add(tmpCylinderCenter, start, tmpCylinderMatDir)
-    // ensure the direction used to create the rotation is always pointing in the same
-    // direction so the triangles of adjacent cylinder will line up
-    Vec3.copy(tmpUp, up)
-    if (Vec3.dot(tmpCylinderMatDir, tmpUp) < 0) Vec3.scale(tmpUp, tmpUp, -1)
-    Vec3.makeRotation(m, tmpUp, tmpCylinderMatDir)
-    return Mat4.setTranslation(m, tmpCylinderCenter)
-}
-
-function getCylinder(props: CylinderProps) {
-    const key = JSON.stringify(props)
-    let cylinder = cylinderMap.get(key)
-    if (cylinder === undefined) {
-        cylinder = Cylinder(props)
-        cylinderMap.set(key, cylinder)
-    }
-    return cylinder
-}
-
-const tmpSphereMat = Mat4.identity()
-
-function setSphereMat(m: Mat4, center: Vec3) {
-    return Mat4.setTranslation(m, center)
-}
-
-function getSphere(props: SphereProps) {
-    const key = JSON.stringify(props)
-    let sphere = sphereMap.get(key)
-    if (sphere === undefined) {
-        sphere = Sphere(props)
-        sphereMap.set(key, sphere)
-    }
-    return sphere
-}
-
-// TODO cache primitives based on props
-
-export namespace MeshBuilder {
-    export function create(initialCount = 2048, chunkSize = 1024, mesh?: Mesh): MeshBuilder {
-        const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.vertexBuffer.ref.value : initialCount);
-        const normals = ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.normalBuffer.ref.value : initialCount);
-        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);
-
-        let currentId = -1
-
-        function add(t: Mat4, _vertices: ArrayLike<number>, _normals: ArrayLike<number>, _indices: ArrayLike<number>) {
-            const { elementCount } = vertices
-            const n = getNormalMatrix(tmpMat3, t)
-            for (let i = 0, il = _vertices.length; i < il; i += 3) {
-                // position
-                Vec3.fromArray(tmpV, _vertices, i)
-                Vec3.transformMat4(tmpV, tmpV, t)
-                ChunkedArray.add3(vertices, tmpV[0], tmpV[1], tmpV[2]);
-                // normal
-                Vec3.fromArray(tmpV, _normals, i)
-                Vec3.transformMat3(tmpV, tmpV, n)
-                ChunkedArray.add3(normals, tmpV[0], tmpV[1], tmpV[2]);
-                // id
-                ChunkedArray.add(ids, currentId);
-            }
-            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);
-            }
-        }
-
-        return {
-            add,
-            addBox: (t: Mat4) => {
-                const { vertices, normals, indices } = Box()
-                add(t, vertices, normals, indices)
-            },
-            addPerforatedBox: (t: Mat4) => {
-                const { vertices, normals, indices } = PerforatedBox()
-                add(t, vertices, normals, indices)
-            },
-            addPlane: (t: Mat4, props?: PlaneProps) => {
-                const { vertices, normals, indices } = Plane(props)
-                add(t, vertices, normals, indices)
-            },
-            addWedge: (t: Mat4) => {
-                const { vertices, normals, indices } = Wedge()
-                add(t, vertices, normals, indices)
-            },
-            addDiamondPrism: (t: Mat4) => {
-                const { vertices, normals, indices } = DiamondPrism()
-                add(t, vertices, normals, indices)
-            },
-            addPentagonalPrism: (t: Mat4) => {
-                const { vertices, normals, indices } = PentagonalPrism()
-                add(t, vertices, normals, indices)
-            },
-            addHexagonalPrism: (t: Mat4) => {
-                const { vertices, normals, indices } = HexagonalPrism()
-                add(t, vertices, normals, indices)
-            },
-            addOctagonalPyramid: (t: Mat4) => {
-                const { vertices, normals, indices } = OctagonalPyramide()
-                add(t, vertices, normals, indices)
-            },
-            addPerforatedOctagonalPyramid: (t: Mat4) => {
-                const { vertices, normals, indices } = PerforatedOctagonalPyramide()
-                add(t, vertices, normals, indices)
-            },
-            addStar: (t: Mat4, props?: StarProps) => {
-                const { vertices, normals, indices } = Star(props)
-                add(t, vertices, normals, indices)
-            },
-            addOctahedron: (t: Mat4) => {
-                const { vertices, normals, indices } = Octahedron()
-                add(t, vertices, normals, indices)
-            },
-            addPerforatedOctahedron: (t: Mat4) => {
-                const { vertices, normals, indices } = PerforatedOctahedron()
-                add(t, vertices, normals, indices)
-            },
-            addCylinder: (start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps) => {
-                const d = Vec3.distance(start, end) * lengthScale
-                props.height = d
-                const { vertices, normals, indices } = getCylinder(props)
-                Vec3.sub(tmpCylinderDir, end, start)
-                setCylinderMat(tmpCylinderMat, start, tmpCylinderDir, d)
-                add(tmpCylinderMat, vertices, normals, indices)
-            },
-            addDoubleCylinder: (start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps) => {
-                const d = Vec3.distance(start, end) * lengthScale
-                props.height = d
-                const { vertices, normals, indices } = getCylinder(props)
-                Vec3.sub(tmpCylinderDir, end, start)
-                // positivly shifted cylinder
-                Vec3.add(tmpCylinderStart, start, shift)
-                setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d)
-                add(tmpCylinderMat, vertices, normals, indices)
-                // negativly shifted cylinder
-                Vec3.sub(tmpCylinderStart, start, shift)
-                setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d)
-                add(tmpCylinderMat, vertices, normals, indices)
-            },
-            addFixedCountDashedCylinder: (start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: CylinderProps) => {
-                const s = Math.floor(segmentCount / 2)
-                const step = 1 / segmentCount
-
-                // automatically adjust length so links/bonds that are rendered as two half cylinders
-                // have evenly spaced dashed cylinders
-                if (lengthScale < 1) {
-                    const bias = lengthScale / 2 / segmentCount
-                    lengthScale += segmentCount % 2 === 1 ? bias : -bias
-                }
-
-                const d = Vec3.distance(start, end) * lengthScale
-                props.height = d * step
-                const { vertices, normals, indices } = getCylinder(props)
-                Vec3.sub(tmpCylinderDir, end, start)
-
-                for (let j = 0; j < s; ++j) {
-                    const f = step * (j * 2 + 1)
-                    Vec3.setMagnitude(tmpCylinderDir, tmpCylinderDir, d * f)
-                    Vec3.add(tmpCylinderStart, start, tmpCylinderDir)
-                    setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d * step)
-                    add(tmpCylinderMat, vertices, normals, indices)
-                }
-            },
-            addSphere: (center: Vec3, radius: number, detail: number) => {
-                const { vertices, normals, indices } = getSphere({ radius, detail })
-                setSphereMat(tmpSphereMat, center)
-                add(tmpSphereMat, vertices, normals, indices)
-            },
-            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);
-            },
-            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);
-            },
-            setId: (id: number) => {
-                if (currentId !== id) {
-                    currentId = id
-                    ChunkedArray.add(offsets, vertices.elementCount)
-                }
-            },
-            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
-                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),
-                    normalsComputed: true,
-                    offsetsComputed: true,
-                }
-            }
-        }
-    }
-}

+ 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
 

+ 42 - 6
src/mol-io/reader/cif/schema/bird.ts

@@ -11,16 +11,15 @@ import { Database, Column } from 'mol-data/db'
 import Schema = Column.Schema
 
 const str = Schema.str;
-const int = Schema.int;
 const float = Schema.float;
-// const coord = Schema.coord;
-
 const Aliased = Schema.Aliased;
-// const Matrix = Schema.Matrix;
-// const Vector = Schema.Vector;
-// const List = Schema.List;
+const int = Schema.int;
 
 export const BIRD_Schema = {
+    /**
+     * Data items in the PDBX_REFERENCE_MOLECULE category record
+     * reference information about small polymer molecules.
+     */
     pdbx_reference_molecule: {
         /**
          * The value of _pdbx_reference_molecule.prd_id is the unique identifier
@@ -108,6 +107,10 @@ export const BIRD_Schema = {
          */
         replaced_by: str,
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_LIST category record
+     * the list of entities within each reference molecule.
+     */
     pdbx_reference_entity_list: {
         /**
          * The value of _pdbx_reference_entity_list.prd_id is a reference
@@ -132,6 +135,10 @@ export const BIRD_Schema = {
          */
         component_id: int,
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_NONPOLY category record
+     * the list of entities within each reference molecule.
+     */
     pdbx_reference_entity_nonpoly: {
         /**
          * The value of _pdbx_reference_entity_nonpoly.prd_id is a reference
@@ -153,6 +160,10 @@ export const BIRD_Schema = {
          */
         chem_comp_id: str,
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_LINK category give details about
+     * the linkages between entities within reference molecules.
+     */
     pdbx_reference_entity_link: {
         /**
          * The value of _pdbx_reference_entity_link.link_id uniquely identifies
@@ -248,6 +259,11 @@ export const BIRD_Schema = {
          */
         link_class: Aliased<'PP' | 'PN' | 'NP' | 'NN'>(str),
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_POLY_LINK category give details about
+     * polymer linkages including both standard and non-standard linkages between
+     * polymer componnents.
+     */
     pdbx_reference_entity_poly_link: {
         /**
          * The value of _pdbx_reference_entity_poly_link.link_id uniquely identifies
@@ -317,6 +333,11 @@ export const BIRD_Schema = {
          */
         value_order: Aliased<'sing' | 'doub' | 'trip' | 'quad' | 'arom' | 'poly' | 'delo' | 'pi'>(str),
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_POLY category record details about
+     * the polymer, such as the type of the polymer, the number of
+     * monomers and whether it has nonstandard features.
+     */
     pdbx_reference_entity_poly: {
         /**
          * The value of _pdbx_reference_entity_poly.prd_id is a reference
@@ -341,6 +362,10 @@ export const BIRD_Schema = {
          */
         db_name: str,
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_POLY_SEQ category specify the sequence
+     * of monomers in a polymer.
+     */
     pdbx_reference_entity_poly_seq: {
         /**
          * The value of _pdbx_reference_entity_poly_seq.prd_id is a reference
@@ -377,6 +402,9 @@ export const BIRD_Schema = {
          */
         hetero: Aliased<'Y' | 'N'>(str),
     },
+    /**
+     * Additional features associated with the reference entity.
+     */
     pdbx_reference_entity_sequence: {
         /**
          * The value of _pdbx_reference_entity_sequence.prd_id is a reference
@@ -401,6 +429,10 @@ export const BIRD_Schema = {
          */
         one_letter_codes: str,
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_SRC_NAT category record
+     * details of the source from which the entity was obtained.
+     */
     pdbx_reference_entity_src_nat: {
         /**
          * The value of _pdbx_reference_entity_src_nat.prd_id is a reference
@@ -434,6 +466,10 @@ export const BIRD_Schema = {
          */
         db_name: str,
     },
+    /**
+     * Data items in the PDBX_PRD_AUDIT category records
+     * the status and tracking information for this molecule.
+     */
     pdbx_prd_audit: {
         /**
          * This data item is a pointer to _pdbx_reference_molecule.prd_id in the

+ 34 - 6
src/mol-io/reader/cif/schema/ccd.ts

@@ -11,16 +11,22 @@ import { Database, Column } from 'mol-data/db'
 import Schema = Column.Schema
 
 const str = Schema.str;
-const int = Schema.int;
 const float = Schema.float;
-const coord = Schema.coord;
-
-const Aliased = Schema.Aliased;
-// const Matrix = Schema.Matrix;
-// const Vector = Schema.Vector;
 const List = Schema.List;
+const Aliased = Schema.Aliased;
+const int = Schema.int;
+const coord = Schema.coord;
 
 export const CCD_Schema = {
+    /**
+     * Data items in the CHEM_COMP category give details about each
+     * of the chemical components from which the relevant chemical
+     * structures can be constructed, such as name, mass or charge.
+     *
+     * The related categories CHEM_COMP_ATOM, CHEM_COMP_BOND,
+     * CHEM_COMP_ANGLE etc. describe the detailed geometry of these
+     * chemical components.
+     */
     chem_comp: {
         /**
          * The formula for the chemical component. Formulae are written
@@ -167,6 +173,14 @@ export const CCD_Schema = {
          */
         pdbx_processing_site: Aliased<'PDBE' | 'EBI' | 'PDBJ' | 'RCSB'>(str),
     },
+    /**
+     * Data items in the CHEM_COMP_ATOM category record details about
+     * the atoms in a chemical component. Specifying the atomic
+     * coordinates for the components in this category is an
+     * alternative to specifying the structure of the component
+     * via bonds, angles, planes etc. in the appropriate
+     * CHEM_COMP subcategories.
+     */
     chem_comp_atom: {
         /**
          * An alternative identifier for the atom. This data item would be
@@ -271,6 +285,12 @@ export const CCD_Schema = {
          */
         pdbx_leaving_atom_flag: Aliased<'Y' | 'N'>(str),
     },
+    /**
+     * Data items in the CHEM_COMP_BOND category record details about
+     * the bonds between atoms in a chemical component. Target values
+     * may be specified as bond orders, as a distance between the two
+     * atoms, or both.
+     */
     chem_comp_bond: {
         /**
          * The ID of the first of the two atoms that define the bond.
@@ -310,6 +330,10 @@ export const CCD_Schema = {
          */
         pdbx_aromatic_flag: Aliased<'Y' | 'N'>(str),
     },
+    /**
+     * Data items in the CHEM_COMP_DESCRIPTOR category provide
+     * string descriptors of component chemical structure.
+     */
     pdbx_chem_comp_descriptor: {
         /**
          * This data item is a pointer to _chem_comp.id in the CHEM_COMP
@@ -336,6 +360,10 @@ export const CCD_Schema = {
          */
         program_version: str,
     },
+    /**
+     * Data items in the CHEM_COMP_IDENTIFIER category provide
+     * identifiers for chemical components.
+     */
     pdbx_chem_comp_identifier: {
         /**
          * This data item is a pointer to _chem_comp.id in the CHEM_COMP

+ 411 - 25
src/mol-io/reader/cif/schema/mmcif.ts

@@ -14,13 +14,22 @@ const str = Schema.str;
 const int = Schema.int;
 const float = Schema.float;
 const coord = Schema.coord;
-
 const Aliased = Schema.Aliased;
 const Matrix = Schema.Matrix;
 const Vector = Schema.Vector;
 const List = Schema.List;
 
 export const mmCIF_Schema = {
+    /**
+     * Data items in the ATOM_SITE category record details about
+     * the atom sites in a macromolecular crystal structure, such as
+     * the positional coordinates, atomic displacement parameters,
+     * magnetic moments and directions.
+     *
+     * The data items for describing anisotropic atomic
+     * displacement factors are only used if the corresponding items
+     * are not given in the ATOM_SITE_ANISOTROP category.
+     */
     atom_site: {
         /**
          * An alternative identifier for _atom_site.label_asym_id that
@@ -204,6 +213,11 @@ export const mmCIF_Schema = {
          */
         ihm_model_id: int,
     },
+    /**
+     * Data items in the ATOM_SITES category record details about
+     * the crystallographic cell and cell transformations, which are
+     * common to all atom sites.
+     */
     atom_sites: {
         /**
          * This data item is a pointer to _entry.id in the ENTRY category.
@@ -236,6 +250,10 @@ export const mmCIF_Schema = {
          */
         fract_transf_vector: Vector(3),
     },
+    /**
+     * Data items in the CELL category record details about the
+     * crystallographic cell parameters.
+     */
     cell: {
         /**
          * Unit-cell angle alpha of the reported structure in degrees.
@@ -283,6 +301,15 @@ export const mmCIF_Schema = {
          */
         pdbx_unique_axis: str,
     },
+    /**
+     * Data items in the CHEM_COMP category give details about each
+     * of the chemical components from which the relevant chemical
+     * structures can be constructed, such as name, mass or charge.
+     *
+     * The related categories CHEM_COMP_ATOM, CHEM_COMP_BOND,
+     * CHEM_COMP_ANGLE etc. describe the detailed geometry of these
+     * chemical components.
+     */
     chem_comp: {
         /**
          * The formula for the chemical component. Formulae are written
@@ -344,6 +371,12 @@ export const mmCIF_Schema = {
          */
         pdbx_synonyms: List(';', x => x),
     },
+    /**
+     * Data items in the CHEM_COMP_BOND category record details about
+     * the bonds between atoms in a chemical component. Target values
+     * may be specified as bond orders, as a distance between the two
+     * atoms, or both.
+     */
     chem_comp_bond: {
         /**
          * The ID of the first of the two atoms that define the bond.
@@ -383,6 +416,37 @@ export const mmCIF_Schema = {
          */
         pdbx_aromatic_flag: Aliased<'Y' | 'N'>(str),
     },
+    /**
+     * Data items in the ENTITY category record details (such as
+     * chemical composition, name and source) about the molecular
+     * entities that are present in the crystallographic structure.
+     *
+     * Items in the various ENTITY subcategories provide a full
+     * chemical description of these molecular entities.
+     *
+     * Entities are of three types:  polymer, non-polymer and water.
+     * Note that the water category includes only water;  ordered
+     * solvent such as sulfate ion or acetone would be described as
+     * individual non-polymer entities.
+     *
+     * The ENTITY category is specific to macromolecular CIF
+     * applications and replaces the function of the CHEMICAL category
+     * in the CIF core.
+     *
+     * It is important to remember that the ENTITY data are not the
+     * result of the crystallographic experiment;  those results are
+     * represented by the ATOM_SITE data items. ENTITY data items
+     * describe the chemistry of the molecules under investigation
+     * and can most usefully be thought of as the ideal groups to which
+     * the structure is restrained or constrained during refinement.
+     *
+     * It is also important to remember that entities do not correspond
+     * directly to the enumeration of the contents of the asymmetric
+     * unit. Entities are described only once, even in those structures
+     * that contain multiple observations of an entity. The
+     * STRUCT_ASYM data items, which reference the entity list,
+     * describe and label the contents of the asymmetric unit.
+     */
     entity: {
         /**
          * A description of special aspects of the entity.
@@ -446,6 +510,11 @@ export const mmCIF_Schema = {
          */
         pdbx_ec: List(',', x => x),
     },
+    /**
+     * Data items in the ENTITY_POLY category record details about the
+     * polymer, such as the type of the polymer, the number of
+     * monomers and whether it has nonstandard features.
+     */
     entity_poly: {
         /**
          * This data item is a pointer to _entity.id in the ENTITY category.
@@ -511,6 +580,14 @@ export const mmCIF_Schema = {
          */
         pdbx_target_identifier: str,
     },
+    /**
+     * Data items in the ENTITY_POLY_SEQ category specify the sequence
+     * of monomers in a polymer. Allowance is made for the possibility
+     * of microheterogeneity in a sample by allowing a given sequence
+     * number to be correlated with more than one monomer ID. The
+     * corresponding ATOM_SITE entries should reflect this
+     * heterogeneity.
+     */
     entity_poly_seq: {
         /**
          * This data item is a pointer to _entity.id in the ENTITY category.
@@ -535,6 +612,12 @@ export const mmCIF_Schema = {
          */
         num: int,
     },
+    /**
+     * There is only one item in the ENTRY category, _entry.id. This
+     * data item gives a name to this entry and is indirectly a key to
+     * the categories (such as CELL, GEOM, EXPTL) that describe
+     * information pertinent to the entire data block.
+     */
     entry: {
         /**
          * The value of _entry.id identifies the data block.
@@ -544,6 +627,11 @@ export const mmCIF_Schema = {
          */
         id: str,
     },
+    /**
+     * Data items in the EXPTL category record details about the
+     * experimental work prior to the intensity measurements and
+     * details about the absorption-correction technique employed.
+     */
     exptl: {
         /**
          * This data item is a pointer to _entry.id in the ENTRY category.
@@ -554,6 +642,10 @@ export const mmCIF_Schema = {
          */
         method: Aliased<'X-RAY DIFFRACTION' | 'NEUTRON DIFFRACTION' | 'FIBER DIFFRACTION' | 'ELECTRON CRYSTALLOGRAPHY' | 'ELECTRON MICROSCOPY' | 'SOLUTION NMR' | 'SOLID-STATE NMR' | 'SOLUTION SCATTERING' | 'POWDER DIFFRACTION' | 'INFRARED SPECTROSCOPY' | 'EPR' | 'FLUORESCENCE TRANSFER' | 'THEORETICAL MODEL'>(str),
     },
+    /**
+     * Data items in the STRUCT category record details about the
+     * description of the crystallographic structure.
+     */
     struct: {
         /**
          * This data item is a pointer to _entry.id in the ENTRY category.
@@ -566,6 +658,10 @@ export const mmCIF_Schema = {
          */
         title: str,
     },
+    /**
+     * Data items in the STRUCT_ASYM category record details about the
+     * structural elements in the asymmetric unit.
+     */
     struct_asym: {
         /**
          * A description of special aspects of this portion of the contents
@@ -594,6 +690,13 @@ export const mmCIF_Schema = {
          */
         pdbx_blank_PDB_chainid_flag: Aliased<'Y' | 'N'>(str),
     },
+    /**
+     * Data items in the STRUCT_CONF category record details about
+     * the backbone conformation of a segment of polymer.
+     *
+     * Data items in the STRUCT_CONF_TYPE category define the
+     * criteria used to identify the backbone conformations.
+     */
     struct_conf: {
         /**
          * A component of the identifier for the residue at which the
@@ -734,6 +837,14 @@ export const mmCIF_Schema = {
          */
         pdbx_PDB_helix_id: str,
     },
+    /**
+     * Data items in the STRUCT_CONN category record details about
+     * the connections between portions of the structure. These can be
+     * hydrogen bonds, salt bridges, disulfide bridges and so on.
+     *
+     * The STRUCT_CONN_TYPE records define the criteria used to
+     * identify these connections.
+     */
     struct_conn: {
         /**
          * This data item is a pointer to _struct_conn_type.id in the
@@ -961,6 +1072,11 @@ export const mmCIF_Schema = {
          */
         pdbx_value_order: Aliased<'sing' | 'doub' | 'trip' | 'quad'>(str),
     },
+    /**
+     * Data items in the STRUCT_CONN_TYPE category record details
+     * about the criteria used to identify interactions between
+     * portions of the structure.
+     */
     struct_conn_type: {
         /**
          * The criteria used to define the interaction.
@@ -976,6 +1092,10 @@ export const mmCIF_Schema = {
          */
         reference: str,
     },
+    /**
+     * Data items in the STRUCT_KEYWORDS category specify keywords
+     * that describe the chemical structure in this entry.
+     */
     struct_keywords: {
         /**
          * This data item is a pointer to _entry.id in the ENTRY category.
@@ -990,6 +1110,14 @@ export const mmCIF_Schema = {
          */
         pdbx_keywords: str,
     },
+    /**
+     * Data items in the STRUCT_NCS_OPER category describe the
+     * noncrystallographic symmetry operations.
+     *
+     * Each operator is specified as a matrix and a subsequent
+     * translation vector. Operators need not represent proper
+     * rotations.
+     */
     struct_ncs_oper: {
         /**
          * A code to indicate whether this operator describes a
@@ -1024,6 +1152,13 @@ export const mmCIF_Schema = {
          */
         vector: Vector(3),
     },
+    /**
+     * Data items in the STRUCT_SHEET_RANGE category record details
+     * about the residue ranges that form a beta-sheet. Residues are
+     * included in a range if they made beta-sheet-type hydrogen-bonding
+     * interactions with at least one adjacent strand and if there are
+     * at least two residues in the range.
+     */
     struct_sheet_range: {
         /**
          * A component of the identifier for the residue at which the
@@ -1145,6 +1280,12 @@ export const mmCIF_Schema = {
          */
         pdbx_end_PDB_ins_code: str,
     },
+    /**
+     * Data items in the STRUCT_SITE category record details about
+     * portions of the structure that contribute to structurally
+     * relevant sites (e.g. active sites, substrate-binding subsites,
+     * metal-coordination sites).
+     */
     struct_site: {
         /**
          * A description of special aspects of the site.
@@ -1192,6 +1333,11 @@ export const mmCIF_Schema = {
          */
         pdbx_auth_ins_code: str,
     },
+    /**
+     * Data items in the STRUCT_SITE_GEN category record details about
+     * the generation of portions of the structure that contribute to
+     * structurally relevant sites.
+     */
     struct_site_gen: {
         /**
          * A description of special aspects of the symmetry generation of
@@ -1282,6 +1428,10 @@ export const mmCIF_Schema = {
          */
         pdbx_num_res: int,
     },
+    /**
+     * Data items in the SYMMETRY category record details about the
+     * space-group symmetry.
+     */
     symmetry: {
         /**
          * This data item is a pointer to _entry.id in the ENTRY category.
@@ -1320,30 +1470,11 @@ export const mmCIF_Schema = {
          */
         'space_group_name_H-M': str,
     },
-    pdbx_struct_assembly: {
-        /**
-         * Provides details of the method used to determine or
-         * compute the assembly.
-         */
-        method_details: str,
-        /**
-         * Provides the details of the oligomeric state of the assembly.
-         */
-        oligomeric_details: str,
-        /**
-         * The number of polymer molecules in the assembly.
-         */
-        oligomeric_count: int,
-        /**
-         * A description of special aspects of the macromolecular assembly.
-         */
-        details: str,
-        /**
-         * The value of _pdbx_struct_assembly.id must uniquely identify a record in
-         * the PDBX_STRUCT_ASSEMBLY list.
-         */
-        id: str,
-    },
+    /**
+     * Data items in the PDBX_STRUCT_MOD_RESIDUE category list the
+     * modified polymer components in the entry and provide some
+     * details describing the nature of the modification.
+     */
     pdbx_struct_mod_residue: {
         /**
          * The value of _pdbx_struct_mod_residue.id must uniquely identify
@@ -1410,6 +1541,11 @@ export const mmCIF_Schema = {
          */
         details: str,
     },
+    /**
+     * Data items in the PDBX_STRUCT_OPER_LIST category describe
+     * Cartesian rotation and translation operations required to
+     * generate or transform the coordinates deposited with this entry.
+     */
     pdbx_struct_oper_list: {
         /**
          * This identifier code must uniquely identify a
@@ -1439,6 +1575,40 @@ export const mmCIF_Schema = {
          */
         vector: Vector(3),
     },
+    /**
+     * Data items in the PDBX_STRUCT_ASSEMBLY category record details about
+     * the structural elements that form macromolecular assemblies.
+     */
+    pdbx_struct_assembly: {
+        /**
+         * Provides details of the method used to determine or
+         * compute the assembly.
+         */
+        method_details: str,
+        /**
+         * Provides the details of the oligomeric state of the assembly.
+         */
+        oligomeric_details: str,
+        /**
+         * The number of polymer molecules in the assembly.
+         */
+        oligomeric_count: int,
+        /**
+         * A description of special aspects of the macromolecular assembly.
+         */
+        details: str,
+        /**
+         * The value of _pdbx_struct_assembly.id must uniquely identify a record in
+         * the PDBX_STRUCT_ASSEMBLY list.
+         */
+        id: str,
+    },
+    /**
+     * Data items in the PDBX_STRUCT_ASSEMBLY_GEN category record details about
+     * the generation of each macromolecular assemblies. The PDBX_STRUCT_ASSEMBLY_GEN
+     * data items provide the specifications of the components that
+     * constitute that assembly in terms of cartesian transformations.
+     */
     pdbx_struct_assembly_gen: {
         /**
          * This data item is a pointer to _struct_asym.id in
@@ -1468,6 +1638,10 @@ export const mmCIF_Schema = {
          */
         oper_expression: str,
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_LIST category record
+     * the list of entities within each reference molecule.
+     */
     pdbx_reference_entity_list: {
         /**
          * The value of _pdbx_reference_entity_list.prd_id is a reference
@@ -1492,6 +1666,10 @@ export const mmCIF_Schema = {
          */
         component_id: int,
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_LINK category give details about
+     * the linkages between entities within reference molecules.
+     */
     pdbx_reference_entity_link: {
         /**
          * The value of _pdbx_reference_entity_link.link_id uniquely identifies
@@ -1587,6 +1765,11 @@ export const mmCIF_Schema = {
          */
         link_class: Aliased<'PP' | 'PN' | 'NP' | 'NN'>(str),
     },
+    /**
+     * Data items in the PDBX_REFERENCE_ENTITY_POLY_LINK category give details about
+     * polymer linkages including both standard and non-standard linkages between
+     * polymer componnents.
+     */
     pdbx_reference_entity_poly_link: {
         /**
          * The value of _pdbx_reference_entity_poly_link.link_id uniquely identifies
@@ -1656,6 +1839,10 @@ export const mmCIF_Schema = {
          */
         value_order: Aliased<'sing' | 'doub' | 'trip' | 'quad' | 'arom' | 'poly' | 'delo' | 'pi'>(str),
     },
+    /**
+     * Data items in the PDBX_MOLECULE category identify reference molecules
+     * within a PDB entry.
+     */
     pdbx_molecule: {
         /**
          * The value of _pdbx_molecule.prd_id is the PDB accession code for this
@@ -1672,6 +1859,10 @@ export const mmCIF_Schema = {
          */
         asym_id: str,
     },
+    /**
+     * Data items in the PDBX_MOLECULE_FEATURES category record features of molecules
+     * within a PDB entry.
+     */
     pdbx_molecule_features: {
         /**
          * The value of _pdbx_molecule_features.prd_id is the PDB accession code for this
@@ -1695,6 +1886,11 @@ export const mmCIF_Schema = {
          */
         details: str,
     },
+    /**
+     * Data items in the IHM_STARTING_MODEL_DETAILS category records the
+     * details about structural models used as starting inputs in
+     * the integrative model building process.
+     */
     ihm_starting_model_details: {
         /**
          * A unique identifier for the starting structural model.
@@ -1750,6 +1946,11 @@ export const mmCIF_Schema = {
          */
         dataset_list_id: int,
     },
+    /**
+     * Data items in the IHM_STARTING_COMPARATIVE_MODELS category records
+     * additional details about comparative models used as starting inputs in
+     * the integrative model building process.
+     */
     ihm_starting_comparative_models: {
         /**
          * A unique identifier for the starting comparative model.
@@ -1805,6 +2006,15 @@ export const mmCIF_Schema = {
          */
         alignment_file_id: int,
     },
+    /**
+     * Data items in the IHM_STARTING_MODEL_SEQ_DIF category provide a
+     * mechanism for indicating and annotating point differences
+     * between the sequence of the entity or biological unit described
+     * in the data block and the sequence of the starting model used in
+     * the integrative modeling referenced from a database. The point
+     * differences may be due to point mutations introduced in the
+     * starting model or the presence of modified amino acid residues.
+     */
     ihm_starting_model_seq_dif: {
         /**
          * A unique identifier for the entry.
@@ -1857,6 +2067,12 @@ export const mmCIF_Schema = {
          */
         details: str,
     },
+    /**
+     * Data items in the IHM_MODEL_REPRESENTATION category records the
+     * details about the architecture and representation of structural
+     * models created by the integrative model building tasks. This
+     * category handles the multi-scale model representation, if employed.
+     */
     ihm_model_representation: {
         /**
          * A unique identifier for the model details record.
@@ -1918,6 +2134,11 @@ export const mmCIF_Schema = {
          */
         model_object_count: int,
     },
+    /**
+     * Data items in the IHM_STRUCT_ASSEMBLY category records the
+     * details of the structural assemblies and used in the
+     * IHM modeling.
+     */
     ihm_struct_assembly: {
         /**
          * A unique identifier for the structural assembly description.
@@ -1968,6 +2189,10 @@ export const mmCIF_Schema = {
          */
         seq_id_end: int,
     },
+    /**
+     * Data items in the IHM_STRUCT_ASSEMBLY_DETAILS category provides
+     * additional details regarding the structure assembly.
+     */
     ihm_struct_assembly_details: {
         /**
          * A unique identifier for the structural assembly.
@@ -1982,6 +2207,10 @@ export const mmCIF_Schema = {
          */
         assembly_description: str,
     },
+    /**
+     * Data items in the IHM_MODELING_PROTOCOL category records the
+     * step-wise details of the integrative modeling workflow.
+     */
     ihm_modeling_protocol: {
         /**
          * A unique identifier for the modeling protocol/step combination.
@@ -2049,6 +2278,10 @@ export const mmCIF_Schema = {
          */
         ordered_flag: Aliased<'YES' | 'NO'>(str),
     },
+    /**
+     * Data items in the IHM_MULTI_STATE_MODELING category records the
+     * details of the multi-state modeling protocol, if applicable.
+     */
     ihm_multi_state_modeling: {
         /**
          * A unique identifier for the multiple states being described.
@@ -2094,6 +2327,11 @@ export const mmCIF_Schema = {
          */
         details: str,
     },
+    /**
+     * Data items in the IHM_MODELING_POST_PROCESS category records
+     * the details of the post processing of the models/results of
+     * the modeling protocol.
+     */
     ihm_modeling_post_process: {
         /**
          * A unique identifier for the post modeling analysis/step combination.
@@ -2133,6 +2371,11 @@ export const mmCIF_Schema = {
          */
         num_models_end: int,
     },
+    /**
+     * Data items in the IHM_ENSEMBLE_INFO category records the
+     * details of the model clusters or ensembles obtained after
+     * sampling.
+     */
     ihm_ensemble_info: {
         /**
          * A unique id for the ensemble.
@@ -2193,6 +2436,10 @@ export const mmCIF_Schema = {
          */
         ensemble_file_id: int,
     },
+    /**
+     * Data items in the IHM_MODEL_LIST category record the
+     * details of the models being deposited.
+     */
     ihm_model_list: {
         /**
          * A unique identifier for the model / model group combination.
@@ -2246,6 +2493,10 @@ export const mmCIF_Schema = {
          */
         representation_id: int,
     },
+    /**
+     * Data items in the IHM_MODEL_REPRESENTATIVE category record the
+     * details of the representative model in an ensemble or cluster.
+     */
     ihm_model_representative: {
         /**
          * A unique identifier for the representative of the model group.
@@ -2268,6 +2519,15 @@ export const mmCIF_Schema = {
          */
         selection_criteria: Aliased<'medoid' | 'closest to the average' | 'lowest energy' | 'target function' | 'fewest violations' | 'minimized average structure' | 'best scoring model' | 'centroid' | 'other selction criteria'>(str),
     },
+    /**
+     * Category holds the list of all datasets used in the IHM modeling.
+     * These can be datasets archived in other related databases such as
+     * BMRB, EMDB, EMPIAR, SASBDB, PRIDE etc., or can be hosted in other
+     * places such as the authors website, github etc. These datasets are
+     * elaborated in detail in the IHM_DATASET_RELATED_DB_REFERENCE and/or
+     * the IHM_DATASET_EXTERNAL_REFERENCE categories. This category
+     * holds the list of all datasets used.
+     */
     ihm_dataset_list: {
         /**
          * A unique identifier for the dataset.
@@ -2283,6 +2543,9 @@ export const mmCIF_Schema = {
          */
         database_hosted: Aliased<'YES' | 'NO'>(str),
     },
+    /**
+     * Category provides a mechanism to group datasets.
+     */
     ihm_dataset_group: {
         /**
          * A unique identifier for the entry.
@@ -2298,6 +2561,9 @@ export const mmCIF_Schema = {
          */
         dataset_list_id: int,
     },
+    /**
+     * Category holds information about related datasets, where one is derived from the other.
+     */
     ihm_related_datasets: {
         /**
          * A unique identifier for the entry.
@@ -2316,6 +2582,11 @@ export const mmCIF_Schema = {
          */
         dataset_list_id_primary: int,
     },
+    /**
+     * Category holds information related to data sources for the entry.
+     * These can be datasets archived in other related databases such as
+     * BMRB, EMDB, EMPIAR, SASBDB, PRIDE etc.
+     */
     ihm_dataset_related_db_reference: {
         /**
          * A unique identifier for the related database entry.
@@ -2344,6 +2615,18 @@ export const mmCIF_Schema = {
          */
         details: str,
     },
+    /**
+     * Category holds links to other external data sources for the I/H model entry.
+     * Input datasets held in other databases such as EMDB, BMRB, SASBDB etc.
+     * are referenced in the IHM_DATASET_RELATED_DB_REFERENCE category.
+     * This data category, along with IHM_EXTERNAL_FILES category, holds information
+     * regarding other non-database external data sources, such as  DOIs (digital
+     * object identifiers) or supplementary files stored locally. The DOIs can either
+     * lead to the external data file(s) directly (as in case of DOIs provided by the PDB)
+     * or might lead to an HTML landing page (as provided by Zenodo). In the latter case,
+     * additional URL (Uniform Resource Locator) information is required to retrieve
+     * the external data file(s).
+     */
     ihm_external_reference_info: {
         /**
          * A unique identifier for the external reference.
@@ -2376,6 +2659,12 @@ export const mmCIF_Schema = {
          */
         associated_url: str,
     },
+    /**
+     * Category provides details regarding external files. The IHM_EXTERNAL_REFERENCE_INFO
+     * category captures the top-level details regarding external data sources.
+     * This category captures the specific details regarding externally stored files
+     * related to the particular I/H model entry.
+     */
     ihm_external_files: {
         /**
          * A unique identifier for each external file.
@@ -2409,6 +2698,10 @@ export const mmCIF_Schema = {
          */
         details: str,
     },
+    /**
+     * Category provides additional details regarding input data hosted externally
+     * at other resources.
+     */
     ihm_dataset_external_reference: {
         /**
          * A unique identifier for the external data.
@@ -2427,6 +2720,13 @@ export const mmCIF_Schema = {
          */
         file_id: int,
     },
+    /**
+     * Data items in the IHM_LOCALIZATION_DENSITY_FILES category records the
+     * details of files that provide information regarding localization densities
+     * of ensembles. These may be stored externally as local files or linked via
+     * DOI and can be in any accepted format that provides volume information
+     * (CCP4, MRC, etc.).
+     */
     ihm_localization_density_files: {
         /**
          * A unique identifier.
@@ -2464,6 +2764,14 @@ export const mmCIF_Schema = {
          */
         asym_id: str,
     },
+    /**
+     * Data items in the IHM_PREDICTED_CONTACT_RESTRAINT category records the
+     * list of predicted contacts used in the integrative modeling experiment.
+     * This has been adapted from the widely used CASP RR format
+     * (http://www.predictioncenter.org/casp8/index.cgi?page=format#RR).
+     * These contacts may be derived from various computational tools.
+     * The software information can be provided in the SOFTWARE category.
+     */
     ihm_predicted_contact_restraint: {
         /**
          * A unique identifier for the predicted contact restraint.
@@ -2563,6 +2871,11 @@ export const mmCIF_Schema = {
          */
         software_id: int,
     },
+    /**
+     * Data items in the IHM_CROSS_LINK_LIST category records the
+     * list of spatial restraints derived from chemical crosslinking
+     * experiment.
+     */
     ihm_cross_link_list: {
         /**
          * A unique identifier for the cross link restraint.
@@ -2628,6 +2941,12 @@ export const mmCIF_Schema = {
          */
         dataset_list_id: int,
     },
+    /**
+     * Data items in the IHM_CROSS_LINK_RESTRAINT category enumerates the
+     * implementation details of the chemical crosslinking restraints in
+     * the integrative modeling. This category holds the details of how
+     * the experimentally derived crosslinks are applied in the modeling.
+     */
     ihm_cross_link_restraint: {
         /**
          * A unique identifier for the cross link record.
@@ -2740,6 +3059,10 @@ export const mmCIF_Schema = {
          */
         sigma_2: float,
     },
+    /**
+     * Data items in the IHM_CROSS_LINK_RESULT_PARAMETERS category records the
+     * results of the crosslinking restraint parameters in the IHM modeling.
+     */
     ihm_cross_link_result_parameters: {
         /**
          * A unique identifier for the restraint/model combination.
@@ -2773,6 +3096,10 @@ export const mmCIF_Schema = {
          */
         sigma_2: float,
     },
+    /**
+     * Data items in the IHM_2DEM_CLASS_AVERAGE_RESTRAINT category records the
+     * details of the 2DEM class averages used in the IHM modeling.
+     */
     ihm_2dem_class_average_restraint: {
         /**
          * A unique identifier for the 2dem class average.
@@ -2829,6 +3156,24 @@ export const mmCIF_Schema = {
          */
         details: str,
     },
+    /**
+     * Data items in the IHM_2DEM_CLASS_AVERAGE_FITTING category records the
+     * details of the fitting of the model to the 2DEM class averages
+     * used in the IHM modeling. The following conventions are recommended
+     * while generating the rotation matrix and translation vector for
+     * transformation.
+     *
+     * - The model is rotated and translated to fit to the 2DEM image.
+     * - The 2DEM image should be in the XY plane.
+     * - The lower left image corner (image pixel index 0,0) should be at x,y,z = (0,0,0).
+     * - The 2D image is scaled by the _ihm_2dem_class_average_restraint.pixel_size_width
+     * and _ihm_2dem_class_average_restraint.pixel_size_height from the
+     * IHM_2DEM_CLASS_AVERAGE_RESTRAINT table.
+     * - The transformation is applied after the scaling and hence the translation vector
+     * should account for the scaling.
+     * - There are no specifications for Z translations i.e., how far the image should be
+     * from the model while projecting. It may be set to zero.
+     */
     ihm_2dem_class_average_fitting: {
         /**
          * A unique identifier for the 2dem class average fitting data.
@@ -2859,6 +3204,11 @@ export const mmCIF_Schema = {
          */
         tr_vector: Vector(3),
     },
+    /**
+     * Data items in the IHM_3DEM_RESTRAINT category records the
+     * details of the 3DEM maps used as restraints in the
+     * IHM modeling.
+     */
     ihm_3dem_restraint: {
         /**
          * A unique identifier for the 3DEM restraint description.
@@ -2902,6 +3252,11 @@ export const mmCIF_Schema = {
          */
         cross_correlation_coefficient: float,
     },
+    /**
+     * Data items in the IHM_SAS_RESTRAINT category records the
+     * details of the SAS data used as restraints in the
+     * IHM modeling.
+     */
     ihm_sas_restraint: {
         /**
          * A unique identifier for the SAS restraint description.
@@ -2960,6 +3315,11 @@ export const mmCIF_Schema = {
          */
         details: str,
     },
+    /**
+     * Data items in the IHM_STARTING_MODEL_COORD category records the coordinates
+     * for structural templates used as starting inputs in the integrative model
+     * building tasks.
+     */
     ihm_starting_model_coord: {
         /**
          * A unique identifier for this coordinate position.
@@ -3030,6 +3390,10 @@ export const mmCIF_Schema = {
          */
         B_iso_or_equiv: float,
     },
+    /**
+     * Data items in the IHM_SPHERE_OBJ_SITE category records the details
+     * of the spherical objects modeled in the integrative structural model.
+     */
     ihm_sphere_obj_site: {
         /**
          * A unique identifier for this pseudo atom / sphere object.
@@ -3083,6 +3447,10 @@ export const mmCIF_Schema = {
          */
         model_id: int,
     },
+    /**
+     * Data items in the IHM_GAUSSIAN_OBJ_SITE category records the details
+     * of the gaussian objects modeled in the integrative structural model.
+     */
     ihm_gaussian_obj_site: {
         /**
          * A unique identifier for this gaussian object in the model.
@@ -3135,6 +3503,10 @@ export const mmCIF_Schema = {
          */
         model_id: int,
     },
+    /**
+     * Data items in the IHM_GAUSSIAN_OBJ_ENSEMBLE category records the details
+     * of the gaussian objects representing an ensemble or cluster of models.
+     */
     ihm_gaussian_obj_ensemble: {
         /**
          * A unique identifier for this gaussian object.
@@ -3187,6 +3559,10 @@ export const mmCIF_Schema = {
          */
         ensemble_id: int,
     },
+    /**
+     * IHM_FEATURE_LIST is the high level category that provides defintions
+     * to select atoms/residues from polymeric and non-polymeric entities.
+     */
     ihm_feature_list: {
         /**
          * A unique identifier for the feature.
@@ -3202,6 +3578,11 @@ export const mmCIF_Schema = {
          */
         entity_type: Aliased<'polymer' | 'non-polymer' | 'macrolide' | 'water'>(str),
     },
+    /**
+     * Data items in the IHM_POLY_RESIDUE_FEATURE category provides the defintions
+     * required to select a specific residue or a set of residues that may or may not be
+     * in a contiguous range.
+     */
     ihm_poly_residue_feature: {
         /**
          * A unique identifier for the category.
@@ -3246,6 +3627,11 @@ export const mmCIF_Schema = {
          */
         seq_id_end: int,
     },
+    /**
+     * Data items in the IHM_DERIVED_DISTANCE_RESTRAINT category records the
+     * list of distance restraints used in the integrative modeling experiment.
+     * These distance redistance restraints may be derived from various kinds of experiments.
+     */
     ihm_derived_distance_restraint: {
         /**
          * A unique identifier for the derived distance restraint.

+ 24 - 0
src/mol-io/reader/obj/parser.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 Result from '../result'
+import { Task, RuntimeContext } from 'mol-task'
+import { Mesh } from 'mol-geo/mesh/mesh';
+
+async function parseInternal(data: string, ctx: RuntimeContext): Promise<Result<Mesh>> {
+    // TODO
+    const mesh: Mesh = Mesh.createEmpty();
+    // Mesh.computeNormalsImmediate(mesh)
+    return Result.success(mesh);
+}
+
+export function parse(data: string) {
+    return Task.create<Result<Mesh>>('Parse OBJ', async ctx => {
+        return await parseInternal(data, ctx);
+    });
+}
+
+export default parse;

+ 1 - 1
src/mol-math/linear-algebra/3d/vec3.ts

@@ -50,13 +50,13 @@ namespace Vec3 {
         v[0] = array[offset + 0]
         v[1] = array[offset + 1]
         v[2] = array[offset + 2]
+        return v
     }
 
     export function toArray(v: Vec3, out: Helpers.NumberArray, offset: number) {
         out[offset + 0] = v[0]
         out[offset + 1] = v[1]
         out[offset + 2] = v[2]
-        return v
     }
 
     export function create(x: number, y: number, z: number): Vec3 {

+ 2 - 2
src/mol-model-props/rcsb/graphql/types.ts

@@ -1,5 +1,5 @@
 /* tslint:disable */
-/** Generated in 2018-08-20T10:50:59-07:00 */
+/** Generated in 2018-08-22T17:02:01-07:00 */
 
 export enum PdbxLeavingAtomFlag {
   N = "N",
@@ -42,7 +42,7 @@ export enum UnpublishedFlag {
   Y = "Y"
 }
 
-export enum PdbxMonochromaticOrLaueML {
+export enum PdbxMonochromaticOrLaueMl {
   L = "L",
   M = "M"
 }

+ 2 - 1
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,4 +15,4 @@ export function isNullLocation(x: any): x is NullLocation {
     return !!x && x.kind === 'null-location';
 }
 
-export type Location = StructureElement | Link.Location | NullLocation
+export type Location = StructureElement | Link.Location | Shape.Location | NullLocation

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

@@ -6,6 +6,7 @@
 
 import { StructureElement } from './structure'
 import { Link } from './structure/structure/unit/links'
+import { Shape } from './shape';
 
 /** A Loci that includes every loci */
 export const EveryLoci = { kind: 'every-loci' as 'every-loci' }
@@ -21,4 +22,4 @@ export function isEmptyLoci(x: any): x is EmptyLoci {
     return !!x && x.kind === 'empty-loci';
 }
 
-export type Loci =  StructureElement.Loci | Link.Loci | EveryLoci | EmptyLoci
+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'

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

@@ -0,0 +1,71 @@
+/**
+ * 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, ValueCell } from 'mol-util';
+import { OrderedSet } from 'mol-data/int';
+import { arrayMax } from 'mol-util/array';
+
+export interface Shape {
+    readonly id: UUID
+    readonly name: string
+    readonly mesh: Mesh
+    readonly colors: ValueCell<Color[]>
+    readonly labels: ValueCell<string[]>
+    readonly groupCount: number
+}
+
+export namespace Shape {
+    export function create(name: string, mesh: Mesh, colors: Color[], labels: string[]): Shape {
+        let currentGroupBufferVersion = -1
+        let currentGroupCount = -1
+
+        return {
+            id: UUID.create(),
+            name,
+            mesh,
+            get groupCount() {
+                if (mesh.groupBuffer.ref.version !== currentGroupBufferVersion) {
+                    currentGroupCount = arrayMax(mesh.groupBuffer.ref.value) + 1
+                }
+                return currentGroupCount
+            },
+            colors: ValueCell.create(colors),
+            labels: ValueCell.create(labels),
+        }
+    }
+
+    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';
+    }
+}

+ 3 - 13
src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts

@@ -11,7 +11,7 @@ import { LinkType } from '../../../types'
 import { findEntityIdByAsymId, findAtomIndexByLabelName } from '../util'
 import { Column } from 'mol-data/db'
 import { ModelPropertyDescriptor } from '../../../properties/custom';
-import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif';
+import { mmCIF_Database, mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif';
 import { SortedArray } from 'mol-data/int';
 import { CifWriter } from 'mol-io/writer/cif'
 import { ElementIndex } from '../../../indexing';
@@ -137,17 +137,7 @@ export namespace StructConn {
         partners: { residueIndex: number, atomIndex: ElementIndex, symmetry: string }[]
     }
 
-    type StructConnType =
-        | 'covale'
-        | 'covale_base'
-        | 'covale_phosphate'
-        | 'covale_sugar'
-        | 'disulf'
-        | 'hydrog'
-        | 'metalc'
-        | 'mismat'
-        | 'modres'
-        | 'saltbr'
+    type StructConnType = typeof mmCIF_Schema.struct_conn.conn_type_id.T
 
     export function attachFromMmCif(model: Model): boolean {
         if (model.customProperties.has(Descriptor)) return true;
@@ -248,7 +238,7 @@ export namespace StructConn {
                 case 'disulf': flags = LinkType.Flag.Covalent | LinkType.Flag.Sulfide; break;
                 case 'hydrog': flags = LinkType.Flag.Hydrogen; break;
                 case 'metalc': flags = LinkType.Flag.MetallicCoordination; break;
-                case 'saltbr': flags = LinkType.Flag.Ion; break;
+                case 'saltbr': flags = LinkType.Flag.Ionic; break;
             }
 
             entries.push({ rowIndex: i, flags, order, distance: pdbx_dist_value.value(i), partners });

+ 18 - 7
src/mol-model/structure/model/types.ts

@@ -50,6 +50,8 @@ export const enum MoleculeType {
     RNA,
     /** DNA, e.g. component type included in `DNAComponentTypeNames` */
     DNA,
+    /** PNA, peptide nucleic acid, comp id included in `PeptideBaseNames` */
+    PNA,
     /** sacharide, e.g. component type included in `SaccharideComponentTypeNames` */
     saccharide
 }
@@ -77,6 +79,12 @@ export const MoleculeTypeAtomRoleId: { [k: number]: { [k in AtomRole]: string }
         direction: 'C1\'', // TODO 'C1*'
         backboneStart: 'P',
         backboneEnd: 'O3\'' // TODO 'O3*'
+    },
+    [MoleculeType.PNA]: {
+        trace: 'N4\'', // TODO 'N4*'
+        direction: 'C7\'', // TODO 'C7*'
+        backboneStart: 'N1\'', // TODO 'N1*'
+        backboneEnd: 'C1\'' // TODO 'C1*'
     }
 }
 
@@ -153,9 +161,10 @@ export const ExtraSaccharideNames = [
 
 export const RnaBaseNames = [ 'A', 'C', 'T', 'G', 'I', 'U' ]
 export const DnaBaseNames = [ 'DA', 'DC', 'DT', 'DG', 'DI', 'DU' ]
-export const PurinBaseNames = [ 'A', 'G', 'DA', 'DG', 'DI' ]
-export const PyrimidineBaseNames = [ 'C', 'T', 'U', 'DC', 'DT', 'DU' ]
-export const BaseNames = RnaBaseNames.concat(DnaBaseNames)
+export const PeptideBaseNames = [ 'APN', 'CPN', 'TPN', 'GPN' ]
+export const PurinBaseNames = [ 'A', 'G', 'DA', 'DG', 'DI', 'APN', 'GPN' ]
+export const PyrimidineBaseNames = [ 'C', 'T', 'U', 'DC', 'DT', 'DU', 'CPN', 'TPN' ]
+export const BaseNames = RnaBaseNames.concat(DnaBaseNames, PeptideBaseNames)
 
 export const isPurinBase = (compId: string) => PurinBaseNames.includes(compId.toUpperCase())
 export const isPyrimidineBase = (compId: string) => PyrimidineBaseNames.includes(compId.toUpperCase())
@@ -164,7 +173,9 @@ export const isPyrimidineBase = (compId: string) => PyrimidineBaseNames.includes
 export function getMoleculeType(compType: string, compId: string) {
     compType = compType.toUpperCase()
     compId = compId.toUpperCase()
-    if (ProteinComponentTypeNames.includes(compType)) {
+    if (PeptideBaseNames.includes(compId)) {
+        return MoleculeType.PNA
+    } else if (ProteinComponentTypeNames.includes(compType)) {
         return MoleculeType.protein
     } else if (RNAComponentTypeNames.includes(compType)) {
         return MoleculeType.RNA
@@ -182,11 +193,11 @@ export function getMoleculeType(compType: string, compId: string) {
 }
 
 export function isPolymer(moleculeType: MoleculeType) {
-    return moleculeType === MoleculeType.protein || moleculeType === MoleculeType.DNA || moleculeType === MoleculeType.RNA
+    return moleculeType === MoleculeType.protein || moleculeType === MoleculeType.DNA || moleculeType === MoleculeType.RNA || moleculeType === MoleculeType.PNA
 }
 
 export function isNucleic(moleculeType: MoleculeType) {
-    return moleculeType === MoleculeType.DNA || moleculeType === MoleculeType.RNA
+    return moleculeType === MoleculeType.DNA || moleculeType === MoleculeType.RNA || moleculeType === MoleculeType.PNA
 }
 
 /**
@@ -484,7 +495,7 @@ export namespace LinkType {
         Covalent             = 0x1,
         MetallicCoordination = 0x2,
         Hydrogen             = 0x4,
-        Ion                  = 0x8,
+        Ionic                = 0x8,
         Sulfide              = 0x10,
         Aromatic             = 0x20,
         Computed             = 0x40

+ 4 - 0
src/mol-model/structure/query/queries/generators.ts

@@ -20,9 +20,13 @@ export const none: StructureQuery = ctx => StructureSelection.Sequence(ctx.input
 export const all: StructureQuery = ctx => StructureSelection.Singletons(ctx.inputStructure, ctx.inputStructure);
 
 export interface AtomsQueryParams {
+    /** Query to be executed for each entity once */
     entityTest: QueryPredicate,
+    /** Query to be executed for each chain once */
     chainTest: QueryPredicate,
+    /** Query to be executed for each residue once */
     residueTest: QueryPredicate,
+    /** Query to be executed for each atom */
     atomTest: QueryPredicate,
     groupBy: QueryFn
 }

+ 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 - 1
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,6 +36,13 @@ export function labelFirst(loci: Loci): string {
             } else {
                 return 'Unknown'
             }
+        case 'group-loci':
+            const g = loci.groups[0]
+            if (g) {
+                return g.shape.labels.ref.value[OrderedSet.getAt(g.ids, 0)]
+            } else {
+                return 'Unknown'
+            }
         case 'every-loci':
             return 'Everything'
         case 'empty-loci':

+ 4 - 2
src/mol-view/stage.ts

@@ -121,9 +121,11 @@ export class Stage {
         // this.loadMmcifUrl(`../../examples/1cbs_updated.cif`)
         // this.loadMmcifUrl(`../../examples/1crn.cif`)
         // this.loadPdbid('5u0q') // mixed dna/rna in same polymer
-        // this.loadPdbid('5u0q') // temp
+        // this.loadPdbid('1xj9') // PNA (peptide nucleic acid)
+        this.loadPdbid('5eme') // PNA (peptide nucleic acid) and RNA
+        // this.loadPdbid('5eme') // temp
 
-        this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000001.cif`) // ok
+        // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000001.cif`) // ok
         // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000002.cif`) // ok
         // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000003.cif`) // ok
         // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000004.cif`) // TODO issue with cross-link extraction, not shown

+ 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.colors.ref.value[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)
         }
     }
 }