Browse Source

Merge remote-tracking branch 'upstream/master'

Sebastian Bittrich 1 year ago
parent
commit
2ddf94313e

+ 12 - 1
CHANGELOG.md

@@ -6,8 +6,15 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+## [v3.35.0] - 2023-05-14
+
+- Enable odd dash count (1,3,5)
+- Add principal axes spec and fix edge cases
 - Add a uniform color theme for NtC tube that still paints residue and segment dividers in a different color
-- Support points & lines in glTF export
+- Mesh exporter improvements
+    - Support points & lines in glTF export
+    - Set alphaMode and doubleSided in glTF export
+    - Fix flipped cylinder caps
 - Fix bond assignments `struct_conn` records referencing waters
 - Add StructConn extension providing functions for inspecting struct_conns
 - Fix `PluginState.setSnapshot` triggering unnecessary state updates
@@ -17,6 +24,10 @@ Note that since we don't clearly distinguish between a public and private interf
 - Parse HEADER record when reading PDB file
 - Support `ignoreHydrogens` in interactions representation
 - Add hydroxyproline (HYP) commonly present in collagen molecules to the list of amino acids
+- Fix assemblies for Archive PDB files (do not generate unique `label_asym_id` if `REMARK 350` is present)
+- Add additional functions to `core.math` in `mol-script`
+    - `cantorPairing`, `sortedCantorPairing`, `invertCantorPairing`,
+    - `trunc`, `sign`
 
 ## [v3.34.0] - 2023-04-16
 

File diff suppressed because it is too large
+ 332 - 223
package-lock.json


+ 19 - 19
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "3.34.0",
+  "version": "3.35.0",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -104,27 +104,27 @@
   "license": "MIT",
   "devDependencies": {
     "@graphql-codegen/add": "^4.0.1",
-    "@graphql-codegen/cli": "^3.3.0",
+    "@graphql-codegen/cli": "^3.3.1",
     "@graphql-codegen/time": "^4.0.0",
-    "@graphql-codegen/typescript": "^3.0.3",
+    "@graphql-codegen/typescript": "^3.0.4",
     "@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
     "@graphql-codegen/typescript-graphql-request": "^4.5.9",
-    "@graphql-codegen/typescript-operations": "^3.0.3",
+    "@graphql-codegen/typescript-operations": "^3.0.4",
     "@types/cors": "^2.8.13",
     "@types/gl": "^6.0.2",
     "@types/jpeg-js": "^0.3.7",
     "@types/pngjs": "^6.0.1",
-    "@types/jest": "^29.5.0",
-    "@types/react": "^18.0.35",
-    "@types/react-dom": "^18.0.11",
-    "@typescript-eslint/eslint-plugin": "^5.58.0",
-    "@typescript-eslint/parser": "^5.58.0",
+    "@types/jest": "^29.5.1",
+    "@types/react": "^18.2.6",
+    "@types/react-dom": "^18.2.4",
+    "@typescript-eslint/eslint-plugin": "^5.59.5",
+    "@typescript-eslint/parser": "^5.59.5",
     "benchmark": "^2.1.4",
     "concurrently": "^8.0.1",
     "cpx2": "^4.2.3",
     "crypto-browserify": "^3.12.0",
     "css-loader": "^6.7.3",
-    "eslint": "^8.38.0",
+    "eslint": "^8.40.0",
     "extra-watch-webpack-plugin": "^1.0.3",
     "file-loader": "^6.2.0",
     "fs-extra": "^11.1.1",
@@ -136,22 +136,22 @@
     "raw-loader": "^4.0.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "sass": "^1.62.0",
+    "sass": "^1.62.1",
     "sass-loader": "^13.2.2",
-    "simple-git": "^3.17.0",
+    "simple-git": "^3.18.0",
     "stream-browserify": "^3.0.0",
     "style-loader": "^3.3.2",
     "ts-jest": "^29.1.0",
     "typescript": "^5.0.4",
-    "webpack": "^5.79.0",
-    "webpack-cli": "^5.0.1"
+    "webpack": "^5.82.1",
+    "webpack-cli": "^5.1.1"
   },
   "dependencies": {
     "@types/argparse": "^2.0.10",
     "@types/benchmark": "^2.1.2",
     "@types/compression": "1.7.2",
     "@types/express": "^4.17.17",
-    "@types/node": "^16.18.23",
+    "@types/node": "^16.18.30",
     "@types/node-fetch": "^2.6.3",
     "@types/swagger-ui-dist": "3.30.1",
     "argparse": "^2.0.1",
@@ -162,11 +162,11 @@
     "h264-mp4-encoder": "^1.0.12",
     "immer": "^9.0.21",
     "immutable": "^4.3.0",
-    "node-fetch": "^2.6.9",
-    "rxjs": "^7.8.0",
-    "swagger-ui-dist": "^4.18.2",
+    "node-fetch": "^2.6.11",
+    "rxjs": "^7.8.1",
+    "swagger-ui-dist": "^4.18.3",
     "tslib": "^2.5.0",
-    "util.promisify": "^1.1.1",
+    "util.promisify": "^1.1.2",
     "xhr2": "^0.2.1"
   },
   "peerDependencies": {

+ 1 - 1
src/extensions/dnatco/ntc-tube/representation.ts

@@ -316,7 +316,7 @@ function createNtCTubeMesh(ctx: VisualContext, unit: Unit, structure: Structure,
                 radiusTop: diameter / 2, radiusBottom: diameter / 2, topCap: true, bottomCap: true, radialSegments: segCount.radial,
             };
             mb.currentGroup = FirstBlockId;
-            addFixedCountDashedCylinder(mb, p_1, p1, 1, 2 * segCount.linear, cylinderProps);
+            addFixedCountDashedCylinder(mb, p_1, p1, 1, 2 * segCount.linear, false, cylinderProps);
         }
     }
 

+ 8 - 4
src/extensions/geo-export/glb-exporter.ts

@@ -170,8 +170,8 @@ export class GlbExporter extends MeshExporter<GlbData> {
         return this.addBuffer(colorBuffer, UNSIGNED_BYTE, 'VEC4', vertexCount, ARRAY_BUFFER, undefined, undefined, true);
     }
 
-    private addMaterial(metalness: number, roughness: number) {
-        const hash = `${metalness}|${roughness}`;
+    private addMaterial(metalness: number, roughness: number, doubleSided: boolean, alpha: boolean) {
+        const hash = `${metalness}|${roughness}|${doubleSided}`;
         if (!this.materialMap.has(hash)) {
             this.materialMap.set(hash, this.materials.length);
             this.materials.push({
@@ -179,7 +179,9 @@ export class GlbExporter extends MeshExporter<GlbData> {
                     baseColorFactor: [1, 1, 1, 1],
                     metallicFactor: metalness,
                     roughnessFactor: roughness
-                }
+                },
+                doubleSided,
+                alphaMode: alpha ? 'BLEND' : 'OPAQUE',
             });
         }
         return this.materialMap.get(hash)!;
@@ -198,8 +200,10 @@ export class GlbExporter extends MeshExporter<GlbData> {
         const instanceCount = values.uInstanceCount.ref.value;
         const metalness = values.uMetalness.ref.value;
         const roughness = values.uRoughness.ref.value;
+        const doubleSided = values.uDoubleSided?.ref.value || values.hasReflection.ref.value;
+        const alpha = values.uAlpha.ref.value < 1;
 
-        const material = this.addMaterial(metalness, roughness);
+        const material = this.addMaterial(metalness, roughness, doubleSided, alpha);
 
         let interpolatedColors: Uint8Array | undefined;
         if (webgl && mesh && (colorType === 'volume' || colorType === 'volumeInstance')) {

+ 12 - 3
src/extensions/geo-export/mesh-exporter.ts

@@ -29,11 +29,15 @@ import { unpackRGBToInt } from '../../mol-util/number-packing';
 import { RenderObjectExporter, RenderObjectExportData } from './render-object-exporter';
 import { readAlphaTexture, readTexture } from '../../mol-gl/compute/util';
 import { assertUnreachable } from '../../mol-util/type-helpers';
+import { ValueCell } from '../../mol-util/value-cell';
 
 const GeoExportName = 'geo-export';
 
 // avoiding namespace lookup improved performance in Chrome (Aug 2020)
 const v3fromArray = Vec3.fromArray;
+const v3sub = Vec3.sub;
+const v3dot = Vec3.dot;
+const v3unitY = Vec3.unitY;
 
 type MeshMode = 'points' | 'lines' | 'triangles'
 
@@ -47,7 +51,7 @@ export interface AddMeshInput {
         drawCount: number
     } | undefined
     meshes: Mesh[] | undefined
-    values: BaseValues
+    values: BaseValues & { readonly uDoubleSided?: ValueCell<any> }
     isGeoTexture: boolean
     mode: MeshMode
     webgl: WebGLContext | undefined
@@ -509,6 +513,7 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
     private async addCylinders(values: CylindersValues, webgl: WebGLContext, ctx: RuntimeContext) {
         const start = Vec3();
         const end = Vec3();
+        const dir = Vec3();
 
         const aStart = values.aStart.ref.value;
         const aEnd = values.aEnd.ref.value;
@@ -546,12 +551,16 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
             for (let i = 0; i < vertexCount; i += 6) {
                 v3fromArray(start, aStart, i * 3);
                 v3fromArray(end, aEnd, i * 3);
+                v3sub(dir, end, start);
 
                 const group = aGroup[i];
                 const radius = MeshExporter.getSize(values, instanceIndex, group) * aScale[i];
                 const cap = aCap[i];
-                const topCap = cap === 1 || cap === 3;
-                const bottomCap = cap >= 2;
+                let topCap = cap === 1 || cap === 3;
+                let bottomCap = cap >= 2;
+                if (v3dot(v3unitY, dir) > 0) {
+                    [bottomCap, topCap] = [topCap, bottomCap];
+                }
                 const cylinderProps = { radiusTop: radius, radiusBottom: radius, topCap, bottomCap, radialSegments };
                 state.currentGroup = aGroup[i];
                 addCylinder(state, start, end, 1, cylinderProps);

+ 18 - 12
src/mol-geo/geometry/cylinders/cylinders-builder.ts

@@ -1,7 +1,8 @@
 /**
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ChunkedArray } from '../../../mol-data/util';
@@ -10,7 +11,7 @@ import { Vec3 } from '../../../mol-math/linear-algebra';
 
 export interface CylindersBuilder {
     add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
-    addFixedCountDashes(start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
+    addFixedCountDashes(start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, stubCap: boolean, group: number): void
     addFixedLengthDashes(start: Vec3, end: Vec3, segmentLength: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
     getCylinders(): Cylinders
 }
@@ -41,19 +42,24 @@ export namespace CylindersBuilder {
             }
         };
 
-        const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number) => {
+        const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, stubCap: boolean, group: number) => {
             const d = Vec3.distance(start, end);
-            const s = Math.floor(segmentCount / 2);
-            const step = 1 / segmentCount;
+            const isOdd = segmentCount % 2 !== 0;
+            const s = Math.floor((segmentCount + 1) / 2);
+            const step = d / (segmentCount + 0.5);
 
-            Vec3.sub(tmpDir, end, start);
+            Vec3.setMagnitude(tmpDir, Vec3.sub(tmpDir, end, start), step);
+            Vec3.copy(tmpVecA, start);
             for (let j = 0; j < s; ++j) {
-                const f = step * (j * 2 + 1);
-                Vec3.setMagnitude(tmpDir, tmpDir, d * f);
-                Vec3.add(tmpVecA, start, tmpDir);
-                Vec3.setMagnitude(tmpDir, tmpDir, d * step * ((j + 1) * 2));
-                Vec3.add(tmpVecB, start, tmpDir);
+                Vec3.add(tmpVecA, tmpVecA, tmpDir);
+                if (isOdd && j === s - 1) {
+                    Vec3.copy(tmpVecB, end);
+                    if (!stubCap) bottomCap = false;
+                } else {
+                    Vec3.add(tmpVecB, tmpVecA, tmpDir);
+                }
                 add(tmpVecA[0], tmpVecA[1], tmpVecA[2], tmpVecB[0], tmpVecB[1], tmpVecB[2], radiusScale, topCap, bottomCap, group);
+                Vec3.add(tmpVecA, tmpVecA, tmpDir);
             }
         };
 
@@ -62,7 +68,7 @@ export namespace CylindersBuilder {
             addFixedCountDashes,
             addFixedLengthDashes: (start: Vec3, end: Vec3, segmentLength: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number) => {
                 const d = Vec3.distance(start, end);
-                addFixedCountDashes(start, end, d / segmentLength, radiusScale, topCap, bottomCap, group);
+                addFixedCountDashes(start, end, d / segmentLength, radiusScale, topCap, bottomCap, true, group);
             },
             getCylinders: () => {
                 const cylinderCount = groups.elementCount / 6;

+ 15 - 10
src/mol-geo/geometry/lines/lines-builder.ts

@@ -1,7 +1,8 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ChunkedArray } from '../../../mol-data/util';
@@ -50,17 +51,21 @@ export namespace LinesBuilder {
 
         const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, group: number) => {
             const d = Vec3.distance(start, end);
-            const s = Math.floor(segmentCount / 2);
-            const step = 1 / segmentCount;
+            const isOdd = segmentCount % 2 !== 0;
+            const s = Math.floor((segmentCount + 1) / 2);
+            const step = d / (segmentCount + 0.5);
 
-            Vec3.sub(tmpDir, end, start);
+            Vec3.setMagnitude(tmpDir, Vec3.sub(tmpDir, end, start), step);
+            Vec3.copy(tmpVecA, start);
             for (let j = 0; j < s; ++j) {
-                const f = step * (j * 2 + 1);
-                Vec3.setMagnitude(tmpDir, tmpDir, d * f);
-                Vec3.add(tmpVecA, start, tmpDir);
-                Vec3.setMagnitude(tmpDir, tmpDir, d * step * ((j + 1) * 2));
-                Vec3.add(tmpVecB, start, tmpDir);
+                Vec3.add(tmpVecA, tmpVecA, tmpDir);
+                if (isOdd && j === s - 1) {
+                    Vec3.copy(tmpVecB, end);
+                } else {
+                    Vec3.add(tmpVecB, tmpVecA, tmpDir);
+                }
                 add(tmpVecA[0], tmpVecA[1], tmpVecA[2], tmpVecB[0], tmpVecB[1], tmpVecB[2], group);
+                Vec3.add(tmpVecA, tmpVecA, tmpDir);
             }
         };
 
@@ -111,4 +116,4 @@ function fillMappingAndIndices(n: number, mb: Float32Array, ib: Uint32Array) {
         ib[io] = o; ib[io + 1] = o + 1; ib[io + 2] = o + 2;
         ib[io + 3] = o + 1; ib[io + 4] = o + 3; ib[io + 5] = o + 2;
     }
-}
+}

+ 21 - 19
src/mol-geo/geometry/mesh/builder/cylinder.ts

@@ -1,7 +1,8 @@
 /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
@@ -97,26 +98,27 @@ export function addDoubleCylinder(state: MeshBuilder.State, start: Vec3, end: Ve
     MeshBuilder.addPrimitive(state, tmpCylinderMat, cylinder);
 }
 
-export function addFixedCountDashedCylinder(state: MeshBuilder.State, start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: BasicCylinderProps) {
-    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;
-    }
-
+export function addFixedCountDashedCylinder(state: MeshBuilder.State, start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, stubCap: boolean, props: BasicCylinderProps) {
     const d = Vec3.distance(start, end) * lengthScale;
-    const cylinder = getCylinder(props);
-    Vec3.sub(tmpCylinderDir, end, start);
+    const isOdd = segmentCount % 2 !== 0;
+    const s = Math.floor((segmentCount + 1) / 2);
+    let step = d / (segmentCount + 0.5);
 
+    let cylinder = getCylinder(props);
+    Vec3.setMagnitude(tmpCylinderDir, Vec3.sub(tmpCylinderDir, end, start), step);
+    Vec3.copy(tmpCylinderStart, 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, false);
+        Vec3.add(tmpCylinderStart, tmpCylinderStart, tmpCylinderDir);
+        if (isOdd && j === s - 1) {
+            if (!stubCap && props.topCap) {
+                props.topCap = false;
+                cylinder = getCylinder(props);
+            }
+            step /= 2;
+        }
+        setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, step, false);
         MeshBuilder.addPrimitive(state, tmpCylinderMat, cylinder);
+
+        Vec3.add(tmpCylinderStart, tmpCylinderStart, tmpCylinderDir);
     }
-}
+}

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

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -24,6 +24,8 @@ import { Mat3 } from './mat3';
 import { Quat } from './quat';
 import { EPSILON } from './common';
 
+const _isFinite = isFinite;
+
 export { ReadonlyVec3 };
 
 interface Vec3 extends Array<number> { [d: number]: number, '@type': 'vec3', length: 3 }
@@ -48,6 +50,10 @@ namespace Vec3 {
         return out;
     }
 
+    export function isFinite(a: Vec3): boolean {
+        return _isFinite(a[0]) && _isFinite(a[1]) && _isFinite(a[2]);
+    }
+
     export function hasNaN(a: Vec3) {
         return isNaN(a[0]) || isNaN(a[1]) || isNaN(a[2]);
     }
@@ -636,4 +642,4 @@ namespace Vec3 {
     export const negUnitZ: ReadonlyVec3 = create(0, 0, -1);
 }
 
-export { Vec3 };
+export { Vec3 };

+ 32 - 0
src/mol-math/linear-algebra/_spec/principal-axes.spec.ts

@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Gianluca Tomasello <giagitom@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { NumberArray } from '../../../mol-util/type-helpers';
+import { Vec3 } from '../3d/vec3';
+import { PrincipalAxes } from '../matrix/principal-axes';
+
+describe('PrincipalAxes', () => {
+    it('same-cartesian-plane', () => {
+        const positions: NumberArray = [ // same y coordinate
+            0.1945, -0.0219, -0.0416,
+            -0.0219, -0.0219, -0.0119,
+        ];
+        const pa = PrincipalAxes.ofPositions(positions);
+        expect(Vec3.isFinite(pa.boxAxes.origin)).toBe(true);
+        expect(Vec3.equals(pa.boxAxes.origin, pa.momentsAxes.origin)).toBe(true);
+    });
+
+    it('same-point', () => {
+        const positions: NumberArray = [ // same coordinates
+            0.1945, -0.0219, -0.0416,
+            0.1945, -0.0219, -0.0416,
+        ];
+        const pa = PrincipalAxes.ofPositions(positions);
+        expect(Vec3.isFinite(pa.boxAxes.origin)).toBe(true);
+        expect(Vec3.equals(pa.boxAxes.origin, pa.momentsAxes.origin)).toBe(true);
+    });
+});

+ 10 - 4
src/mol-math/linear-algebra/matrix/principal-axes.ts

@@ -1,7 +1,8 @@
 /**
- * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { Matrix } from './matrix';
@@ -123,12 +124,17 @@ namespace PrincipalAxes {
         const dirB = Vec3.setMagnitude(Vec3(), a.dirB, (d2a + d2b) / 2);
         const dirC = Vec3.setMagnitude(Vec3(), a.dirC, (d3a + d3b) / 2);
 
+        const okDirA = Vec3.isFinite(dirA);
+        const okDirB = Vec3.isFinite(dirB);
+        const okDirC = Vec3.isFinite(dirC);
+
         const origin = Vec3();
         const addCornerHelper = function (d1: number, d2: number, d3: number) {
             Vec3.copy(tmpBoxVec, center);
-            Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, a.dirA, d1);
-            Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, a.dirB, d2);
-            Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, a.dirC, d3);
+
+            if (okDirA) Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, a.dirA, d1);
+            if (okDirB) Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, a.dirB, d2);
+            if (okDirC) Vec3.scaleAndAdd(tmpBoxVec, tmpBoxVec, a.dirC, d3);
             Vec3.add(origin, origin, tmpBoxVec);
         };
         addCornerHelper(d1a, d2a, d3a);

+ 5 - 3
src/mol-model-formats/structure/pdb/atom-site.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -39,7 +39,7 @@ export function getAtomSiteTemplate(data: string, count: number) {
     };
 }
 
-export function getAtomSite(sites: AtomSiteTemplate, terIndices: Set<number>): { [K in keyof mmCIF_Schema['atom_site'] | 'partial_charge']?: CifField } {
+export function getAtomSite(sites: AtomSiteTemplate, terIndices: Set<number>, options: { hasAssemblies: boolean }): { [K in keyof mmCIF_Schema['atom_site'] | 'partial_charge']?: CifField } {
     const pdbx_PDB_model_num = CifField.ofStrings(sites.pdbx_PDB_model_num);
     const auth_asym_id = CifField.ofTokens(sites.auth_asym_id);
     const auth_seq_id = CifField.ofTokens(sites.auth_seq_id);
@@ -87,7 +87,9 @@ export function getAtomSite(sites: AtomSiteTemplate, terIndices: Set<number>): {
         if (asymIdCounts.has(asymId)) {
             // only change the chains name if there are TER records
             // otherwise assume repeated chain name use is from interleaved chains
-            if (terIndices.has(i)) {
+            // also don't change the chains name if there are assemblies
+            // as those require the original chain name
+            if (terIndices.has(i) && !options.hasAssemblies) {
                 const asymIdCount = asymIdCounts.get(asymId)! + 1;
                 asymIdCounts.set(asymId, asymIdCount);
                 currLabelAsymId = `${asymId}_${asymIdCount}`;

+ 4 - 2
src/mol-model-formats/structure/pdb/to-cif.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -54,6 +54,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
 
     let modelNum = 0, modelStr = '';
     let conectRange: [number, number] | undefined = undefined;
+    let hasAssemblies = false;
     const terIndices = new Set<number>();
 
     for (let i = 0, _i = lines.count; i < _i; i++) {
@@ -152,6 +153,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
                     }
                     helperCategories.push(...parseRemark350(lines, i, j));
                     i = j - 1;
+                    hasAssemblies = true;
                 }
                 break;
             case 'S':
@@ -208,7 +210,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
         atomSite.label_entity_id[i] = entityBuilder.getEntityId(compId, moleculeType, asymIds.value(i));
     }
 
-    const atom_site = getAtomSite(atomSite, terIndices);
+    const atom_site = getAtomSite(atomSite, terIndices, { hasAssemblies });
     if (!isPdbqt) delete atom_site.partial_charge;
 
     if (conectRange) {

+ 2 - 1
src/mol-model-props/computed/representations/shared.ts

@@ -2,13 +2,14 @@
  * Copyright (c) 2022-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 
 export const InteractionsSharedParams = {
     sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
-    dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
+    dashCount: PD.Numeric(6, { min: 1, max: 10, step: 1 }),
     dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
     ignoreHydrogens: PD.Boolean(false),
     ignoreHydrogensVariant: PD.Select('all', PD.arrayToOptions(['all', 'non-polar'] as const)),

+ 5 - 4
src/mol-repr/structure/visual/polymer-gap-cylinder.ts

@@ -1,7 +1,8 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
@@ -68,11 +69,11 @@ function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, structure:
 
             cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA) * sizeFactor;
             builderState.currentGroup = i;
-            addFixedCountDashedCylinder(builderState, pA, pB, 0.5, segmentCount, cylinderProps);
+            addFixedCountDashedCylinder(builderState, pA, pB, 0.5, segmentCount, false, cylinderProps);
 
             cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB) * sizeFactor;
             builderState.currentGroup = i + 1;
-            addFixedCountDashedCylinder(builderState, pB, pA, 0.5, segmentCount, cylinderProps);
+            addFixedCountDashedCylinder(builderState, pB, pA, 0.5, segmentCount, false, cylinderProps);
         }
 
         i += 2;
@@ -106,4 +107,4 @@ export function PolymerGapVisual(materialId: number): UnitsVisual<PolymerGapPara
             );
         }
     }, materialId);
-}
+}

+ 36 - 70
src/mol-repr/structure/visual/util/link.ts

@@ -1,8 +1,9 @@
 /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Zhenyu Zhang <jump2cn@gmail.com>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { Vec3 } from '../../../../mol-math/linear-algebra';
@@ -25,8 +26,8 @@ export const LinkCylinderParams = {
     linkCap: PD.Boolean(false),
     aromaticScale: PD.Numeric(0.3, { min: 0, max: 1, step: 0.01 }),
     aromaticSpacing: PD.Numeric(1.5, { min: 0, max: 3, step: 0.01 }),
-    aromaticDashCount: PD.Numeric(2, { min: 2, max: 6, step: 2 }),
-    dashCount: PD.Numeric(4, { min: 0, max: 10, step: 2 }),
+    aromaticDashCount: PD.Numeric(2, { min: 1, max: 6, step: 1 }),
+    dashCount: PD.Numeric(4, { min: 0, max: 10, step: 1 }),
     dashScale: PD.Numeric(0.8, { min: 0, max: 2, step: 0.1 }),
     dashCap: PD.Boolean(true),
     stubCap: PD.Boolean(true),
@@ -38,8 +39,8 @@ export type LinkCylinderProps = typeof DefaultLinkCylinderProps
 export const LinkLineParams = {
     linkScale: PD.Numeric(0.5, { min: 0, max: 1, step: 0.1 }),
     linkSpacing: PD.Numeric(0.1, { min: 0, max: 2, step: 0.01 }),
-    aromaticDashCount: PD.Numeric(2, { min: 2, max: 6, step: 2 }),
-    dashCount: PD.Numeric(4, { min: 0, max: 10, step: 2 }),
+    aromaticDashCount: PD.Numeric(2, { min: 1, max: 6, step: 1 }),
+    dashCount: PD.Numeric(4, { min: 0, max: 10, step: 1 }),
 };
 export const DefaultLinkLineProps = PD.getDefaultValues(LinkLineParams);
 export type LinkLineProps = typeof DefaultLinkLineProps
@@ -133,8 +134,6 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
         bottomCap: linkCap
     };
 
-    const segmentCount = dashCount + 1;
-
     for (let edgeIndex = 0, _eI = linkCount; edgeIndex < _eI; ++edgeIndex) {
         if (ignore && ignore(edgeIndex)) continue;
 
@@ -153,8 +152,6 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
         const [topCap, bottomCap] = dirFlag ? [linkStub, linkCap] : [linkCap, linkStub];
         builderState.currentGroup = edgeIndex;
 
-        const aromaticSegmentCount = aromaticDashCount + 1;
-
         if (linkStyle === LinkStyle.Solid) {
             cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius;
             cylinderProps.topCap = topCap;
@@ -164,12 +161,7 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
         } else if (linkStyle === LinkStyle.Dashed) {
             cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius * dashScale;
             cylinderProps.topCap = cylinderProps.bottomCap = dashCap;
-
-            if (segmentCount > 1) {
-                addFixedCountDashedCylinder(builderState, va, vb, 0.5, segmentCount, cylinderProps);
-            } else {
-                addCylinder(builderState, va, vb, 0.5, cylinderProps);
-            }
+            addFixedCountDashedCylinder(builderState, va, vb, 0.5, dashCount, linkStub, cylinderProps);
         } else if (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple || linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
             const order = (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble) ? 2 :
                 (linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple) ? 3 : 1.5;
@@ -196,13 +188,13 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
                 v3setMagnitude(vShift, vShift, aromaticOffset);
                 v3sub(va, va, vShift);
                 v3sub(vb, vb, vShift);
-                addFixedCountDashedCylinder(builderState, va, vb, 0.5, aromaticSegmentCount, cylinderProps);
+                addFixedCountDashedCylinder(builderState, va, vb, 0.5, aromaticDashCount, linkStub, cylinderProps);
 
                 if (linkStyle === LinkStyle.MirroredAromatic) {
                     v3setMagnitude(vShift, vShift, aromaticOffset * 2);
                     v3add(va, va, vShift);
                     v3add(vb, vb, vShift);
-                    addFixedCountDashedCylinder(builderState, va, vb, 0.5, aromaticSegmentCount, cylinderProps);
+                    addFixedCountDashedCylinder(builderState, va, vb, 0.5, aromaticDashCount, linkStub, cylinderProps);
                 }
             } else if (linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.OffsetTriple) {
                 const multipleOffset = linkRadius + multiRadius + linkScale * linkRadius * linkSpacing;
@@ -284,13 +276,6 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
     const center = Vec3();
     let count = 0;
 
-    // automatically adjust length for evenly spaced dashed cylinders
-    const segmentCount = dashCount % 2 === 1 ? dashCount : dashCount + 1;
-    const lengthScale = 0.5 - (0.5 / 2 / segmentCount);
-
-    const aromaticSegmentCount = aromaticDashCount + 1;
-    const aromaticLengthScale = 0.5 - (0.5 / 2 / aromaticSegmentCount);
-
     for (let edgeIndex = 0, _eI = linkCount; edgeIndex < _eI; ++edgeIndex) {
         if (ignore && ignore(edgeIndex)) continue;
 
@@ -308,14 +293,8 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
             v3scale(vm, v3add(vm, va, vb), 0.5);
             builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, edgeIndex);
         } else if (linkStyle === LinkStyle.Dashed) {
-            if (segmentCount > 1) {
-                v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
-                v3sub(vb, vb, tmpV12);
-                builder.addFixedCountDashes(va, vb, segmentCount, dashScale, dashCap, dashCap, edgeIndex);
-            } else {
-                v3scale(vm, v3add(vm, va, vb), 0.5);
-                builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], dashScale, dashCap, dashCap, edgeIndex);
-            }
+            v3scale(vm, v3add(vm, va, vb), 0.5);
+            builder.addFixedCountDashes(va, vm, dashCount, dashScale, dashCap, dashCap, linkStub, edgeIndex);
         } else if (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple || linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
             const order = (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble) ? 2 :
                 (linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple) ? 3 : 1.5;
@@ -330,22 +309,19 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
 
                 const aromaticOffset = linkRadius + aromaticScale * linkRadius + aromaticScale * linkRadius * aromaticSpacing;
 
-                v3scale(tmpV12, v3sub(tmpV12, vb, va), aromaticLengthScale);
-                v3sub(vb, vb, tmpV12);
-
                 v3setMagnitude(tmpV12, v3sub(tmpV12, vb, va), linkRadius * 0.5);
                 v3add(va, va, tmpV12);
 
                 v3setMagnitude(vShift, vShift, aromaticOffset);
                 v3sub(va, va, vShift);
-                v3sub(vb, vb, vShift);
-                builder.addFixedCountDashes(va, vb, aromaticSegmentCount, aromaticScale, dashCap, dashCap, edgeIndex);
+                v3sub(vm, vm, vShift);
+                builder.addFixedCountDashes(va, vm, aromaticDashCount, aromaticScale, dashCap, dashCap, linkStub, edgeIndex);
 
                 if (linkStyle === LinkStyle.MirroredAromatic) {
                     v3setMagnitude(vShift, vShift, aromaticOffset * 2);
                     v3add(va, va, vShift);
-                    v3add(vb, vb, vShift);
-                    builder.addFixedCountDashes(va, vb, aromaticSegmentCount, aromaticScale, dashCap, dashCap, edgeIndex);
+                    v3add(vm, vm, vShift);
+                    builder.addFixedCountDashes(va, vm, aromaticDashCount, aromaticScale, dashCap, dashCap, linkStub, edgeIndex);
                 }
             } else if (linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.OffsetTriple) {
                 const multipleOffset = linkRadius + multiScale * linkRadius + linkScale * linkRadius * linkSpacing;
@@ -353,7 +329,7 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
 
                 builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, edgeIndex);
 
-                v3setMagnitude(tmpV12, v3sub(tmpV12, va, vb), linkRadius / 1.5);
+                v3setMagnitude(tmpV12, v3sub(tmpV12, va, vm), linkRadius / 1.5);
                 v3sub(va, va, tmpV12);
 
                 if (order === 3) builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], multiScale, linkCap, linkStub, edgeIndex);
@@ -366,11 +342,11 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
                 builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], multiScale, linkCap, linkStub, edgeIndex);
             }
         } else if (linkStyle === LinkStyle.Disk) {
-            v3scale(tmpV12, v3sub(tmpV12, vb, va), 0.475);
+            v3scale(tmpV12, v3sub(tmpV12, vm, va), 0.475);
             v3add(va, va, tmpV12);
-            v3sub(vb, vb, tmpV12);
+            v3sub(vm, vm, tmpV12);
 
-            builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, linkStub, edgeIndex);
+            builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, edgeIndex);
         }
     }
 
@@ -409,12 +385,6 @@ export function createLinkLines(ctx: VisualContext, linkBuilder: LinkBuilderProp
     const center = Vec3();
     let count = 0;
 
-    // automatically adjust length for evenly spaced dashed lines
-    const segmentCount = dashCount % 2 === 1 ? dashCount : dashCount + 1;
-    const lengthScale = 0.5 - (0.5 / 2 / segmentCount);
-
-    const aromaticSegmentCount = aromaticDashCount + 1;
-    const aromaticLengthScale = 0.5 - (0.5 / 2 / aromaticSegmentCount);
     const aromaticOffsetFactor = 4.5;
     const multipleOffsetFactor = 3;
 
@@ -433,14 +403,8 @@ export function createLinkLines(ctx: VisualContext, linkBuilder: LinkBuilderProp
             v3scale(vm, v3add(vm, va, vb), 0.5);
             builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
         } else if (linkStyle === LinkStyle.Dashed) {
-            if (segmentCount > 1) {
-                v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
-                v3sub(vb, vb, tmpV12);
-                builder.addFixedCountDashes(va, vb, segmentCount, edgeIndex);
-            } else {
-                v3scale(vm, v3add(vm, va, vb), 0.5);
-                builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
-            }
+            v3scale(vm, v3add(vm, va, vb), 0.5);
+            builder.addFixedCountDashes(va, vm, dashCount, edgeIndex);
         } else if (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple || linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
             const order = linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble ? 2 :
                 linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple ? 3 : 1.5;
@@ -453,26 +417,28 @@ export function createLinkLines(ctx: VisualContext, linkBuilder: LinkBuilderProp
             if (linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
                 builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
 
-                v3scale(tmpV12, v3sub(tmpV12, vb, va), aromaticLengthScale);
-                v3sub(vb, vb, tmpV12);
+                const aromaticOffset = absOffset * aromaticOffsetFactor;
 
-                v3setMagnitude(vShift, vShift, absOffset * aromaticOffsetFactor);
+                v3setMagnitude(tmpV12, v3sub(tmpV12, vb, va), aromaticOffset * 0.5);
+                v3add(va, va, tmpV12);
+
+                v3setMagnitude(vShift, vShift, aromaticOffset);
                 v3sub(va, va, vShift);
-                v3sub(vb, vb, vShift);
-                builder.addFixedCountDashes(va, vb, aromaticSegmentCount, edgeIndex);
+                v3sub(vm, vm, vShift);
+                builder.addFixedCountDashes(va, vm, aromaticDashCount, edgeIndex);
 
                 if (linkStyle === LinkStyle.MirroredAromatic) {
-                    v3setMagnitude(vShift, vShift, absOffset * aromaticOffsetFactor * 2);
+                    v3setMagnitude(vShift, vShift, aromaticOffset * 2);
                     v3add(va, va, vShift);
-                    v3add(vb, vb, vShift);
-                    builder.addFixedCountDashes(va, vb, aromaticSegmentCount, edgeIndex);
+                    v3add(vm, vm, vShift);
+                    builder.addFixedCountDashes(va, vm, aromaticDashCount, edgeIndex);
                 }
             } else if (linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.OffsetTriple) {
                 v3setMagnitude(vShift, vShift, absOffset * multipleOffsetFactor);
 
                 builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
 
-                v3scale(tmpV12, v3sub(tmpV12, va, vb), linkSpacing * linkScale);
+                v3scale(tmpV12, v3sub(tmpV12, va, vm), linkSpacing * linkScale);
                 v3sub(va, va, tmpV12);
 
                 if (order === 3) builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], edgeIndex);
@@ -485,12 +451,12 @@ export function createLinkLines(ctx: VisualContext, linkBuilder: LinkBuilderProp
                 builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], edgeIndex);
             }
         } else if (linkStyle === LinkStyle.Disk) {
-            v3scale(tmpV12, v3sub(tmpV12, vb, va), 0.475);
+            v3scale(tmpV12, v3sub(tmpV12, vm, va), 0.475);
             v3add(va, va, tmpV12);
-            v3sub(vb, vb, tmpV12);
+            v3sub(vm, vm, tmpV12);
 
             // TODO what to do here? Line as disk doesn't work well.
-            builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], edgeIndex);
+            builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], edgeIndex);
         }
     }
 
@@ -505,4 +471,4 @@ export function createLinkLines(ctx: VisualContext, linkBuilder: LinkBuilderProp
     } else {
         return { lines: l };
     }
-}
+}

+ 6 - 4
src/mol-script/language/symbol-table/core.ts

@@ -46,10 +46,6 @@ export const TTargs = Arguments.Dictionary({
     1: Argument(Type.Num)
 });
 
-const XX = { test: Argument(Type.Str) };
-const t: Arguments.PropTypes<typeof XX> = 0 as any;
-t.test;
-
 const type = {
     '@header': 'Types',
     bool: symbol(Arguments.Dictionary({ 0: Argument(Type.AnyValue) }), Type.Bool, 'Convert a value to boolean.'),
@@ -116,10 +112,16 @@ const math = {
     min: binOp(Type.Num),
     max: binOp(Type.Num),
 
+    cantorPairing: binRel(Type.Num, Type.Num),
+    sortedCantorPairing: binRel(Type.Num, Type.Num),
+    invertCantorPairing: symbol(Arguments.Dictionary({ 0: Argument(Type.Num) }), Types.List(Type.Num)),
+
     floor: unaryOp(Type.Num),
     ceil: unaryOp(Type.Num),
     roundInt: unaryOp(Type.Num),
+    trunc: unaryOp(Type.Num),
     abs: unaryOp(Type.Num),
+    sign: unaryOp(Type.Num),
     sqrt: unaryOp(Type.Num),
     cbrt: unaryOp(Type.Num),
     sin: unaryOp(Type.Num),

+ 7 - 1
src/mol-script/runtime/query/table.ts

@@ -12,7 +12,7 @@ import { ElementSymbol, BondType, SecondaryStructureType } from '../../../mol-mo
 import { SetUtils } from '../../../mol-util/set';
 import { upperCaseAny } from '../../../mol-util/string';
 import { VdwRadius, AtomWeight, AtomNumber } from '../../../mol-model/structure/model/properties/atomic';
-import { cantorPairing } from '../../../mol-data/util';
+import { cantorPairing, invertCantorPairing, sortedCantorPairing } from '../../../mol-data/util';
 import { bundleElementImpl, bundleGenerator } from '../../../mol-model/structure/query/queries/internal';
 import { arrayEqual } from '../../../mol-util/array';
 
@@ -118,10 +118,16 @@ const symbols = [
         return ret;
     }),
 
+    C(MolScript.core.math.cantorPairing, (ctx, v) => cantorPairing(v[0](ctx), v[1](ctx))),
+    C(MolScript.core.math.sortedCantorPairing, (ctx, v) => sortedCantorPairing(v[0](ctx), v[1](ctx))),
+    C(MolScript.core.math.invertCantorPairing, (ctx, v) => invertCantorPairing([0, 0], v[0](ctx))),
+
     C(MolScript.core.math.floor, (ctx, v) => Math.floor(v[0](ctx))),
     C(MolScript.core.math.ceil, (ctx, v) => Math.ceil(v[0](ctx))),
     C(MolScript.core.math.roundInt, (ctx, v) => Math.round(v[0](ctx))),
+    C(MolScript.core.math.trunc, (ctx, v) => Math.trunc(v[0](ctx))),
     C(MolScript.core.math.abs, (ctx, v) => Math.abs(v[0](ctx))),
+    C(MolScript.core.math.sign, (ctx, v) => Math.sign(v[0](ctx))),
     C(MolScript.core.math.sqrt, (ctx, v) => Math.sqrt(v[0](ctx))),
     C(MolScript.core.math.cbrt, (ctx, v) => Math.cbrt(v[0](ctx))),
     C(MolScript.core.math.sin, (ctx, v) => Math.sin(v[0](ctx))),

+ 5 - 0
src/mol-script/script/mol-script/symbols.ts

@@ -54,10 +54,15 @@ export const SymbolTable = [
         Alias(MolScript.core.math.mod, 'mod'),
         Alias(MolScript.core.math.min, 'min'),
         Alias(MolScript.core.math.max, 'max'),
+        Alias(MolScript.core.math.cantorPairing, 'cantor-pairing'),
+        Alias(MolScript.core.math.sortedCantorPairing, 'sorted-cantor-pairing'),
+        Alias(MolScript.core.math.invertCantorPairing, 'invert-cantor-pairing'),
         Alias(MolScript.core.math.floor, 'floor'),
         Alias(MolScript.core.math.ceil, 'ceil'),
         Alias(MolScript.core.math.roundInt, 'round'),
+        Alias(MolScript.core.math.trunc, 'trunc'),
         Alias(MolScript.core.math.abs, 'abs'),
+        Alias(MolScript.core.math.sign, 'sign'),
         Alias(MolScript.core.math.sqrt, 'sqrt'),
         Alias(MolScript.core.math.cbrt, 'cbrt'),
         Alias(MolScript.core.math.sin, 'sin'),

Some files were not shown because too many files changed in this diff