Bladeren bron

added coarse polymer trace, more polymer iterator cleanup

Alexander Rose 6 jaren geleden
bovenliggende
commit
7075087c40

+ 13 - 13
src/mol-data/int/sorted-ranges.ts

@@ -24,24 +24,24 @@ namespace SortedRanges {
         return size
     }
 
-    export function transientSegments<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
-        return new Iterator<T>(ranges, set)
+    export function transientSegments<T extends number = number, I extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
+        return new Iterator<T, I>(ranges, set)
     }
 
-    export class Iterator<I extends number = number> implements _Iterator<Segmentation.Segment<I>> {
-        private value: Segmentation.Segment<I> = { index: 0 as I, start: 0, end: 0 }
-    
+    export class Iterator<T extends number = number, I extends number = number> implements _Iterator<Segmentation.Segment<I>> {
+        private value: Segmentation.Segment<I> = { index: 0 as I, start: 0 as T, end: 0 as T }
+
         private curIndex = 0
         private maxIndex = 0
-        private interval: Interval<I>
-        private curMin: I = 0 as I
-    
+        private interval: Interval<T>
+        private curMin: T = 0 as T
+
         hasNext: boolean = false;
-    
+
         updateInterval() {
             this.interval = Interval.ofRange(this.ranges[this.curIndex], this.ranges[this.curIndex + 1])
         }
-    
+
         updateValue() {
             this.value.index = this.curIndex / 2 as I
             this.value.start = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex])
@@ -61,13 +61,13 @@ namespace SortedRanges {
             }
             return this.value;
         }
-    
+
         getRangeIndex(value: number) {
             const index = SortedArray.findPredecessorIndex(this.ranges, value)
             return (index % 2 === 1) ? index - 1 : index
         }
-    
-        constructor(private ranges: SortedRanges<I>, private set: OrderedSet<I>) {
+
+        constructor(private ranges: SortedRanges<T>, private set: OrderedSet<T>) {
             if (ranges.length) {
                 this.curIndex = this.getRangeIndex(OrderedSet.min(set))
                 this.maxIndex = Math.min(ranges.length - 2, this.getRangeIndex(OrderedSet.max(set)))

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

@@ -43,11 +43,11 @@ async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit
     while (polymerBackboneIt.hasNext) {
         // TODO size theme
         const { centerA, centerB } = polymerBackboneIt.move()
-        pos(centerA.element, pA)
-        pos(centerB.element, pB)
-        builder.setId(elements[centerA.element])
+        pos(elements[centerA.element], pA)
+        pos(elements[centerB.element], pB)
+        builder.setId(centerA.element)
         builder.addCylinder(pA, pB, 0.5, { radiusTop: 0.2, radiusBottom: 0.2 })
-        builder.setId(elements[centerB.element])
+        builder.setId(centerB.element)
         builder.addCylinder(pB, pA, 0.5, { radiusTop: 0.2, radiusBottom: 0.2 })
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {

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

@@ -81,15 +81,13 @@ function interpolateNormals(controlPoints: Helpers.NumberArray, tangentVectors:
 
 async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
     const polymerElementCount = getPolymerElementCount(unit)
-    console.log('polymerElementCount', polymerElementCount)
+    console.log('polymerElementCount trace', polymerElementCount)
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 
-    const { elements } = unit
-
     // TODO better vertex count estimates
     const builder = MeshBuilder.create(polymerElementCount * 30, polymerElementCount * 30 / 2, mesh)
-    const linearSegments = 12
-    const radialSegments = 16
+    const linearSegments = 8
+    const radialSegments = 12
     const tension = 0.9
 
     const tanA = Vec3.zero()
@@ -110,7 +108,8 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me
     const polymerTraceIt = PolymerTraceIterator(unit)
     while (polymerTraceIt.hasNext) {
         const v = polymerTraceIt.move()
-        builder.setId(elements[v.center.element])
+        // builder.setId(elements[v.center.element])
+        builder.setId(v.center.element)
 
         for (let j = 0; j <= linearSegments; ++j) {
             const t = j * 1.0 / linearSegments;

+ 128 - 81
src/mol-geo/representation/structure/visual/util/polymer.ts

@@ -5,11 +5,12 @@
  */
 
 import { Unit, StructureElement, Model, ElementIndex, ResidueIndex } from 'mol-model/structure';
-import { Segmentation, OrderedSet, Interval } from 'mol-data/int';
+import { Segmentation, OrderedSet, Interval, SortedArray } from 'mol-data/int';
 import { MoleculeType, SecondaryStructureType } from 'mol-model/structure/model/types';
 import Iterator from 'mol-data/iterator';
 import { Vec3 } from 'mol-math/linear-algebra';
 import SortedRanges from 'mol-data/int/sorted-ranges';
+import { CoarseSphereConformation, CoarseGaussianConformation } from 'mol-model/structure/model/properties/coarse';
 
 export function getPolymerRanges(unit: Unit): SortedRanges<ElementIndex> {
     switch (unit.kind) {
@@ -71,25 +72,25 @@ function getResidueTypeAtomId(moleculeType: MoleculeType, atomType: 'trace' | 'd
     return ''
 }
 
-function getMoleculeType(model: Model, residueIndex: number) {
+function getMoleculeType(model: Model, residueIndex: ResidueIndex) {
     const compId = model.atomicHierarchy.residues.label_comp_id.value(residueIndex)
     const chemCompMap = model.properties.chemicalComponentMap
     const cc = chemCompMap.get(compId)
     return cc ? cc.moleculeType : MoleculeType.unknown
 }
 
-function getElementIndexForAtomId(unit: Unit.Atomic, residueSegment: Segmentation.Segment, atomId: string) {
-    const elements = unit.elements
-    const { label_atom_id } = unit.model.atomicHierarchy.atoms
-    for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
-        if (label_atom_id.value(elements[j]) === atomId) return j as ElementIndex
+function getElementIndexForAtomId(model: Model, rI: ResidueIndex, atomId: string): ElementIndex {
+    const { offsets } = model.atomicHierarchy.residueAtomSegments
+    const { label_atom_id } = model.atomicHierarchy.atoms
+    for (let j = offsets[rI], _j = offsets[rI + 1]; j < _j; j++) {
+        if (label_atom_id.value(j) === atomId) return j as ElementIndex
     }
-    return residueSegment.end - 1 as ElementIndex
+    return offsets[rI] as ElementIndex
 }
 
-function getResidueTypeAtomIdElementIndex(unit: Unit.Atomic, residueSegment: Segmentation.Segment, type: 'trace' | 'direction') {
-    const atomId = getResidueTypeAtomId(getMoleculeType(unit.model, residueSegment.index), type)
-    return getElementIndexForAtomId(unit, residueSegment, atomId)
+function getElementIndexForResidueTypeAtomId(model: Model, rI: ResidueIndex, atomType: 'trace' | 'direction') {
+    const atomId = getResidueTypeAtomId(getMoleculeType(model, rI), atomType)
+    return getElementIndexForAtomId(model, rI, atomId)
 }
 
 // function residueLabel(model: Model, rI: number) {
@@ -124,21 +125,32 @@ function createPolymerBackbonePair (unit: Unit) {
 
 const enum AtomicPolymerBackboneIteratorState { nextPolymer, firstResidue, nextResidue }
 
-export class AtomicPolymerBackboneIterator<T extends number = number> implements Iterator<PolymerBackbonePair> {
+export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePair> {
     private value: PolymerBackbonePair
-    private polymerIt: SortedRanges.Iterator<ElementIndex>
+    private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
     private residueIt: Segmentation.SegmentIterator<ResidueIndex>
     private state: AtomicPolymerBackboneIteratorState = AtomicPolymerBackboneIteratorState.nextPolymer
     hasNext: boolean = false;
 
+    getElementIndex(residueIndex: ResidueIndex, atomType: 'trace' | 'direction') {
+        const index = getElementIndexForResidueTypeAtomId(this.unit.model, residueIndex, atomType)
+        // // TODO handle case when it returns -1
+        // return SortedArray.indexOf(this.unit.elements, index) as ElementIndex
+
+        const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex
+        if (elementIndex === -1) {
+            console.log('-1', residueIndex, atomType, index)
+        }
+        return elementIndex === -1 ? 0 as ElementIndex : elementIndex
+    }
+
     move() {
         if (this.state === AtomicPolymerBackboneIteratorState.nextPolymer) {
             while (this.polymerIt.hasNext) {
                 const residueSegment = this.polymerIt.move()
                 this.residueIt.setSegment(residueSegment);
                 if (this.residueIt.hasNext) {
-                    this.value.centerB.element = getResidueTypeAtomIdElementIndex(this.unit, residueSegment, 'trace')
-                    // setTraceElement(this.value.centerB, this.residueIt.move())
+                    this.value.centerB.element = this.getElementIndex(this.residueIt.move().index, 'trace')
                     this.state = AtomicPolymerBackboneIteratorState.nextResidue
                     break
                 }
@@ -147,8 +159,7 @@ export class AtomicPolymerBackboneIterator<T extends number = number> implements
 
         if (this.state === AtomicPolymerBackboneIteratorState.nextResidue) {
             this.value.centerA.element = this.value.centerB.element
-            this.value.centerB.element = getResidueTypeAtomIdElementIndex(this.unit, this.residueIt.move(), 'trace')
-            // setTraceElement(this.value.centerB, this.residueIt.move())
+            this.value.centerB.element = this.getElementIndex(this.residueIt.move().index, 'trace')
             if (!this.residueIt.hasNext) {
                 // TODO need to advance to a polymer that has two or more residues (can't assume it has)
                 this.state = AtomicPolymerBackboneIteratorState.nextPolymer
@@ -169,10 +180,10 @@ export class AtomicPolymerBackboneIterator<T extends number = number> implements
 
 const enum CoarsePolymerBackboneIteratorState { nextPolymer, nextElement }
 
-export class CoarsePolymerBackboneIterator<T extends number = number> implements Iterator<PolymerBackbonePair> {
+export class CoarsePolymerBackboneIterator implements Iterator<PolymerBackbonePair> {
     private value: PolymerBackbonePair
-    private polymerIt: SortedRanges.Iterator<ElementIndex>
-    private polymerSegment: Segmentation.Segment<ElementIndex>
+    private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
+    private polymerSegment: Segmentation.Segment<ResidueIndex>
     private state: CoarsePolymerBackboneIteratorState = CoarsePolymerBackboneIteratorState.nextPolymer
     private elementIndex: number
     hasNext: boolean = false;
@@ -182,9 +193,8 @@ export class CoarsePolymerBackboneIterator<T extends number = number> implements
             if (this.polymerIt.hasNext) {
                 this.polymerSegment = this.polymerIt.move();
                 this.elementIndex = this.polymerSegment.start
-                // this.elementIndex += 1
                 if (this.elementIndex + 1 < this.polymerSegment.end) {
-                    this.value.centerB.element = this.value.centerB.unit.elements[this.elementIndex]
+                    this.value.centerB.element = this.unit.elements[this.elementIndex]
                     this.state = CoarsePolymerBackboneIteratorState.nextElement
                 } else {
                     this.state = CoarsePolymerBackboneIteratorState.nextPolymer
@@ -195,7 +205,7 @@ export class CoarsePolymerBackboneIterator<T extends number = number> implements
         if (this.state === CoarsePolymerBackboneIteratorState.nextElement) {
             this.elementIndex += 1
             this.value.centerA.element = this.value.centerB.element
-            this.value.centerB.element = this.value.centerB.unit.elements[this.elementIndex]
+            this.value.centerB.element = this.unit.elements[this.elementIndex]
             if (this.elementIndex + 1 >= this.polymerSegment.end) {
                 this.state = CoarsePolymerBackboneIteratorState.nextPolymer
             }
@@ -205,16 +215,13 @@ export class CoarsePolymerBackboneIterator<T extends number = number> implements
         return this.value;
     }
 
-    constructor(unit: Unit.Spheres | Unit.Gaussians) {
+    constructor(private unit: Unit.Spheres | Unit.Gaussians) {
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
         this.value = createPolymerBackbonePair(unit)
         this.hasNext = this.polymerIt.hasNext
     }
 }
 
-
-
-
 /**
  * Iterates over individual residues/coarse elements in polymers of a unit while
  * providing information about the neighbourhood in the underlying model for drawing splines
@@ -242,32 +249,21 @@ function createPolymerTraceElement (unit: Unit): PolymerTraceElement {
         first: false, last: false,
         secStrucType: SecondaryStructureType.create(SecondaryStructureType.Flag.NA),
         t0: Vec3.zero(), t1: Vec3.zero(), t2: Vec3.zero(), t3: Vec3.zero(), t4: Vec3.zero(),
-        d12: Vec3.zero(), d23: Vec3.zero(),
+        d12: Vec3.create(1, 0, 0), d23: Vec3.create(1, 0, 0),
     }
 }
 
 const enum AtomicPolymerTraceIteratorState { nextPolymer, nextResidue }
 
-function setSegment (outSegment: Segmentation.Segment<number>, index: number, segments: Segmentation<number>, min: number, max: number): Segmentation.Segment<number> {
-    // index = Math.min(Math.max(0, index), segments.segments.length - 2)
-    const _index = Math.min(Math.max(min, index), max)
-    if (isNaN(_index)) console.log(_index, index, min, max)
-    outSegment.index = _index
-    outSegment.start = segments.offsets[_index]
-    outSegment.end = segments.offsets[_index + 1]
-    // console.log(index, {...outSegment}, {...boundingSegment}, segments.segments[boundingSegment.index])
-    return outSegment
-}
-
-export class AtomicPolymerTraceIterator<T extends number = number> implements Iterator<PolymerTraceElement> {
+export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> {
     private value: PolymerTraceElement
-    private polymerIt: SortedRanges.Iterator<ElementIndex>
+    private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
     private residueIt: Segmentation.SegmentIterator<ResidueIndex>
-    private residueAtomSegmentMin: number
-    private residueAtomSegmentMax: number
+    private polymerSegment: Segmentation.Segment<ResidueIndex>
+    private residueSegmentMin: ResidueIndex
+    private residueSegmentMax: ResidueIndex
     private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer
     private residueAtomSegments: Segmentation<ElementIndex, ResidueIndex>
-    private tmpSegment: Segmentation.Segment<ResidueIndex>
 
     hasNext: boolean = false;
 
@@ -277,12 +273,25 @@ export class AtomicPolymerTraceIterator<T extends number = number> implements It
         target[2] = this.unit.model.atomicConformation.z[index]
     }
 
-    updateResidueSegmentRange(polymerSegment: Segmentation.Segment<ElementIndex>) {
-        const { polymerRanges, residueAtomSegments } = this.unit.model.atomicHierarchy
-        const sMin = polymerRanges[polymerSegment.index * 2]
-        const sMax = polymerRanges[polymerSegment.index * 2 + 1]
-        this.residueAtomSegmentMin = residueAtomSegments.index[sMin]
-        this.residueAtomSegmentMax = residueAtomSegments.index[sMax]
+    updateResidueSegmentRange(polymerSegment: Segmentation.Segment<ResidueIndex>) {
+        const { index } = this.unit.model.atomicHierarchy.residueAtomSegments
+        this.residueSegmentMin = index[this.unit.elements[polymerSegment.start]]
+        this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]]
+    }
+
+    getAtomIndex(residueIndex: number, atomType: 'trace' | 'direction') {
+        const index = Math.min(Math.max(this.residueSegmentMin, residueIndex), this.residueSegmentMax)
+        return getElementIndexForResidueTypeAtomId(this.unit.model, index as ResidueIndex, atomType)
+    }
+
+    getElementIndex(residueIndex: number, atomType: 'trace' | 'direction') {
+        const index = this.getAtomIndex(residueIndex, atomType)
+        // TODO handle case when it returns -1
+        const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex
+        if (elementIndex === -1) {
+            console.log('-1', residueIndex, atomType, index)
+        }
+        return elementIndex === -1 ? 0 as ElementIndex : elementIndex
     }
 
     move() {
@@ -290,10 +299,9 @@ export class AtomicPolymerTraceIterator<T extends number = number> implements It
 
         if (this.state === AtomicPolymerTraceIteratorState.nextPolymer) {
             while (polymerIt.hasNext) {
-                const polymerSegment = polymerIt.move();
-                // console.log('polymerSegment', {...polymerSegment})
-                residueIt.setSegment(polymerSegment);
-                this.updateResidueSegmentRange(polymerSegment)
+                this.polymerSegment = polymerIt.move();
+                residueIt.setSegment(this.polymerSegment);
+                this.updateResidueSegmentRange(this.polymerSegment)
                 if (residueIt.hasNext) {
                     this.state = AtomicPolymerTraceIteratorState.nextResidue
                     break
@@ -302,32 +310,22 @@ export class AtomicPolymerTraceIterator<T extends number = number> implements It
         }
 
         if (this.state === AtomicPolymerTraceIteratorState.nextResidue) {
-            const { tmpSegment, residueAtomSegments, residueAtomSegmentMin, residueAtomSegmentMax } = this
-            const residueSegment = residueIt.move();
-            const resSegIdx = residueSegment.index
-            // console.log(residueLabel(this.unit.model, resSegIdx), resSegIdx, this.unit.model.properties.secondaryStructure.type[resSegIdx])
-            value.center.element = getResidueTypeAtomIdElementIndex(this.unit, residueSegment, 'trace')
+            const { index: residueIndex } = residueIt.move();
+            value.center.element = this.getElementIndex(residueIndex, 'trace')
 
-            setSegment(tmpSegment, resSegIdx - 2, residueAtomSegments, residueAtomSegmentMin, residueAtomSegmentMax)
-            this.pos(value.t0, getResidueTypeAtomIdElementIndex(this.unit, tmpSegment, 'trace'))
+            this.pos(value.t0, this.getAtomIndex(residueIndex - 2, 'trace'))
+            this.pos(value.t1, this.getAtomIndex(residueIndex - 1, 'trace'))
+            this.pos(value.t2, this.getAtomIndex(residueIndex, 'trace'))
+            this.pos(value.t3, this.getAtomIndex(residueIndex + 1, 'trace'))
+            this.pos(value.t4, this.getAtomIndex(residueIndex + 2, 'trace'))
 
-            setSegment(tmpSegment, resSegIdx - 1, residueAtomSegments, residueAtomSegmentMin, residueAtomSegmentMax)
-            this.pos(value.t1, getResidueTypeAtomIdElementIndex(this.unit, tmpSegment, 'trace'))
-            this.pos(value.d12, getResidueTypeAtomIdElementIndex(this.unit, tmpSegment, 'direction'))
+            this.pos(value.d12, this.getAtomIndex(residueIndex - 1, 'direction'))
+            this.pos(value.d23, this.getAtomIndex(residueIndex, 'direction'))
 
-            setSegment(tmpSegment, resSegIdx, residueAtomSegments, residueAtomSegmentMin, residueAtomSegmentMax)
-            value.secStrucType = this.unit.model.properties.secondaryStructure.type[resSegIdx]
-            this.pos(value.t2, getResidueTypeAtomIdElementIndex(this.unit, tmpSegment, 'trace'))
-            this.pos(value.d23, getResidueTypeAtomIdElementIndex(this.unit, tmpSegment, 'direction'))
+            this.value.secStrucType = this.unit.model.properties.secondaryStructure.type[residueIndex]
 
-            setSegment(tmpSegment, resSegIdx + 1, residueAtomSegments, residueAtomSegmentMin, residueAtomSegmentMax)
-            this.pos(value.t3, getResidueTypeAtomIdElementIndex(this.unit, tmpSegment, 'trace'))
-
-            setSegment(tmpSegment, resSegIdx + 2, residueAtomSegments, residueAtomSegmentMin, residueAtomSegmentMax)
-            this.pos(value.t4, getResidueTypeAtomIdElementIndex(this.unit, tmpSegment, 'trace'))
-
-            value.first = resSegIdx === residueAtomSegmentMin
-            value.last = resSegIdx === residueAtomSegmentMax
+            value.first = residueIndex === this.polymerSegment.start
+            value.last = residueIndex === this.polymerSegment.end - 1
 
             if (!residueIt.hasNext) {
                 this.state = AtomicPolymerTraceIteratorState.nextPolymer
@@ -344,22 +342,71 @@ export class AtomicPolymerTraceIterator<T extends number = number> implements It
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
         this.residueIt = Segmentation.transientSegments(this.residueAtomSegments, unit.elements);
         this.value = createPolymerTraceElement(unit)
-        this.tmpSegment = { index: 0 as ResidueIndex, start: 0 as ElementIndex, end: 0 as ElementIndex }
         this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
     }
 }
 
-export class CoarsePolymerTraceIterator<T extends number = number> implements Iterator<PolymerTraceElement> {
-    private value: PolymerTraceElement
+const enum CoarsePolymerTraceIteratorState { nextPolymer, nextElement }
 
+export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement> {
+    private value: PolymerTraceElement
+    private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
+    private polymerSegment: Segmentation.Segment<ResidueIndex>
+    private state: CoarsePolymerTraceIteratorState = CoarsePolymerTraceIteratorState.nextPolymer
+    private conformation: CoarseSphereConformation | CoarseGaussianConformation
+    private elementIndex: number
     hasNext: boolean = false;
 
+    private pos(target: Vec3, elementIndex: number) {
+        elementIndex = Math.min(Math.max(this.polymerSegment.start, elementIndex), this.polymerSegment.end - 1)
+        const index = this.unit.elements[elementIndex]
+        target[0] = this.conformation.x[index]
+        target[1] = this.conformation.y[index]
+        target[2] = this.conformation.z[index]
+    }
+
     move() {
+        if (this.state === CoarsePolymerTraceIteratorState.nextPolymer) {
+            while (this.polymerIt.hasNext) {
+                this.polymerSegment = this.polymerIt.move();
+                this.elementIndex = this.polymerSegment.start
+
+                if (this.elementIndex + 1 < this.polymerSegment.end) {
+                    this.state = CoarsePolymerTraceIteratorState.nextElement
+                    break
+                }
+            }
+        }
+
+        if (this.state === CoarsePolymerTraceIteratorState.nextElement) {
+            this.elementIndex += 1
+            this.value.center.element = this.value.center.unit.elements[this.elementIndex]
+
+            this.pos(this.value.t0, this.elementIndex - 2)
+            this.pos(this.value.t1, this.elementIndex - 1)
+            this.pos(this.value.t2, this.elementIndex)
+            this.pos(this.value.t3, this.elementIndex + 1)
+            this.pos(this.value.t4, this.elementIndex + 2)
+
+            this.value.first = this.elementIndex === this.polymerSegment.start
+            this.value.last = this.elementIndex === this.polymerSegment.end - 1
+
+            if (this.elementIndex + 1 >= this.polymerSegment.end) {
+                this.state = CoarsePolymerTraceIteratorState.nextPolymer
+            }
+        }
+
+        this.hasNext = this.elementIndex + 1 < this.polymerSegment.end || this.polymerIt.hasNext
         return this.value;
     }
 
-    constructor(unit: Unit.Spheres | Unit.Gaussians) {
+    constructor(private unit: Unit.Spheres | Unit.Gaussians) {
+        this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
         this.value = createPolymerTraceElement(unit)
-        this.hasNext = false
+        switch (unit.kind) {
+            case Unit.Kind.Spheres: this.conformation = unit.model.coarseConformation.spheres; break
+            case Unit.Kind.Gaussians: this.conformation = unit.model.coarseConformation.gaussians; break
+        }
+        this.hasNext = this.polymerIt.hasNext
     }
 }