diff --git a/src/mol-repr/structure/representation/gaussian-surface.ts b/src/mol-repr/structure/representation/gaussian-surface.ts index 556ec327107888065780162f18b78ea343fe189c..8320a86876c5d74fcc9e79c89a835910aaad2f69 100644 --- a/src/mol-repr/structure/representation/gaussian-surface.ts +++ b/src/mol-repr/structure/representation/gaussian-surface.ts @@ -4,17 +4,18 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { GaussianSurfaceMeshVisual, GaussianSurfaceTextureMeshVisual, GaussianSurfaceMeshParams } from '../visual/gaussian-surface-mesh'; +import { GaussianSurfaceMeshVisual, GaussianSurfaceTextureMeshVisual, GaussianSurfaceMeshParams, StructureGaussianSurfaceMeshParams, StructureGaussianSurfaceMeshVisual } from '../visual/gaussian-surface-mesh'; import { UnitsRepresentation } from '../units-representation'; import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe'; import { ParamDefinition as PD } from '../../../mol-util/param-definition'; -import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder } from '../representation'; +import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, ComplexRepresentation } from '../representation'; import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation'; import { ThemeRegistryContext } from '../../../mol-theme/theme'; import { Structure } from '../../../mol-model/structure'; const GaussianSurfaceVisuals = { 'gaussian-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianSurfaceMeshParams>) => UnitsRepresentation('Gaussian surface', ctx, getParams, GaussianSurfaceMeshVisual), + 'structure-gaussian-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, StructureGaussianSurfaceMeshParams>) => ComplexRepresentation('Structure-Gaussian surface', ctx, getParams, StructureGaussianSurfaceMeshVisual), 'gaussian-surface-texture-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianSurfaceMeshParams>) => UnitsRepresentation('Gaussian surface', ctx, getParams, GaussianSurfaceTextureMeshVisual), 'gaussian-wireframe': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianWireframeParams>) => UnitsRepresentation('Gaussian wireframe', ctx, getParams, GaussianWireframeVisual), } diff --git a/src/mol-repr/structure/visual/element-point.ts b/src/mol-repr/structure/visual/element-point.ts index f234613c3b64282c4970068d65057110367ecfd8..1484a8c39ee8bb72bc4e799c3af24ea54159b79a 100644 --- a/src/mol-repr/structure/visual/element-point.ts +++ b/src/mol-repr/structure/visual/element-point.ts @@ -12,7 +12,7 @@ import { Theme } from '../../../mol-theme/theme'; import { Points } from '../../../mol-geo/geometry/points/points'; import { PointsBuilder } from '../../../mol-geo/geometry/points/points-builder'; import { Vec3 } from '../../../mol-math/linear-algebra'; -import { StructureElementIterator, getElementLoci, eachElement } from './util/element'; +import { ElementIterator, getElementLoci, eachElement } from './util/element'; import { VisualUpdateState } from '../../util'; export const ElementPointParams = { @@ -46,7 +46,7 @@ export function ElementPointVisual(materialId: number): UnitsVisual<ElementPoint return UnitsPointsVisual<ElementPointParams>({ defaultProps: PD.getDefaultValues(ElementPointParams), createGeometry: createElementPoint, - createLocationIterator: StructureElementIterator.fromGroup, + createLocationIterator: ElementIterator.fromGroup, getLoci: getElementLoci, eachLocation: eachElement, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ElementPointParams>, currentProps: PD.Values<ElementPointParams>) => { diff --git a/src/mol-repr/structure/visual/element-sphere.ts b/src/mol-repr/structure/visual/element-sphere.ts index 76715a6402b408273dcf4314b32e1188dbf08cba..32b5f4ce12193a0a59037660cdaf8ffab351d4d4 100644 --- a/src/mol-repr/structure/visual/element-sphere.ts +++ b/src/mol-repr/structure/visual/element-sphere.ts @@ -8,7 +8,7 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { UnitsMeshParams, UnitsSpheresParams, UnitsVisual, UnitsSpheresVisual, UnitsMeshVisual } from '../units-visual'; import { WebGLContext } from '../../../mol-gl/webgl/context'; -import { createElementSphereImpostor, StructureElementIterator, getElementLoci, eachElement, createElementSphereMesh } from './util/element'; +import { createElementSphereImpostor, ElementIterator, getElementLoci, eachElement, createElementSphereMesh } from './util/element'; import { VisualUpdateState } from '../../util'; export const ElementSphereParams = { @@ -28,7 +28,7 @@ export function ElementSphereImpostorVisual(materialId: number): UnitsVisual<Ele return UnitsSpheresVisual<ElementSphereParams>({ defaultProps: PD.getDefaultValues(ElementSphereParams), createGeometry: createElementSphereImpostor, - createLocationIterator: StructureElementIterator.fromGroup, + createLocationIterator: ElementIterator.fromGroup, getLoci: getElementLoci, eachLocation: eachElement, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ElementSphereParams>, currentProps: PD.Values<ElementSphereParams>) => { @@ -43,7 +43,7 @@ export function ElementSphereMeshVisual(materialId: number): UnitsVisual<Element return UnitsMeshVisual<ElementSphereParams>({ defaultProps: PD.getDefaultValues(ElementSphereParams), createGeometry: createElementSphereMesh, - createLocationIterator: StructureElementIterator.fromGroup, + createLocationIterator: ElementIterator.fromGroup, getLoci: getElementLoci, eachLocation: eachElement, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ElementSphereParams>, currentProps: PD.Values<ElementSphereParams>) => { diff --git a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts index d77e36c946d3e6a8c4f5812a5005f2a67dcc412c..60e1e7e513f8c2aebe3d92a25194aa423e82ee70 100644 --- a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts +++ b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts @@ -6,20 +6,21 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { UnitsMeshParams, UnitsTextureMeshParams, UnitsVisual, UnitsMeshVisual, UnitsTextureMeshVisual } from '../units-visual'; -import { GaussianDensityParams, computeUnitGaussianDensity, GaussianDensityTextureProps, computeUnitGaussianDensityTexture2d, GaussianDensityProps } from './util/gaussian'; +import { GaussianDensityParams, computeUnitGaussianDensity, GaussianDensityTextureProps, computeUnitGaussianDensityTexture2d, GaussianDensityProps, computeStructureGaussianDensity } from './util/gaussian'; import { WebGLContext } from '../../../mol-gl/webgl/context'; import { VisualContext } from '../../visual'; import { Unit, Structure } from '../../../mol-model/structure'; import { Theme } from '../../../mol-theme/theme'; import { Mesh } from '../../../mol-geo/geometry/mesh/mesh'; import { computeMarchingCubesMesh } from '../../../mol-geo/util/marching-cubes/algorithm'; -import { StructureElementIterator, getElementLoci, eachElement } from './util/element'; +import { ElementIterator, getElementLoci, eachElement, getSerialElementLoci, eachSerialElement } from './util/element'; import { VisualUpdateState } from '../../util'; import { TextureMesh } from '../../../mol-geo/geometry/texture-mesh/texture-mesh'; import { calcActiveVoxels } from '../../../mol-gl/compute/marching-cubes/active-voxels'; import { createHistogramPyramid } from '../../../mol-gl/compute/histogram-pyramid/reduction'; import { createIsosurfaceBuffers } from '../../../mol-gl/compute/marching-cubes/isosurface'; import { Sphere3D } from '../../../mol-math/geometry'; +import { ComplexVisual, ComplexMeshParams, ComplexMeshVisual } from '../complex-visual'; export const GaussianSurfaceMeshParams = { ...UnitsMeshParams, @@ -56,7 +57,7 @@ export function GaussianSurfaceMeshVisual(materialId: number): UnitsVisual<Gauss return UnitsMeshVisual<GaussianSurfaceMeshParams>({ defaultProps: PD.getDefaultValues(GaussianSurfaceMeshParams), createGeometry: createGaussianSurfaceMesh, - createLocationIterator: StructureElementIterator.fromGroup, + createLocationIterator: ElementIterator.fromGroup, getLoci: getElementLoci, eachLocation: eachElement, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianSurfaceMeshParams>, currentProps: PD.Values<GaussianSurfaceMeshParams>) => { @@ -71,6 +72,49 @@ export function GaussianSurfaceMeshVisual(materialId: number): UnitsVisual<Gauss // +export const StructureGaussianSurfaceMeshParams = { + ...ComplexMeshParams, + ...GaussianDensityParams, + ignoreHydrogens: PD.Boolean(false), +} +export type StructureGaussianSurfaceMeshParams = typeof StructureGaussianSurfaceMeshParams + +async function createStructureGaussianSurfaceMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> { + const { smoothness } = props + const { transform, field, idField } = await computeStructureGaussianDensity(structure, props, ctx.webgl).runInContext(ctx.runtime) + + const params = { + isoLevel: Math.exp(-smoothness), + scalarField: field, + idField + } + const surface = await computeMarchingCubesMesh(params, mesh).runAsChild(ctx.runtime) + + Mesh.transformImmediate(surface, transform) + Mesh.uniformTriangleGroup(surface) + + return surface +} + +export function StructureGaussianSurfaceMeshVisual(materialId: number): ComplexVisual<StructureGaussianSurfaceMeshParams> { + return ComplexMeshVisual<StructureGaussianSurfaceMeshParams>({ + defaultProps: PD.getDefaultValues(StructureGaussianSurfaceMeshParams), + createGeometry: createStructureGaussianSurfaceMesh, + createLocationIterator: ElementIterator.fromStructure, + getLoci: getSerialElementLoci, + eachLocation: eachSerialElement, + setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianSurfaceMeshParams>, currentProps: PD.Values<GaussianSurfaceMeshParams>) => { + if (newProps.resolution !== currentProps.resolution) state.createGeometry = true + if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true + if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true + if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true + if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true + } + }, materialId) +} + +// + async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityTextureProps, textureMesh?: TextureMesh): Promise<TextureMesh> { if (!ctx.webgl) throw new Error('webgl context required to create gaussian surface texture-mesh') const isoLevel = Math.exp(-props.smoothness) @@ -102,7 +146,7 @@ export function GaussianSurfaceTextureMeshVisual(materialId: number): UnitsVisua return UnitsTextureMeshVisual<GaussianSurfaceMeshParams>({ defaultProps: PD.getDefaultValues(GaussianSurfaceMeshParams), createGeometry: createGaussianSurfaceTextureMesh, - createLocationIterator: StructureElementIterator.fromGroup, + createLocationIterator: ElementIterator.fromGroup, getLoci: getElementLoci, eachLocation: eachElement, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianSurfaceMeshParams>, currentProps: PD.Values<GaussianSurfaceMeshParams>) => { diff --git a/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts b/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts index bc5b4910f3ed6fbd2c2e7146fe24d518015895d6..06b292a2173d21e08001b6b26882371c948d3e6e 100644 --- a/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts +++ b/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts @@ -12,7 +12,7 @@ import { Lines } from '../../../mol-geo/geometry/lines/lines'; import { computeUnitGaussianDensity, GaussianDensityParams, GaussianDensityProps } from './util/gaussian'; import { computeMarchingCubesLines } from '../../../mol-geo/util/marching-cubes/algorithm'; import { UnitsLinesParams, UnitsVisual, UnitsLinesVisual } from '../units-visual'; -import { StructureElementIterator, getElementLoci, eachElement } from './util/element'; +import { ElementIterator, getElementLoci, eachElement } from './util/element'; import { VisualUpdateState } from '../../util'; async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, lines?: Lines): Promise<Lines> { @@ -43,7 +43,7 @@ export function GaussianWireframeVisual(materialId: number): UnitsVisual<Gaussia return UnitsLinesVisual<GaussianWireframeParams>({ defaultProps: PD.getDefaultValues(GaussianWireframeParams), createGeometry: createGaussianWireframe, - createLocationIterator: StructureElementIterator.fromGroup, + createLocationIterator: ElementIterator.fromGroup, getLoci: getElementLoci, eachLocation: eachElement, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianWireframeParams>, currentProps: PD.Values<GaussianWireframeParams>) => { diff --git a/src/mol-repr/structure/visual/molecular-surface-mesh.ts b/src/mol-repr/structure/visual/molecular-surface-mesh.ts index cba0730ac31fab299d3a057050a54a081ef4972c..4f14f9db614fb8d5cbba10337c7b5bb66ac63ced 100644 --- a/src/mol-repr/structure/visual/molecular-surface-mesh.ts +++ b/src/mol-repr/structure/visual/molecular-surface-mesh.ts @@ -13,7 +13,7 @@ import { Theme } from '../../../mol-theme/theme'; import { Mesh } from '../../../mol-geo/geometry/mesh/mesh'; import { computeUnitMolecularSurface, MolecularSurfaceProps } from './util/molecular-surface'; import { computeMarchingCubesMesh } from '../../../mol-geo/util/marching-cubes/algorithm'; -import { StructureElementIterator, getElementLoci, eachElement } from './util/element'; +import { ElementIterator, getElementLoci, eachElement } from './util/element'; import { VisualUpdateState } from '../../util'; export const MolecularSurfaceMeshParams = { @@ -46,7 +46,7 @@ export function MolecularSurfaceMeshVisual(materialId: number): UnitsVisual<Mole return UnitsMeshVisual<MolecularSurfaceMeshParams>({ defaultProps: PD.getDefaultValues(MolecularSurfaceMeshParams), createGeometry: createMolecularSurfaceMesh, - createLocationIterator: StructureElementIterator.fromGroup, + createLocationIterator: ElementIterator.fromGroup, getLoci: getElementLoci, eachLocation: eachElement, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<MolecularSurfaceMeshParams>, currentProps: PD.Values<MolecularSurfaceMeshParams>) => { diff --git a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts index 1993c714b11b213f27773e5943f716d7fce99dcb..73da6a17de41b4753927ac618c336d294b2cfd00 100644 --- a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts +++ b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts @@ -16,7 +16,7 @@ import { PolymerBackboneIterator } from './util/polymer'; import { OrderedSet } from '../../../mol-data/int'; import { addCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder'; import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../units-visual'; -import { StructureElementIterator, getElementLoci, eachElement } from './util/element'; +import { ElementIterator, getElementLoci, eachElement } from './util/element'; import { VisualUpdateState } from '../../util'; export const PolymerBackboneCylinderParams = { @@ -71,7 +71,7 @@ export function PolymerBackboneVisual(materialId: number): UnitsVisual<PolymerBa defaultProps: PD.getDefaultValues(PolymerBackboneParams), createGeometry: createPolymerBackboneCylinderMesh, // TODO create a specialized location iterator - createLocationIterator: StructureElementIterator.fromGroup, + createLocationIterator: ElementIterator.fromGroup, getLoci: getElementLoci, eachLocation: eachElement, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerBackboneParams>, currentProps: PD.Values<PolymerBackboneParams>) => { diff --git a/src/mol-repr/structure/visual/util/common.ts b/src/mol-repr/structure/visual/util/common.ts index 33d909fafc023e693811a79a92490ae1ff0a8355..40041e13cb7dcf646c855d238ab5090289dff78d 100644 --- a/src/mol-repr/structure/visual/util/common.ts +++ b/src/mol-repr/structure/visual/util/common.ts @@ -138,37 +138,69 @@ export function getUnitConformationAndRadius(unit: Unit, ignoreHydrogens = false } export function getStructureConformationAndRadius(structure: Structure, ignoreHydrogens = false) { - const xs: number[] = [] - const ys: number[] = [] - const zs: number[] = [] - const rs: number[] = [] - const id: number[] = [] - const l = StructureElement.create() const sizeTheme = PhysicalSizeTheme({}, {}) - let m = 0 - for (let i = 0, il = structure.units.length; i < il; ++i) { - const unit = structure.units[i] - const { elements } = unit - const { x, y, z } = unit.conformation - l.unit = unit - for (let j = 0, jl = elements.length; j < jl; ++j) { - const eI = elements[j] - if (ignoreHydrogens && isHydrogen(unit, eI)) continue - - const mj = m + j - xs[mj] = x(eI) - ys[mj] = y(eI) - zs[mj] = z(eI) - l.element = eI - rs[mj] = sizeTheme.size(l) - id[mj] = mj + let xs: ArrayLike<number> + let ys: ArrayLike<number> + let zs: ArrayLike<number> + let rs: ArrayLike<number> + let id: ArrayLike<number> + + if (ignoreHydrogens) { + const _xs: number[] = [] + const _ys: number[] = [] + const _zs: number[] = [] + const _rs: number[] = [] + const _id: number[] = [] + for (let i = 0, m = 0, il = structure.units.length; i < il; ++i) { + const unit = structure.units[i] + const { elements } = unit + const { x, y, z } = unit.conformation + l.unit = unit + for (let j = 0, jl = elements.length; j < jl; ++j) { + const eI = elements[j] + if (ignoreHydrogens && isHydrogen(unit, eI)) continue + + _xs.push(x(eI)) + _ys.push(y(eI)) + _zs.push(z(eI)) + l.element = eI + _rs.push(sizeTheme.size(l)) + _id.push(m + j) + } + m += elements.length + } + xs = _xs, ys = _ys, zs = _zs, rs = _rs + id = _id + } else { + const { elementCount } = structure + const _xs = new Float32Array(elementCount) + const _ys = new Float32Array(elementCount) + const _zs = new Float32Array(elementCount) + const _rs = new Float32Array(elementCount) + for (let i = 0, m = 0, il = structure.units.length; i < il; ++i) { + const unit = structure.units[i] + const { elements } = unit + const { x, y, z } = unit.conformation + l.unit = unit + for (let j = 0, jl = elements.length; j < jl; ++j) { + const eI = elements[j] + + const mj = m + j + _xs[mj] = x(eI) + _ys[mj] = y(eI) + _zs[mj] = z(eI) + l.element = eI + _rs[mj] = sizeTheme.size(l) + } + m += elements.length } - m += elements.length + xs = _xs, ys = _ys, zs = _zs, rs = _rs + id = fillSerial(new Uint32Array(elementCount)) } - const position = { indices: OrderedSet.ofRange(0, m), x: xs, y: ys, z: zs, id } + const position = { indices: OrderedSet.ofRange(0, id.length), x: xs, y: ys, z: zs, id } const radius = (index: number) => rs[index] return { position, radius } diff --git a/src/mol-repr/structure/visual/util/element.ts b/src/mol-repr/structure/visual/util/element.ts index 25f536c9a543069306a729f2919b7eab863421f6..d70d4fa10545dbfd2d27a903629b50a5db7576ff 100644 --- a/src/mol-repr/structure/visual/util/element.ts +++ b/src/mol-repr/structure/visual/util/element.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -86,12 +86,12 @@ export function eachElement(loci: Loci, structureGroup: StructureGroup, apply: ( const unitIdx = group.unitIndexMap.get(e.unit.id) if (unitIdx !== undefined) { if (Interval.is(e.indices)) { - const start = unitIdx * elementCount + Interval.start(e.indices); - const end = unitIdx * elementCount + Interval.end(e.indices); + const start = unitIdx * elementCount + Interval.start(e.indices) + const end = unitIdx * elementCount + Interval.end(e.indices) if (apply(Interval.ofBounds(start, end))) changed = true } else { for (let i = 0, _i = e.indices.length; i < _i; i++) { - const idx = unitIdx * elementCount + e.indices[i]; + const idx = unitIdx * elementCount + e.indices[i] if (apply(Interval.ofSingleton(idx))) changed = true } } @@ -105,13 +105,53 @@ export function getElementLoci(pickingId: PickingId, structureGroup: StructureGr if (id === objectId) { const { structure, group } = structureGroup const unit = group.units[instanceId] - const indices = OrderedSet.ofSingleton(groupId as StructureElement.UnitIndex); + const indices = OrderedSet.ofSingleton(groupId as StructureElement.UnitIndex) return StructureElement.Loci(structure, [{ unit, indices }]) } return EmptyLoci } -export namespace StructureElementIterator { +// + +export function eachSerialElement(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) { + let changed = false + if (!StructureElement.isLoci(loci)) return false + if (!Structure.areEquivalent(loci.structure, structure)) return false + const { unitElementCount } = structure.serialMapping + for (const e of loci.elements) { + const unitIdx = structure.unitIndexMap.get(e.unit.id) + if (unitIdx !== undefined) { + if (Interval.is(e.indices)) { + const start = unitElementCount[unitIdx] + Interval.start(e.indices) + const end = unitElementCount[unitIdx] + Interval.end(e.indices) + if (apply(Interval.ofBounds(start, end))) changed = true + } else { + for (let i = 0, _i = e.indices.length; i < _i; i++) { + const idx = unitElementCount[unitIdx] + e.indices[i] + if (apply(Interval.ofSingleton(idx))) changed = true + } + } + } + } + return changed +} + +export function getSerialElementLoci(pickingId: PickingId, structure: Structure, id: number) { + const { objectId, groupId } = pickingId + if (id === objectId) { + const { unitIndices, unitElementCount } = structure.serialMapping + const unitIdx = unitIndices[groupId] + const unit = structure.units[unitIdx] + const idx = groupId - unitElementCount[unitIdx] + const indices = OrderedSet.ofSingleton(idx as StructureElement.UnitIndex) + return StructureElement.Loci(structure, [{ unit, indices }]) + } + return EmptyLoci +} + +// + +export namespace ElementIterator { export function fromGroup(group: Unit.SymmetryGroup): LocationIterator { const groupCount = group.elements.length const instanceCount = group.units.length @@ -124,4 +164,18 @@ export namespace StructureElementIterator { } return LocationIterator(groupCount, instanceCount, getLocation) } + + export function fromStructure(structure: Structure): LocationIterator { + const { units, elementCount } = structure + const groupCount = elementCount + const instanceCount = 1 + const { unitIndices, elementIndices } = structure.serialMapping + const location = StructureElement.create() + const getLocation = (groupIndex: number) => { + location.unit = units[unitIndices[groupIndex]] + location.element = elementIndices[groupIndex] + return location + } + return LocationIterator(groupCount, instanceCount, getLocation, true) + } } \ No newline at end of file