diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 0bac89820c5cb757e9ba31cc21e26894220ecd73..034f8ddec039ebad472e1fdbb856ec8938333402 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -540,7 +540,8 @@ namespace Structure { for (let j = 0, jl = structure.units.length; j < jl; ++j) { const u = structure.units[j] const invariantId = u.invariantId + count - const newUnit = Unit.create(units.length, invariantId, u.kind, u.model, u.conformation.operator, u.elements) + const chainGroupId = u.chainGroupId + count + const newUnit = Unit.create(units.length, invariantId, chainGroupId, u.kind, u.model, u.conformation.operator, u.elements) units.push(newUnit) } count = units.length @@ -644,6 +645,7 @@ namespace Structure { const lookup = GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(startIndices) }, gridCellCount); const { offset, count, array } = lookup.buckets; + builder.beginChainGroup(); for (let i = 0, _i = offset.length; i < _i; i++) { const start = offset[i]; const set: number[] = []; @@ -655,6 +657,7 @@ namespace Structure { } builder.addUnit(Unit.Kind.Atomic, model, SymmetryOperator.Default, SortedArray.ofSortedArray(new Int32Array(set))); } + builder.endChainGroup(); } function addCoarseUnits(builder: StructureBuilder, model: Model, elements: CoarseElements, kind: Unit.Kind) { @@ -685,9 +688,22 @@ namespace Structure { private units: Unit[] = []; private invariantId = idFactory() + private chainGroupId = -1; + private inChainGroup = false; + + beginChainGroup() { + this.chainGroupId++; + this.inChainGroup = true; + } + + endChainGroup() { + this.inChainGroup = false; + } + addUnit(kind: Unit.Kind, model: Model, operator: SymmetryOperator, elements: StructureElement.Set, invariantId?: number): Unit { if (invariantId === undefined) invariantId = this.invariantId() - const unit = Unit.create(this.units.length, invariantId, kind, model, operator, elements); + const chainGroupId = this.inChainGroup ? this.chainGroupId : ++this.chainGroupId; + const unit = Unit.create(this.units.length, invariantId, chainGroupId, kind, model, operator, elements); this.units.push(unit); return unit; } diff --git a/src/mol-model/structure/structure/unit.ts b/src/mol-model/structure/structure/unit.ts index e36aba74fc910266f3aaf50d04a88f0b40b7b08c..ac00a011753509649ca2f180618bd5e0a364fff7 100644 --- a/src/mol-model/structure/structure/unit.ts +++ b/src/mol-model/structure/structure/unit.ts @@ -33,11 +33,11 @@ namespace Unit { export function isSpheres(u: Unit): u is Spheres { return u.kind === Kind.Spheres; } export function isGaussians(u: Unit): u is Gaussians { return u.kind === Kind.Gaussians; } - export function create(id: number, invariantId: number, kind: Kind, model: Model, operator: SymmetryOperator, elements: StructureElement.Set): Unit { + export function create(id: number, invariantId: number, chainGroupId: number, kind: Kind, model: Model, operator: SymmetryOperator, elements: StructureElement.Set): Unit { switch (kind) { - case Kind.Atomic: return new Atomic(id, invariantId, model, elements, SymmetryOperator.createMapping(operator, model.atomicConformation, void 0), AtomicProperties()); - case Kind.Spheres: return createCoarse(id, invariantId, model, Kind.Spheres, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.spheres, getSphereRadiusFunc(model)), CoarseProperties()); - case Kind.Gaussians: return createCoarse(id, invariantId, model, Kind.Gaussians, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.gaussians, getGaussianRadiusFunc(model)), CoarseProperties()); + case Kind.Atomic: return new Atomic(id, invariantId, chainGroupId, model, elements, SymmetryOperator.createMapping(operator, model.atomicConformation, void 0), AtomicProperties()); + case Kind.Spheres: return createCoarse(id, invariantId, chainGroupId, model, Kind.Spheres, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.spheres, getSphereRadiusFunc(model)), CoarseProperties()); + case Kind.Gaussians: return createCoarse(id, invariantId, chainGroupId, model, Kind.Gaussians, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.gaussians, getGaussianRadiusFunc(model)), CoarseProperties()); } } @@ -98,6 +98,7 @@ namespace Unit { readonly id: number, /** invariant ID stays the same even if the Operator/conformation changes. */ readonly invariantId: number, + readonly chainGroupId: number, readonly elements: StructureElement.Set, readonly model: Model, readonly conformation: SymmetryOperator.ArrayMapping<ElementIndex>, @@ -139,6 +140,8 @@ namespace Unit { readonly id: number; readonly invariantId: number; + /** Used to identify a single chain split into multiple units. */ + readonly chainGroupId: number; readonly elements: StructureElement.Set; readonly model: Model; readonly conformation: SymmetryOperator.ArrayMapping<ElementIndex>; @@ -152,12 +155,12 @@ namespace Unit { getChild(elements: StructureElement.Set): Unit { if (elements.length === this.elements.length) return this; - return new Atomic(this.id, this.invariantId, this.model, elements, this.conformation, AtomicProperties()); + return new Atomic(this.id, this.invariantId, this.chainGroupId, this.model, elements, this.conformation, AtomicProperties()); } applyOperator(id: number, operator: SymmetryOperator, dontCompose = false): Unit { const op = dontCompose ? operator : SymmetryOperator.compose(this.conformation.operator, operator); - return new Atomic(id, this.invariantId, this.model, this.elements, SymmetryOperator.createMapping(op, this.model.atomicConformation, this.conformation.r), this.props); + return new Atomic(id, this.invariantId, this.chainGroupId, this.model, this.elements, SymmetryOperator.createMapping(op, this.model.atomicConformation, this.conformation.r), this.props); } get lookup3d() { @@ -221,9 +224,10 @@ namespace Unit { return this.model.atomicHierarchy.residueAtomSegments.index[this.elements[elementIndex]]; } - constructor(id: number, invariantId: number, model: Model, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping<ElementIndex>, props: AtomicProperties) { + constructor(id: number, invariantId: number, chainGroupId: number, model: Model, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping<ElementIndex>, props: AtomicProperties) { this.id = id; this.invariantId = invariantId; + this.chainGroupId = chainGroupId; this.model = model; this.elements = elements; this.conformation = conformation; @@ -264,6 +268,7 @@ namespace Unit { readonly id: number; readonly invariantId: number; + readonly chainGroupId: number; readonly elements: StructureElement.Set; readonly model: Model; readonly conformation: SymmetryOperator.ArrayMapping<ElementIndex>; @@ -275,12 +280,12 @@ namespace Unit { getChild(elements: StructureElement.Set): Unit { if (elements.length === this.elements.length) return this as any as Unit /** lets call this an ugly temporary hack */; - return createCoarse(this.id, this.invariantId, this.model, this.kind, elements, this.conformation, CoarseProperties()); + return createCoarse(this.id, this.invariantId, this.chainGroupId, this.model, this.kind, elements, this.conformation, CoarseProperties()); } applyOperator(id: number, operator: SymmetryOperator, dontCompose = false): Unit { const op = dontCompose ? operator : SymmetryOperator.compose(this.conformation.operator, operator); - const ret = createCoarse(id, this.invariantId, this.model, this.kind, this.elements, SymmetryOperator.createMapping(op, this.getCoarseConformation(), this.conformation.r), this.props); + const ret = createCoarse(id, this.invariantId, this.chainGroupId, this.model, this.kind, this.elements, SymmetryOperator.createMapping(op, this.getCoarseConformation(), this.conformation.r), this.props); // (ret as Coarse<K, C>)._lookup3d = this._lookup3d; return ret; } @@ -309,11 +314,12 @@ namespace Unit { return this.kind === Kind.Spheres ? this.model.coarseConformation.spheres : this.model.coarseConformation.gaussians; } - constructor(id: number, invariantId: number, model: Model, kind: K, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping<ElementIndex>, props: CoarseProperties) { + constructor(id: number, invariantId: number, chainGroupId: number, model: Model, kind: K, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping<ElementIndex>, props: CoarseProperties) { this.kind = kind; this.objectPrimitive = kind === Kind.Spheres ? 'sphere' : 'gaussian' this.id = id; this.invariantId = invariantId; + this.chainGroupId = chainGroupId; this.model = model; this.elements = elements; this.conformation = conformation; @@ -337,8 +343,8 @@ namespace Unit { }; } - function createCoarse<K extends Kind.Gaussians | Kind.Spheres>(id: number, invariantId: number, model: Model, kind: K, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping<ElementIndex>, props: CoarseProperties): Unit { - return new Coarse(id, invariantId, model, kind, elements, conformation, props) as any as Unit /** lets call this an ugly temporary hack */; + function createCoarse<K extends Kind.Gaussians | Kind.Spheres>(id: number, invariantId: number, chainGroupId: number, model: Model, kind: K, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping<ElementIndex>, props: CoarseProperties): Unit { + return new Coarse(id, invariantId, chainGroupId, model, kind, elements, conformation, props) as any as Unit /** lets call this an ugly temporary hack */; } export class Spheres extends Coarse<Kind.Spheres, CoarseSphereConformation> { }