diff --git a/CHANGELOG.md b/CHANGELOG.md index b1c62f1f3055ae11dfcc3a2b16f0cce45d95dede..7503f5118ff53d6a686ad832601ad26b0629f2be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ Note that since we don't clearly distinguish between a public and private interf ## [Unreleased] +- Make operators in `IndexPairBonds` a directed property +- Remove erroneous bounding-box overlap test in `Structure.eachUnitPair` +- Fix `EdgeBuilder.addNextEdge` for loop edges - Optimize inter unit bond compute - Improve SSAO for thin geometry (e.g. lines) diff --git a/src/mol-math/graph/int-adjacency-graph.ts b/src/mol-math/graph/int-adjacency-graph.ts index 743bfbe8cf309cf66edde67ff5e3e0ebacf8629e..9c608cd31ae17fa7a11caa827b3f8bff5a123ffc 100644 --- a/src/mol-math/graph/int-adjacency-graph.ts +++ b/src/mol-math/graph/int-adjacency-graph.ts @@ -1,5 +1,5 @@ /** - * 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 David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -49,7 +49,7 @@ export interface IntAdjacencyGraph<VertexIndex extends number, EdgeProps extends export namespace IntAdjacencyGraph { export type EdgePropsBase = { [name: string]: ArrayLike<any> } - export function areEqual<I extends number, P extends IntAdjacencyGraph.EdgePropsBase>(a: IntAdjacencyGraph<I, P>, b: IntAdjacencyGraph<I, P>) { + export function areEqual<VertexIndex extends number, EdgeProps extends IntAdjacencyGraph.EdgePropsBase>(a: IntAdjacencyGraph<VertexIndex, EdgeProps>, b: IntAdjacencyGraph<VertexIndex, EdgeProps>) { if (a === b) return true; if (a.vertexCount !== b.vertexCount || a.edgeCount !== b.edgeCount) return false; @@ -149,12 +149,11 @@ export namespace IntAdjacencyGraph { const a = this.xs[this.current], b = this.ys[this.current]; const oa = this.offsets[a] + this.bucketFill[a]; - const ob = this.offsets[b] + this.bucketFill[b]; - this.a[oa] = a; this.b[oa] = b; this.bucketFill[a]++; + const ob = this.offsets[b] + this.bucketFill[b]; this.a[ob] = b; this.b[ob] = a; this.bucketFill[b]++; @@ -176,6 +175,13 @@ export namespace IntAdjacencyGraph { prop[this.curB] = value; } + assignDirectedProperty<T>(propA: { [i: number]: T }, valueA: T, propB: { [i: number]: T }, valueB: T) { + propA[this.curA] = valueA; + propA[this.curB] = valueB; + propB[this.curB] = valueA; + propB[this.curA] = valueB; + } + constructor(public vertexCount: number, public xs: ArrayLike<VertexIndex>, public ys: ArrayLike<VertexIndex>) { this.edgeCount = xs.length; this.offsets = new Int32Array(this.vertexCount + 1); @@ -206,11 +212,11 @@ export namespace IntAdjacencyGraph { edgeCount: number; /** the size of the A and B arrays */ slotCount: number; - a: Int32Array; - b: Int32Array; + a: AssignableArrayLike<VertexIndex>; + b: AssignableArrayLike<VertexIndex>; - createGraph<EdgeProps extends IntAdjacencyGraph.EdgePropsBase>(edgeProps: EdgeProps) { - return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps); + createGraph<EdgeProps extends IntAdjacencyGraph.EdgePropsBase, Props>(edgeProps: EdgeProps, props?: Props) { + return create<VertexIndex, EdgeProps, Props>(this.offsets, this.a, this.b, this.edgeCount, edgeProps, props); } /** @@ -261,8 +267,8 @@ export namespace IntAdjacencyGraph { } this.offsets[this.vertexCount] = offset; this.slotCount = offset; - this.a = new Int32Array(offset); - this.b = new Int32Array(offset); + this.a = new Int32Array(offset) as unknown as AssignableArrayLike<VertexIndex>; + this.b = new Int32Array(offset) as unknown as AssignableArrayLike<VertexIndex>; } } @@ -295,13 +301,13 @@ export namespace IntAdjacencyGraph { } } - export function fromVertexPairs<V extends number>(vertexCount: number, xs: V[], ys: V[]) { + export function fromVertexPairs<VertexIndex extends number>(vertexCount: number, xs: VertexIndex[], ys: VertexIndex[]) { const graphBuilder = new IntAdjacencyGraph.EdgeBuilder(vertexCount, xs, ys); graphBuilder.addAllEdges(); return graphBuilder.createGraph({}); } - export function induceByVertices<V extends number, P extends IntAdjacencyGraph.EdgePropsBase>(graph: IntAdjacencyGraph<V, P>, vertexIndices: ArrayLike<number>): IntAdjacencyGraph<V, P> { + export function induceByVertices<VertexIndex extends number, EdgeProps extends IntAdjacencyGraph.EdgePropsBase, Props>(graph: IntAdjacencyGraph<VertexIndex, EdgeProps>, vertexIndices: ArrayLike<number>, props?: Props): IntAdjacencyGraph<VertexIndex, EdgeProps> { const { b, offset, vertexCount, edgeProps } = graph; const vertexMap = new Int32Array(vertexCount); for (let i = 0, _i = vertexIndices.length; i < _i; i++) vertexMap[vertexIndices[i]] = i + 1; @@ -316,8 +322,8 @@ export namespace IntAdjacencyGraph { const newOffsets = new Int32Array(vertexIndices.length + 1); const edgeIndices = new Int32Array(2 * newEdgeCount); - const newA = new Int32Array(2 * newEdgeCount) as unknown as AssignableArrayLike<V>; - const newB = new Int32Array(2 * newEdgeCount) as unknown as AssignableArrayLike<V>; + const newA = new Int32Array(2 * newEdgeCount) as unknown as AssignableArrayLike<VertexIndex>; + const newB = new Int32Array(2 * newEdgeCount) as unknown as AssignableArrayLike<VertexIndex>; let eo = 0, vo = 0; for (let i = 0; i < vertexCount; i++) { if (vertexMap[i] === 0) continue; @@ -326,20 +332,20 @@ export namespace IntAdjacencyGraph { const bb = vertexMap[b[j]]; if (bb === 0) continue; - newA[eo] = aa as V; - newB[eo] = bb - 1 as V; + newA[eo] = aa as VertexIndex; + newB[eo] = bb - 1 as VertexIndex; edgeIndices[eo] = j; eo++; } newOffsets[++vo] = eo; } - const newEdgeProps = {} as P; - for (const key of Object.keys(edgeProps) as (keyof P)[]) { - newEdgeProps[key] = arrayPickIndices(edgeProps[key], edgeIndices) as P[keyof P]; + const newEdgeProps = {} as EdgeProps; + for (const key of Object.keys(edgeProps) as (keyof EdgeProps)[]) { + newEdgeProps[key] = arrayPickIndices(edgeProps[key], edgeIndices) as EdgeProps[keyof EdgeProps]; } - return create(newOffsets, newA, newB, newEdgeCount, newEdgeProps); + return create<VertexIndex, EdgeProps, Props>(newOffsets, newA, newB, newEdgeCount, newEdgeProps, props); } export function connectedComponents(graph: IntAdjacencyGraph<any, any>): { componentCount: number, componentIndex: Int32Array } { diff --git a/src/mol-model-formats/structure/property/bonds/index-pair.ts b/src/mol-model-formats/structure/property/bonds/index-pair.ts index 81b3b3759121b290f7d8e706e381784b85764803..d0a07bf4e320d2b3cc95ad38f207a1cdd634ad47 100644 --- a/src/mol-model-formats/structure/property/bonds/index-pair.ts +++ b/src/mol-model-formats/structure/property/bonds/index-pair.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 Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> @@ -34,8 +34,10 @@ function getGraph(indexA: ArrayLike<ElementIndex>, indexB: ArrayLike<ElementInde for (let i = 0, _i = builder.edgeCount; i < _i; i++) { builder.addNextEdge(); builder.assignProperty(key, props.key ? props.key[i] : -1); - builder.assignProperty(operatorA, props.operatorA ? props.operatorA[i] : -1); - builder.assignProperty(operatorB, props.operatorB ? props.operatorB[i] : -1); + builder.assignDirectedProperty( + operatorA, props.operatorA ? props.operatorA[i] : -1, + operatorB, props.operatorB ? props.operatorB[i] : -1 + ); builder.assignProperty(order, props.order ? props.order[i] : 1); builder.assignProperty(distance, props.distance ? props.distance[i] : -1); builder.assignProperty(flag, props.flag ? props.flag[i] : BondType.Flag.Covalent); diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 33324a2f7661a3f55a83787434977e145c8065b6..685b578e4a39bebb3cf1e4df8200f31a9bdc19fc 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -23,7 +23,7 @@ import { Carbohydrates } from './carbohydrates/data'; import { computeCarbohydrates } from './carbohydrates/compute'; import { Vec3, Mat4 } from '../../../mol-math/linear-algebra'; import { idFactory } from '../../../mol-util/id-factory'; -import { Box3D, GridLookup3D } from '../../../mol-math/geometry'; +import { GridLookup3D } from '../../../mol-math/geometry'; import { UUID } from '../../../mol-util'; import { CustomProperties } from '../../custom-property'; import { AtomicHierarchy } from '../model/properties/atomic'; @@ -1213,25 +1213,20 @@ namespace Structure { const lookup = structure.lookup3d; const imageCenter = Vec3(); - const bbox = Box3D(); - const rvec = Vec3.create(maxRadius, maxRadius, maxRadius); - for (const unit of structure.units) { - if (!validUnit(unit)) continue; + for (const unitA of structure.units) { + if (!validUnit(unitA)) continue; - const bs = unit.boundary.sphere; - Box3D.expand(bbox, unit.boundary.box, rvec); - Vec3.transformMat4(imageCenter, bs.center, unit.conformation.operator.matrix); + const bs = unitA.boundary.sphere; + Vec3.transformMat4(imageCenter, bs.center, unitA.conformation.operator.matrix); const closeUnits = lookup.findUnitIndices(imageCenter[0], imageCenter[1], imageCenter[2], bs.radius + maxRadius); for (let i = 0; i < closeUnits.count; i++) { - const other = structure.units[closeUnits.indices[i]]; - if (unit.id >= other.id) continue; - - if (other.elements.length > 3 && !Box3D.overlaps(bbox, other.boundary.box)) continue; - if (!validUnit(other) || !validUnitPair(unit, other)) continue; + const unitB = structure.units[closeUnits.indices[i]]; + if (unitA.id >= unitB.id) continue; + if (!validUnit(unitB) || !validUnitPair(unitA, unitB)) continue; - if (other.elements.length >= unit.elements.length) callback(unit, other); - else callback(other, unit); + if (unitB.elements.length >= unitA.elements.length) callback(unitA, unitB); + else callback(unitB, unitA); } } }