Browse Source

wip, polymer repr

Alexander Rose 6 years ago
parent
commit
af338aff39

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

@@ -7,7 +7,7 @@
 import { ValueCell } from 'mol-util/value-cell'
 
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
-import { Unit, Element, StructureProperties } from 'mol-model/structure';
+import { Unit } from 'mol-model/structure';
 import { DefaultStructureProps, UnitsVisual } from '../index';
 import { RuntimeContext } from 'mol-task'
 import { createTransforms, createColors } from './util/common';
@@ -16,101 +16,36 @@ import { MeshValues } from 'mol-gl/renderable';
 import { getMeshData } from '../../../util/mesh-data';
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
-import { Segmentation } from 'mol-data/int';
 import { createMarkers, MarkerAction } from '../../../util/marker-data';
-import { Loci, EmptyLoci } from 'mol-model/loci';
+import { Loci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util';
 import { MeshBuilder } from '../../../shape/mesh-builder';
-import { Vec3 } from 'mol-math/linear-algebra';
-import { getPolymerElementCount } from './util/polymer';
-import { MoleculeType } from 'mol-model/structure/model/types';
+import { getPolymerElementCount, PolymerBackboneIterator } from './util/polymer';
+import { getElementLoci, markElement } from './util/element';
 
 async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
     const polymerElementCount = getPolymerElementCount(unit)
+    console.log('polymerElementCount backbone', polymerElementCount)
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 
     // TODO better vertex count estimates
     const builder = MeshBuilder.create(polymerElementCount * 30, polymerElementCount * 30 / 2, mesh)
 
-    const chemCompMap = unit.model.properties.chemicalComponentMap
-    const { elements } = unit
-    const curV = Vec3.zero()
-    const prevV = Vec3.zero()
-    const pos = unit.conformation.invariantPosition
-    const l = Element.Location(unit)
-
-    if (Unit.isAtomic(unit)) {
-        const { polymerSegments, residueSegments } = unit.model.atomicHierarchy
-        const polymerIt = Segmentation.transientSegments(polymerSegments, elements);
-        const residuesIt = Segmentation.transientSegments(residueSegments, elements);
-
-        let i = 0
-        let first = true
-
-        while (polymerIt.hasNext) {
-            const polymerSegment = polymerIt.move();
-            residuesIt.setSegment(polymerSegment);
-            first = true
-            while (residuesIt.hasNext) {
-                const residueSegment = residuesIt.move();
-                l.element = elements[residueSegment.start];
-
-                const compId = StructureProperties.residue.label_comp_id(l)
-                const cc = chemCompMap.get(compId)
-                const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
-                let traceName = ''
-                if (moleculeType === MoleculeType.protein) {
-                    traceName = 'CA'
-                } else if (moleculeType === MoleculeType.DNA || moleculeType === MoleculeType.RNA) {
-                    traceName = 'P'
-                }
-
-                for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
-                    l.element = elements[j];
-                    if (StructureProperties.atom.label_atom_id(l) === traceName) break
-                }
-                pos(l.element, curV)
-
-                if (!first) {
-                    builder.setId(residueSegment.start)
-                    builder.addCylinder(prevV, curV, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
-                } else {
-                    first = false
-                }
-
-                Vec3.copy(prevV, curV)
-
-                if (i % 10000 === 0 && ctx.shouldUpdate) {
-                    await ctx.update({ message: 'Backbone mesh', current: i, max: polymerElementCount });
-                }
-                ++i
-            }
-        }
-    } else if (Unit.isSpheres(unit)) {
-        let prevSeqIdEnd = -1
-        for (let i = 0, il = elements.length; i < il; ++i) {
-            l.element = elements[i]
-            if (StructureProperties.entity.type(l) !== 'polymer') continue;
-
-            pos(elements[i], curV)
-            const seqIdBegin = StructureProperties.coarse.seq_id_begin(l)
-            const seqIdEnd = StructureProperties.coarse.seq_id_end(l)
-
-            pos(elements[i], curV)
-
-            if (seqIdBegin - 1 === prevSeqIdEnd) {
-                builder.setId(i)
-                builder.addCylinder(prevV, curV, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
-            }
-
-            Vec3.copy(prevV, curV)
-            prevSeqIdEnd = seqIdEnd
-
-            if (i % 10000 === 0 && ctx.shouldUpdate) {
-                await ctx.update({ message: 'Backbone mesh', current: i, max: polymerElementCount });
-            }
+    let i = 0
+    const polymerTraceIt = PolymerBackboneIterator(unit)
+    while (polymerTraceIt.hasNext) {
+        const { indexA, indexB, posA, posB } = polymerTraceIt.move()
+        builder.setId(indexA)
+        // TODO size theme
+        builder.addCylinder(posA, posB, 0.5, { radiusTop: 0.2, radiusBottom: 0.2 })
+        builder.setId(indexB)
+        builder.addCylinder(posB, posA, 0.5, { radiusTop: 0.2, radiusBottom: 0.2 })
+
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'Backbone mesh', current: i, max: polymerElementCount });
         }
+        ++i
     }
 
     return builder.getMesh()
@@ -145,14 +80,10 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> {
             mesh = unitKinds.includes(unit.kind)
                 ? await createPolymerBackboneCylinderMesh(ctx, unit, mesh)
                 : Mesh.createEmpty(mesh)
+            console.log(mesh)
 
-            if (ctx.shouldUpdate) await ctx.update('Computing trace transforms');
             const transforms = createTransforms(group)
-
-            if (ctx.shouldUpdate) await ctx.update('Computing trace colors');
             const color = createColors(group, elementCount, colorTheme)
-
-            if (ctx.shouldUpdate) await ctx.update('Computing trace marks');
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -164,6 +95,7 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> {
                 aTransform: transforms,
                 elements: mesh.indexBuffer,
                 ...createMeshValues(currentProps, counts),
+                aColor: ValueCell.create(new Float32Array(mesh.vertexCount * 3))
             }
             const state = createRenderableState(currentProps)
 
@@ -200,11 +132,10 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> {
             return true
         },
         getLoci(pickingId: PickingId) {
-            // TODO
-            return EmptyLoci
+            return getElementLoci(renderObject.id, currentGroup, pickingId)
         },
         mark(loci: Loci, action: MarkerAction) {
-            // TODO
+            markElement(renderObject.values.tMarker, currentGroup, loci, action)
         },
         destroy() {
             // TODO

+ 16 - 74
src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts

@@ -7,7 +7,7 @@
 import { ValueCell } from 'mol-util/value-cell'
 
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
-import { Unit, Element, StructureProperties } from 'mol-model/structure';
+import { Unit, Element } from 'mol-model/structure';
 import { DefaultStructureProps, UnitsVisual } from '../index';
 import { RuntimeContext } from 'mol-task'
 import { createTransforms, createColors } from './util/common';
@@ -17,14 +17,13 @@ import { MeshValues } from 'mol-gl/renderable';
 import { getMeshData } from '../../../util/mesh-data';
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
-import { OrderedSet, Segmentation } from 'mol-data/int';
+import { OrderedSet } from 'mol-data/int';
 import { createMarkers, MarkerAction } from '../../../util/marker-data';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util';
 import { MeshBuilder } from '../../../shape/mesh-builder';
-import { Vec3 } from 'mol-math/linear-algebra';
-import { getPolymerElementCount } from './util/polymer';
+import { getPolymerElementCount, PolymerBackboneIterator } from './util/polymer';
 
 async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
     const polymerElementCount = getPolymerElementCount(unit)
@@ -34,77 +33,20 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me
     // TODO better vertex count estimates
     const builder = MeshBuilder.create(polymerElementCount * 30, polymerElementCount * 30 / 2, mesh)
 
-    const { elements } = unit
-    const curV = Vec3.zero()
-    const prevV = Vec3.zero()
-    const pos = unit.conformation.invariantPosition
-    const l = Element.Location(unit)
-
-    if (Unit.isAtomic(unit)) {
-        const { chainSegments, residueSegments } = unit.model.atomicHierarchy
-        const chainsIt = Segmentation.transientSegments(chainSegments, elements);
-        const residuesIt = Segmentation.transientSegments(residueSegments, elements);
-
-        let i = 0
-        let prevSeqId = -1
-
-        while (chainsIt.hasNext) {
-            const chainSegment = chainsIt.move();
-            residuesIt.setSegment(chainSegment);
-            while (residuesIt.hasNext) {
-                const residueSegment = residuesIt.move();
-                l.element = elements[residueSegment.start];
-                if (StructureProperties.entity.type(l) !== 'polymer') continue;
-
-                const seqId = StructureProperties.residue.label_seq_id(l)
-
-                // for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
-                //     l.element = elements[j];
-                // }
-                // TODO get proper trace element
-                pos(l.element, curV)
-
-                if (seqId - 1 === prevSeqId) {
-                    // TODO draw trace
-                    builder.setId(residueSegment.start)
-                    builder.addCylinder(prevV, curV, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
-                }
-
-                Vec3.copy(prevV, curV)
-                prevSeqId = seqId
-
-                if (i % 10000 === 0 && ctx.shouldUpdate) {
-                    await ctx.update({ message: 'Cartoon mesh', current: i, max: polymerElementCount });
-                }
-                ++i
-            }
-        }
-    } else if (Unit.isSpheres(unit)) {
-        let prevSeqIdEnd = -1
-        for (let i = 0, il = elements.length; i < il; ++i) {
-            l.element = elements[i]
-            if (StructureProperties.entity.type(l) !== 'polymer') continue;
-            // console.log(elementLabel(l), StructureProperties.entity.type(l))
-
-            pos(elements[i], curV)
-            const seqIdBegin = StructureProperties.coarse.seq_id_begin(l)
-            const seqIdEnd = StructureProperties.coarse.seq_id_end(l)
-
-            pos(elements[i], curV)
-
-            if (seqIdBegin - 1 === prevSeqIdEnd) {
-                // TODO draw trace
-                builder.setId(i)
-                builder.addCylinder(prevV, curV, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
-            }
-
-            Vec3.copy(prevV, curV)
-            prevSeqIdEnd = seqIdEnd
-
-            if (i % 10000 === 0 && ctx.shouldUpdate) {
-                await ctx.update({ message: 'Backbone mesh', current: i, max: polymerElementCount });
-            }
+    let i = 0
+    const polymerTraceIt = PolymerBackboneIterator(unit)
+    while (polymerTraceIt.hasNext) {
+        const v = polymerTraceIt.move()
+        builder.setId(v.indexA)
+        // TODO size theme
+        builder.addCylinder(v.posA, v.posB, 0.5, { radiusTop: 0.2, radiusBottom: 0.2 })
+        builder.setId(v.indexB)
+        builder.addCylinder(v.posB, v.posA, 0.5, { radiusTop: 0.2, radiusBottom: 0.2 })
+
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'Backbone mesh', current: i, max: polymerElementCount });
         }
+        ++i
     }
 
     return builder.getMesh()

+ 310 - 60
src/mol-geo/representation/structure/visual/util/polymer.ts

@@ -6,7 +6,11 @@
 
 import { Unit, Element, StructureProperties } from 'mol-model/structure';
 import { Segmentation } from 'mol-data/int';
-// import Iterator from 'mol-data/iterator';
+import { MoleculeType } from 'mol-model/structure/model/types';
+import Iterator from 'mol-data/iterator';
+import { SegmentIterator } from 'mol-data/int/impl/segmentation';
+import { Vec3 } from 'mol-math/linear-algebra';
+import { SymmetryOperator } from 'mol-math/geometry';
 
 export function getPolymerElementCount(unit: Unit) {
     let count = 0
@@ -32,62 +36,308 @@ export function getPolymerElementCount(unit: Unit) {
     return count
 }
 
-// interface PolymerTrace {
-//     center: Element
-//     direction: Element
-// }
-
-// export class PolymerTraceIterator<T extends number = number> implements Iterator<PolymerTrace> {
-//     private segmentMin = 0;
-//     private segmentMax = 0;
-//     private setRange = Interval.Empty;
-//     private value: TraceSegment = { index: 0, start: 0 as T, end: 0 as T };
-
-//     hasNext: boolean = false;
-
-//     move() {
-//         while (this.hasNext) {
-//             if (this.updateValue()) {
-//                 this.value.index = this.segmentMin++;
-//                 this.hasNext = this.segmentMax >= this.segmentMin && Interval.size(this.setRange) > 0;
-//                 break;
-//             } else {
-//                 this.updateSegmentRange();
-//             }
-//         }
-//         return this.value;
-//     }
-
-//     private updateValue() {
-//         const segmentEnd = this.segments[this.segmentMin + 1];
-//         // TODO: add optimized version for interval and array?
-//         const setEnd = OrderedSet.findPredecessorIndexInInterval(this.set, segmentEnd, this.setRange);
-//         this.value.start = Interval.start(this.setRange) as T;
-//         this.value.end = setEnd as T;
-//         this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange));
-//         return setEnd > this.value.start;
-//     }
-
-//     private updateSegmentRange() {
-//         const sMin = Interval.min(this.setRange), sMax = Interval.max(this.setRange);
-//         if (sMax < sMin) {
-//             this.hasNext = false;
-//             return;
-//         }
-
-//         this.segmentMin = this.segmentMap[OrderedSet.getAt(this.set, sMin)];
-//         this.segmentMax = this.segmentMap[OrderedSet.getAt(this.set, sMax)];
-
-//         this.hasNext = this.segmentMax >= this.segmentMin;
-//     }
-
-//     setSegment(segment: Segs.Segment<T>) {
-//         this.setRange = Interval.ofBounds(segment.start, segment.end);
-//         this.updateSegmentRange();
-//     }
-
-//     constructor(private segments: SortedArray, private segmentMap: Int32Array, private set: OrderedSet, inputRange: Interval) {
-//         this.setRange = inputRange;
-//         this.updateSegmentRange();
-//     }
-// }
+function getTraceName(l: Element.Location) {
+    const compId = StructureProperties.residue.label_comp_id(l)
+    const chemCompMap = l.unit.model.properties.chemicalComponentMap
+    const cc = chemCompMap.get(compId)
+    const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
+    let traceName = ''
+    if (moleculeType === MoleculeType.protein) {
+        traceName = 'CA'
+    } else if (moleculeType === MoleculeType.DNA || moleculeType === MoleculeType.RNA) {
+        traceName = 'P'
+    }
+    return traceName
+}
+
+function setTraceElement(l: Element.Location, residueSegment: Segmentation.Segment<Element>) {
+    const elements = l.unit.elements
+    l.element = elements[residueSegment.start]
+    const traceName = getTraceName(l)
+
+    for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
+        l.element = elements[j];
+        if (StructureProperties.atom.label_atom_id(l) === traceName) return j
+    }
+    return residueSegment.end - 1
+}
+
+/** Iterates over consecutive pairs of residues/coarse elements in polymers */
+export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePair> {
+    switch (unit.kind) {
+        case Unit.Kind.Atomic: return new AtomicPolymerBackboneIterator(unit)
+        case Unit.Kind.Spheres:
+        case Unit.Kind.Gaussians:
+            return new CoarsePolymerBackboneIterator(unit)
+    }
+}
+
+interface PolymerBackbonePair {
+    centerA: Element.Location
+    centerB: Element.Location
+    indexA: number
+    indexB: number
+    posA: Vec3
+    posB: Vec3
+}
+
+function createPolymerBackbonePair (unit: Unit) {
+    return {
+        centerA: Element.Location(unit),
+        centerB: Element.Location(unit),
+        indexA: 0,
+        indexB: 0,
+        posA: Vec3.zero(),
+        posB: Vec3.zero()
+    }
+}
+
+const enum AtomicPolymerBackboneIteratorState { nextPolymer, firstResidue, nextResidue }
+
+export class AtomicPolymerBackboneIterator<T extends number = number> implements Iterator<PolymerBackbonePair> {
+    private value: PolymerBackbonePair
+
+    private polymerIt: SegmentIterator<Element>
+    private residueIt: SegmentIterator<Element>
+    private polymerSegment: Segmentation.Segment<Element>
+    private state: AtomicPolymerBackboneIteratorState = AtomicPolymerBackboneIteratorState.nextPolymer
+    private pos: SymmetryOperator.CoordinateMapper
+
+    hasNext: boolean = false;
+
+    move() {
+        const { residueIt, polymerIt, value, pos } = this
+
+        if (this.state === AtomicPolymerBackboneIteratorState.nextPolymer) {
+            if (polymerIt.hasNext) {
+                this.polymerSegment = polymerIt.move();
+                residueIt.setSegment(this.polymerSegment);
+                this.state = AtomicPolymerBackboneIteratorState.firstResidue
+            }
+        }
+
+        if (this.state === AtomicPolymerBackboneIteratorState.firstResidue) {
+            const residueSegment = residueIt.move();
+            if (residueIt.hasNext) {
+                value.indexB = setTraceElement(value.centerB, residueSegment)
+                pos(value.centerB.element, value.posB)
+                this.state = AtomicPolymerBackboneIteratorState.nextResidue
+            } else {
+                this.state = AtomicPolymerBackboneIteratorState.nextPolymer
+            }
+
+        }
+
+        if (this.state === AtomicPolymerBackboneIteratorState.nextResidue) {
+            const residueSegment = residueIt.move();
+            value.centerA.element = value.centerB.element
+            value.indexA = value.indexB
+            Vec3.copy(value.posA, value.posB)
+            value.indexB = setTraceElement(value.centerB, residueSegment)
+            pos(value.centerB.element, value.posB)
+
+            if (!residueIt.hasNext) {
+                this.state = AtomicPolymerBackboneIteratorState.nextPolymer
+            }
+        }
+
+        this.hasNext = residueIt.hasNext || polymerIt.hasNext
+
+        return this.value;
+    }
+
+    constructor(unit: Unit.Atomic) {
+        const { polymerSegments, residueSegments } = unit.model.atomicHierarchy
+        this.polymerIt = Segmentation.transientSegments(polymerSegments, unit.elements);
+        this.residueIt = Segmentation.transientSegments(residueSegments, unit.elements);
+        this.pos = unit.conformation.invariantPosition
+        this.value = createPolymerBackbonePair(unit)
+        this.hasNext = this.residueIt.hasNext || this.polymerIt.hasNext
+    }
+}
+
+const enum CoarsePolymerBackboneIteratorState { nextPolymer, firstElement, nextElement }
+
+export class CoarsePolymerBackboneIterator<T extends number = number> implements Iterator<PolymerBackbonePair> {
+    private value: PolymerBackbonePair
+
+    private polymerIt: SegmentIterator<Element>
+    private polymerSegment: Segmentation.Segment<Element>
+    private state: CoarsePolymerBackboneIteratorState = CoarsePolymerBackboneIteratorState.nextPolymer
+    private pos: SymmetryOperator.CoordinateMapper
+    private elementIndex: number
+
+    hasNext: boolean = false;
+
+    move() {
+        const { polymerIt, value, pos } = this
+
+        if (this.state === CoarsePolymerBackboneIteratorState.nextPolymer) {
+            if (polymerIt.hasNext) {
+                this.polymerSegment = polymerIt.move();
+                this.elementIndex = this.polymerSegment.start
+                this.state = CoarsePolymerBackboneIteratorState.firstElement
+            }
+        }
+
+        if (this.state === CoarsePolymerBackboneIteratorState.firstElement) {
+            this.elementIndex += 1
+            if (this.elementIndex + 1 < this.polymerSegment.end) {
+                value.centerB.element = value.centerB.unit.elements[this.elementIndex]
+                value.indexB = this.elementIndex
+                pos(value.centerB.element, value.posB)
+
+                this.state = CoarsePolymerBackboneIteratorState.nextElement
+            } else {
+                this.state = CoarsePolymerBackboneIteratorState.nextPolymer
+            }
+
+        }
+
+        if (this.state === CoarsePolymerBackboneIteratorState.nextElement) {
+            this.elementIndex += 1
+            value.centerA.element = value.centerB.element
+            value.indexA = value.indexB
+            Vec3.copy(value.posA, value.posB)
+
+            value.centerB.element = value.centerB.unit.elements[this.elementIndex]
+            value.indexB = this.elementIndex
+            pos(value.centerB.element, value.posB)
+
+            if (this.elementIndex + 1 >= this.polymerSegment.end) {
+                this.state = CoarsePolymerBackboneIteratorState.nextPolymer
+            }
+        }
+
+        this.hasNext = this.elementIndex + 1 < this.polymerSegment.end || polymerIt.hasNext
+
+        return this.value;
+    }
+
+    constructor(unit: Unit.Spheres | Unit.Gaussians) {
+        const { polymerSegments } = Unit.isSpheres(unit)
+            ? unit.model.coarseHierarchy.spheres
+            : unit.model.coarseHierarchy.gaussians
+        this.polymerIt = Segmentation.transientSegments(polymerSegments, unit.elements);
+
+        this.pos = unit.conformation.invariantPosition
+        this.value = createPolymerBackbonePair(unit)
+
+        this.hasNext = this.polymerIt.hasNext
+    }
+}
+
+
+
+
+/**
+ * Iterates over individual residues/coarse elements in polymers while providing information
+ * about the neighbourhood in the underlying model for drawing splines
+ */
+export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement> {
+    switch (unit.kind) {
+        case Unit.Kind.Atomic: return new AtomicPolymerTraceIterator(unit)
+        case Unit.Kind.Spheres:
+        case Unit.Kind.Gaussians:
+            return new CoarsePolymerTraceIterator(unit)
+    }
+}
+
+interface PolymerTraceElement {
+    center: Element.Location
+    index: number
+    pos: Vec3
+    posPrev: Vec3
+    posNext: Vec3
+    posNextNext: Vec3
+}
+
+function createPolymerTraceElement (unit: Unit) {
+    return {
+        center: Element.Location(unit),
+        index: 0,
+        pos: Vec3.zero(),
+        posPrev: Vec3.zero(),
+        posNext: Vec3.zero(),
+        posNextNext: Vec3.zero()
+    }
+}
+
+// const enum AtomicPolymerTraceIteratorState { nextPolymer, firstResidue, nextResidue }
+
+export class AtomicPolymerTraceIterator<T extends number = number> implements Iterator<PolymerTraceElement> {
+    private value: PolymerTraceElement
+
+    private polymerIt: SegmentIterator<Element>
+    private residueIt: SegmentIterator<Element>
+    // private polymerSegment: Segmentation.Segment<Element>
+    // private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer
+    // private pos: SymmetryOperator.CoordinateMapper
+
+    hasNext: boolean = false;
+
+    move() {
+        // const { residueIt, polymerIt, value, pos } = this
+
+        // if (this.state === AtomicPolymerTraceIteratorState.nextPolymer) {
+        //     if (polymerIt.hasNext) {
+        //         this.polymerSegment = polymerIt.move();
+        //         residueIt.setSegment(this.polymerSegment);
+        //         this.state = AtomicPolymerTraceIteratorState.firstResidue
+        //     }
+        // }
+
+        // if (this.state === AtomicPolymerTraceIteratorState.firstResidue) {
+        //     const residueSegment = residueIt.move();
+        //     if (residueIt.hasNext) {
+        //         value.indexB = setTraceElement(value.centerB, residueSegment)
+        //         pos(value.centerB.element, value.posB)
+        //         this.state = AtomicPolymerTraceIteratorState.nextResidue
+        //     } else {
+        //         this.state = AtomicPolymerTraceIteratorState.nextPolymer
+        //     }
+
+        // }
+
+        // if (this.state === AtomicPolymerTraceIteratorState.nextResidue) {
+        //     const residueSegment = residueIt.move();
+        //     value.centerA.element = value.centerB.element
+        //     value.indexA = value.indexB
+        //     Vec3.copy(value.posA, value.posB)
+        //     value.indexB = setTraceElement(value.centerB, residueSegment)
+        //     pos(value.centerB.element, value.posB)
+
+        //     if (!residueIt.hasNext) {
+        //         this.state = AtomicPolymerTraceIteratorState.nextPolymer
+        //     }
+        // }
+
+        // this.hasNext = residueIt.hasNext || polymerIt.hasNext
+
+        return this.value;
+    }
+
+    constructor(unit: Unit.Atomic) {
+        const { polymerSegments, residueSegments } = unit.model.atomicHierarchy
+        this.polymerIt = Segmentation.transientSegments(polymerSegments, unit.elements);
+        this.residueIt = Segmentation.transientSegments(residueSegments, unit.elements);
+        // this.pos = unit.conformation.invariantPosition
+        this.value = createPolymerTraceElement(unit)
+        this.hasNext = this.residueIt.hasNext || this.polymerIt.hasNext
+    }
+}
+
+export class CoarsePolymerTraceIterator<T extends number = number> implements Iterator<PolymerTraceElement> {
+    private value: PolymerTraceElement
+
+    hasNext: boolean = false;
+
+    move() {
+        return this.value;
+    }
+
+    constructor(unit: Unit.Spheres | Unit.Gaussians) {
+        this.value = createPolymerTraceElement(unit)
+        this.hasNext = false
+    }
+}

+ 20 - 6
src/mol-view/stage.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import Viewer from 'mol-view/viewer'
+import Viewer from './viewer'
 import { StateContext } from './state/context';
 import { Progress } from 'mol-task';
 import { MmcifUrlToModel, ModelToStructure, StructureToSpacefill, StructureToBallAndStick, StructureToDistanceRestraint, StructureToCartoon, StructureToBackbone } from './state/transform';
@@ -15,6 +15,7 @@ import { BallAndStickProps } from 'mol-geo/representation/structure/ball-and-sti
 import { CartoonProps } from 'mol-geo/representation/structure/cartoon';
 import { DistanceRestraintProps } from 'mol-geo/representation/structure/distance-restraint';
 import { BackboneProps } from 'mol-geo/representation/structure/backbone';
+import { Queries as Q, StructureProperties as SP, Query, Selection } from 'mol-model/structure';
 
 const spacefillProps: SpacefillProps = {
     doubleSided: true,
@@ -26,7 +27,8 @@ const spacefillProps: SpacefillProps = {
 const ballAndStickProps: BallAndStickProps = {
     doubleSided: true,
     colorTheme: { name: 'chain-id' },
-    sizeTheme: { name: 'uniform', value: 0.25 },
+    sizeTheme: { name: 'uniform', value: 0.05 },
+    linkRadius: 0.05,
     quality: 'auto',
     useFog: false
 }
@@ -68,16 +70,20 @@ export class Stage {
 
         // this.loadPdbid('1jj2')
         // this.loadPdbid('4umt') // ligand has bond with order 3
-        // this.loadPdbid('1crn') // small
+        this.loadPdbid('1crn') // small
         // this.loadPdbid('1rb8') // virus
         // this.loadPdbid('1blu') // metal coordination
         // this.loadPdbid('3pqr') // inter unit bonds
         // this.loadPdbid('4v5a') // ribosome
         // this.loadPdbid('3j3q') // ...
-        this.loadPdbid('3sn6') // ...
+        // this.loadPdbid('3sn6') // discontinuous chains
         // this.loadMmcifUrl(`../../examples/1cbs_full.bcif`)
 
         // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000001.cif`)
+        // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000002.cif`)
+        // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000003.cif`)
+        // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000004.cif`)
+        // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000005.cif`)
     }
 
     async loadMmcifUrl (url: string) {
@@ -85,13 +91,21 @@ export class Stage {
         const modelEntity = await MmcifUrlToModel.apply(this.ctx, urlEntity)
         const structureEntity = await ModelToStructure.apply(this.ctx, modelEntity)
 
+        StructureToBallAndStick.apply(this.ctx, structureEntity, { ...ballAndStickProps, visible: true })
         StructureToSpacefill.apply(this.ctx, structureEntity, { ...spacefillProps, visible: false })
-        StructureToBallAndStick.apply(this.ctx, structureEntity, { ...ballAndStickProps, visible: false })
         StructureToDistanceRestraint.apply(this.ctx, structureEntity, { ...distanceRestraintProps, visible: false })
-        StructureToBackbone.apply(this.ctx, structureEntity, { ...backboneProps, visible: true })
+        // StructureToBackbone.apply(this.ctx, structureEntity, { ...backboneProps, visible: true })
         StructureToCartoon.apply(this.ctx, structureEntity, { ...cartoonProps, visible: false })
 
         this.globalContext.components.sequenceView.setState({ structure: structureEntity.value });
+
+        const structureEntity2 = await ModelToStructure.apply(this.ctx, modelEntity)
+        const q1 = Q.generators.atoms({
+            residueTest: l => SP.residue.label_seq_id(l) > 30
+        });
+        structureEntity2.value = Selection.unionStructure(await Query(q1)(structureEntity2.value).run());
+        StructureToBackbone.apply(this.ctx, structureEntity2, { ...backboneProps, visible: true })
+        StructureToCartoon.apply(this.ctx, structureEntity2, { ...cartoonProps, visible: true })
     }
 
     loadPdbid (pdbid: string) {