|
@@ -1,5 +1,5 @@
|
|
|
/**
|
|
|
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
*
|
|
|
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
|
*/
|
|
@@ -8,7 +8,10 @@ import { Color } from '../../mol-util/color';
|
|
|
import { UUID } from '../../mol-util';
|
|
|
import { OrderedSet } from '../../mol-data/int';
|
|
|
import { Geometry } from '../../mol-geo/geometry/geometry';
|
|
|
-import { Mat4 } from '../../mol-math/linear-algebra';
|
|
|
+import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
|
|
|
+import { Sphere3D } from '../../mol-math/geometry';
|
|
|
+import { CentroidHelper } from '../../mol-math/geometry/centroid-helper';
|
|
|
+import { GroupMapping } from '../../mol-geo/util';
|
|
|
|
|
|
export interface Shape<G extends Geometry = Geometry> {
|
|
|
/** A uuid to identify a shape object */
|
|
@@ -73,13 +76,13 @@ export namespace ShapeGroup {
|
|
|
readonly kind: 'group-loci',
|
|
|
readonly shape: Shape,
|
|
|
readonly groups: ReadonlyArray<{
|
|
|
- ids: OrderedSet<number>
|
|
|
+ readonly ids: OrderedSet<number>
|
|
|
+ readonly instance: number
|
|
|
}>
|
|
|
- readonly instance: number
|
|
|
}
|
|
|
|
|
|
- export function Loci(shape: Shape, groups: ArrayLike<{ ids: OrderedSet<number> }>, instance: number): Loci {
|
|
|
- return { kind: 'group-loci', shape, groups: groups as Loci['groups'], instance };
|
|
|
+ export function Loci(shape: Shape, groups: Loci['groups']): Loci {
|
|
|
+ return { kind: 'group-loci', shape, groups: groups as Loci['groups'] };
|
|
|
}
|
|
|
|
|
|
export function isLoci(x: any): x is Loci {
|
|
@@ -89,11 +92,11 @@ export namespace ShapeGroup {
|
|
|
export function areLociEqual(a: Loci, b: Loci) {
|
|
|
if (a.shape !== b.shape) return false
|
|
|
if (a.groups.length !== b.groups.length) return false
|
|
|
- if (a.instance !== b.instance) return false
|
|
|
for (let i = 0, il = a.groups.length; i < il; ++i) {
|
|
|
- const groupA = a.groups[i]
|
|
|
- const groupB = b.groups[i]
|
|
|
- if (!OrderedSet.areEqual(groupA.ids, groupB.ids)) return false
|
|
|
+ const { ids: idsA, instance: instanceA } = a.groups[i]
|
|
|
+ const { ids: idsB, instance: instanceB } = b.groups[i]
|
|
|
+ if (instanceA !== instanceB) return false
|
|
|
+ if (!OrderedSet.areEqual(idsA, idsB)) return false
|
|
|
}
|
|
|
return true
|
|
|
}
|
|
@@ -109,4 +112,75 @@ export namespace ShapeGroup {
|
|
|
}
|
|
|
return size
|
|
|
}
|
|
|
+
|
|
|
+ const sphereHelper = new CentroidHelper(), tmpPos = Vec3.zero();
|
|
|
+
|
|
|
+ function sphereHelperInclude(groups: Loci['groups'], mapping: GroupMapping, positions: Float32Array, transforms: Mat4[]) {
|
|
|
+ const { indices, offsets } = mapping
|
|
|
+ for (const { ids, instance } of groups) {
|
|
|
+ OrderedSet.forEach(ids, v => {
|
|
|
+ for (let i = offsets[v], il = offsets[v + 1]; i < il; ++i) {
|
|
|
+ Vec3.fromArray(tmpPos, positions, indices[i] * 3)
|
|
|
+ Vec3.transformMat4(tmpPos, tmpPos, transforms[instance])
|
|
|
+ sphereHelper.includeStep(tmpPos)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function sphereHelperRadius(groups: Loci['groups'], mapping: GroupMapping, positions: Float32Array, transforms: Mat4[]) {
|
|
|
+ const { indices, offsets } = mapping
|
|
|
+ for (const { ids, instance } of groups) {
|
|
|
+ OrderedSet.forEach(ids, v => {
|
|
|
+ for (let i = offsets[v], il = offsets[v + 1]; i < il; ++i) {
|
|
|
+ Vec3.fromArray(tmpPos, positions, indices[i] * 3)
|
|
|
+ Vec3.transformMat4(tmpPos, tmpPos, transforms[instance])
|
|
|
+ sphereHelper.radiusStep(tmpPos)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ export function getBoundingSphere(loci: Loci, boundingSphere?: Sphere3D) {
|
|
|
+ if (!boundingSphere) boundingSphere = Sphere3D()
|
|
|
+
|
|
|
+ sphereHelper.reset();
|
|
|
+ let padding = 0
|
|
|
+
|
|
|
+ const { geometry, transforms } = loci.shape
|
|
|
+
|
|
|
+ if (geometry.kind === 'mesh') {
|
|
|
+ const positions = geometry.vertexBuffer.ref.value
|
|
|
+ sphereHelperInclude(loci.groups, geometry.groupMapping, positions, transforms)
|
|
|
+ sphereHelper.finishedIncludeStep()
|
|
|
+ sphereHelperRadius(loci.groups, geometry.groupMapping, positions, transforms)
|
|
|
+ } else if (geometry.kind === 'lines') {
|
|
|
+ const start = geometry.startBuffer.ref.value
|
|
|
+ const end = geometry.endBuffer.ref.value
|
|
|
+ sphereHelperInclude(loci.groups, geometry.groupMapping, start, transforms)
|
|
|
+ sphereHelperInclude(loci.groups, geometry.groupMapping, end, transforms)
|
|
|
+ sphereHelper.finishedIncludeStep()
|
|
|
+ sphereHelperRadius(loci.groups, geometry.groupMapping, start, transforms)
|
|
|
+ sphereHelperRadius(loci.groups, geometry.groupMapping, end, transforms)
|
|
|
+ } else if (geometry.kind === 'spheres') {
|
|
|
+ const positions = geometry.centerBuffer.ref.value
|
|
|
+ sphereHelperInclude(loci.groups, geometry.groupMapping, positions, transforms)
|
|
|
+ sphereHelper.finishedIncludeStep()
|
|
|
+ sphereHelperRadius(loci.groups, geometry.groupMapping, positions, transforms)
|
|
|
+ for (const { ids, instance } of loci.groups) {
|
|
|
+ OrderedSet.forEach(ids, v => {
|
|
|
+ const value = loci.shape.getSize(v, instance)
|
|
|
+ if (padding < value) padding = value
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // TODO implement for other geometry kinds
|
|
|
+ return Sphere3D.copy(boundingSphere, geometry.boundingSphere)
|
|
|
+ }
|
|
|
+
|
|
|
+ Vec3.copy(boundingSphere.center, sphereHelper.center)
|
|
|
+ boundingSphere.radius = Math.sqrt(sphereHelper.radiusSq)
|
|
|
+ Sphere3D.expand(boundingSphere, boundingSphere, padding)
|
|
|
+ return boundingSphere
|
|
|
+ }
|
|
|
}
|