mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 13:30:24 +08:00
Fix Volume and Isosurface getBoundingSphere ignoring instances
This commit is contained in:
@@ -12,6 +12,7 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
- Add mesoscale representation preset
|
||||
- Add presets option to `ObjectList` param definition
|
||||
- Fix memory leak in `State.dispose()` not invoking transformer `dispose` callbacks for live cells
|
||||
- Fix `Volume` and `Isosurface` getBoundingSphere ignoring instances
|
||||
|
||||
## [v5.9.0] - 2026-05-03
|
||||
- Fix edge case when `PluginSpec.animations` is empty
|
||||
|
||||
@@ -167,9 +167,9 @@ namespace Loci {
|
||||
} else if (loci.kind === 'data-loci') {
|
||||
return loci.getBoundingSphere?.(boundingSphere);
|
||||
} else if (loci.kind === 'volume-loci') {
|
||||
return Volume.getBoundingSphere(loci.volume, boundingSphere);
|
||||
return Volume.getBoundingSphere(loci.volume, loci.instances, boundingSphere);
|
||||
} else if (loci.kind === 'isosurface-loci') {
|
||||
return Volume.Isosurface.getBoundingSphere(loci.volume, loci.isoValue, boundingSphere);
|
||||
return Volume.Isosurface.getBoundingSphere(loci.volume, loci.isoValue, loci.instances, boundingSphere);
|
||||
} else if (loci.kind === 'cell-loci') {
|
||||
return Volume.Cell.getBoundingSphere(loci.volume, loci.elements, boundingSphere);
|
||||
} else if (loci.kind === 'segment-loci') {
|
||||
|
||||
@@ -68,6 +68,36 @@ namespace Grid {
|
||||
return Sphere3D.fromDimensionsAndTransform(boundingSphere, dimensions, transform);
|
||||
}
|
||||
|
||||
const _isoBbox = Box3D();
|
||||
export function getIsosurfaceBoundingSphere(grid: Grid, isoValue: number, boundingSphere?: Sphere3D) {
|
||||
const neg = isoValue < 0;
|
||||
|
||||
const c = [0, 0, 0];
|
||||
const getCoords = grid.cells.space.getCoords;
|
||||
const d = grid.cells.data;
|
||||
const [xn, yn, zn] = grid.cells.space.dimensions;
|
||||
|
||||
let minx = xn - 1, miny = yn - 1, minz = zn - 1;
|
||||
let maxx = 0, maxy = 0, maxz = 0;
|
||||
for (let i = 0, il = d.length; i < il; ++i) {
|
||||
if ((neg && d[i] <= isoValue) || (!neg && d[i] >= isoValue)) {
|
||||
getCoords(i, c);
|
||||
if (c[0] < minx) minx = c[0];
|
||||
if (c[1] < miny) miny = c[1];
|
||||
if (c[2] < minz) minz = c[2];
|
||||
if (c[0] > maxx) maxx = c[0];
|
||||
if (c[1] > maxy) maxy = c[1];
|
||||
if (c[2] > maxz) maxz = c[2];
|
||||
}
|
||||
}
|
||||
|
||||
Vec3.set(_isoBbox.min, minx - 1, miny - 1, minz - 1);
|
||||
Vec3.set(_isoBbox.max, maxx + 1, maxy + 1, maxz + 1);
|
||||
const transform = Grid.getGridToCartesianTransform(grid);
|
||||
Box3D.transform(_isoBbox, _isoBbox, transform);
|
||||
return Sphere3D.fromBox3D(boundingSphere || Sphere3D(), _isoBbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute histogram with given bin count.
|
||||
* Cached on the Grid object.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { Grid } from './grid';
|
||||
import { OrderedSet } from '../../mol-data/int';
|
||||
import { Interval, OrderedSet } from '../../mol-data/int';
|
||||
import { Box3D, Sphere3D } from '../../mol-math/geometry';
|
||||
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
|
||||
@@ -191,14 +191,14 @@ export namespace Volume {
|
||||
export function isLociEmpty(loci: Loci) { return isEmpty(loci.volume) || OrderedSet.isEmpty(loci.instances); }
|
||||
|
||||
const boundaryHelper = new BoundaryHelper('98');
|
||||
export function getBoundingSphere(volume: Volume, boundingSphere?: Sphere3D) {
|
||||
export function getBoundingSphere(volume: Volume, instances: OrderedSet<InstanceIndex>, boundingSphere?: Sphere3D) {
|
||||
const gs = Grid.getBoundingSphere(volume.grid);
|
||||
if (!boundingSphere) boundingSphere = Sphere3D();
|
||||
if (volume.instances.length === 0) return Sphere3D.copy(boundingSphere, gs);
|
||||
if (OrderedSet.isEmpty(instances)) return Sphere3D.copy(boundingSphere, gs);
|
||||
|
||||
const spheres: Sphere3D[] = [];
|
||||
for (let i = 0, il = volume.instances.length; i < il; ++i) {
|
||||
const { transform } = volume.instances[i];
|
||||
for (let i = 0, il = OrderedSet.size(instances); i < il; ++i) {
|
||||
const { transform } = volume.instances[OrderedSet.getAt(instances, i)];
|
||||
spheres.push(Sphere3D.transform(Sphere3D(), gs, transform));
|
||||
}
|
||||
|
||||
@@ -220,35 +220,23 @@ export namespace Volume {
|
||||
export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && Volume.IsoValue.areSame(a.isoValue, b.isoValue, a.volume.grid.stats) && OrderedSet.areEqual(a.instances, b.instances); }
|
||||
export function isLociEmpty(loci: Loci) { return isEmpty(loci.volume) || OrderedSet.isEmpty(loci.instances); }
|
||||
|
||||
const bbox = Box3D();
|
||||
export function getBoundingSphere(volume: Volume, isoValue: Volume.IsoValue, boundingSphere?: Sphere3D) {
|
||||
const boundaryHelper = new BoundaryHelper('98');
|
||||
export function getBoundingSphere(volume: Volume, isoValue: Volume.IsoValue, instances: OrderedSet<InstanceIndex>, boundingSphere?: Sphere3D) {
|
||||
const value = Volume.IsoValue.toAbsolute(isoValue, volume.grid.stats).absoluteValue;
|
||||
const neg = value < 0;
|
||||
const gs = Grid.getIsosurfaceBoundingSphere(volume.grid, value);
|
||||
|
||||
const c = [0, 0, 0];
|
||||
const getCoords = volume.grid.cells.space.getCoords;
|
||||
const d = volume.grid.cells.data;
|
||||
const [xn, yn, zn] = volume.grid.cells.space.dimensions;
|
||||
if (OrderedSet.isEmpty(instances)) return Sphere3D.copy(boundingSphere || Sphere3D(), gs);
|
||||
|
||||
let minx = xn - 1, miny = yn - 1, minz = zn - 1;
|
||||
let maxx = 0, maxy = 0, maxz = 0;
|
||||
for (let i = 0, il = d.length; i < il; ++i) {
|
||||
if ((neg && d[i] <= value) || (!neg && d[i] >= value)) {
|
||||
getCoords(i, c);
|
||||
if (c[0] < minx) minx = c[0];
|
||||
if (c[1] < miny) miny = c[1];
|
||||
if (c[2] < minz) minz = c[2];
|
||||
if (c[0] > maxx) maxx = c[0];
|
||||
if (c[1] > maxy) maxy = c[1];
|
||||
if (c[2] > maxz) maxz = c[2];
|
||||
}
|
||||
const spheres: Sphere3D[] = [];
|
||||
for (let i = 0, il = OrderedSet.size(instances); i < il; ++i) {
|
||||
spheres.push(Sphere3D.transform(Sphere3D(), gs, volume.instances[OrderedSet.getAt(instances, i)].transform));
|
||||
}
|
||||
|
||||
Vec3.set(bbox.min, minx - 1, miny - 1, minz - 1);
|
||||
Vec3.set(bbox.max, maxx + 1, maxy + 1, maxz + 1);
|
||||
const transform = Grid.getGridToCartesianTransform(volume.grid);
|
||||
Box3D.transform(bbox, bbox, transform);
|
||||
return Sphere3D.fromBox3D(boundingSphere || Sphere3D(), bbox);
|
||||
boundaryHelper.reset();
|
||||
for (const s of spheres) boundaryHelper.includeSphere(s);
|
||||
boundaryHelper.finishedIncludeStep();
|
||||
for (const s of spheres) boundaryHelper.radiusSphere(s);
|
||||
return boundaryHelper.getSphere(boundingSphere);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +404,7 @@ export namespace Volume {
|
||||
}
|
||||
return Sphere3D.fromBox3D(boundingSphere || Sphere3D(), bbox);
|
||||
} else {
|
||||
return Volume.getBoundingSphere(volume, boundingSphere);
|
||||
return Volume.getBoundingSphere(volume, Interval.ofLength(volume.instances.length as InstanceIndex), boundingSphere);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ export function createVolumeSphereImpostor(ctx: VisualContext, volume: Volume, k
|
||||
}
|
||||
|
||||
const s = builder.getSpheres();
|
||||
s.setBoundingSphere(Volume.Isosurface.getBoundingSphere(volume, props.isoValue));
|
||||
s.setBoundingSphere(Grid.getIsosurfaceBoundingSphere(volume.grid, Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue));
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ export function createVolumeSphereMesh(ctx: VisualContext, volume: Volume, key:
|
||||
}
|
||||
|
||||
const m = MeshBuilder.getMesh(builderState);
|
||||
m.setBoundingSphere(Volume.Isosurface.getBoundingSphere(volume, props.isoValue));
|
||||
m.setBoundingSphere(Grid.getIsosurfaceBoundingSphere(volume.grid, Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue));
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ export function createVolumePoint(ctx: VisualContext, volume: Volume, key: numbe
|
||||
}
|
||||
|
||||
const pt = builder.getPoints();
|
||||
pt.setBoundingSphere(Volume.Isosurface.getBoundingSphere(volume, props.isoValue));
|
||||
pt.setBoundingSphere(Grid.getIsosurfaceBoundingSphere(volume.grid, Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue));
|
||||
return pt;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: Vol
|
||||
ValueCell.updateIfChanged(surface.varyingGroup, true);
|
||||
}
|
||||
|
||||
surface.setBoundingSphere(Volume.Isosurface.getBoundingSphere(volume, props.isoValue));
|
||||
surface.setBoundingSphere(Grid.getIsosurfaceBoundingSphere(volume.grid, Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue));
|
||||
|
||||
return surface;
|
||||
}
|
||||
@@ -318,7 +318,7 @@ export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume
|
||||
const transform = Grid.getGridToCartesianTransform(volume.grid);
|
||||
Lines.transform(wireframe, transform);
|
||||
|
||||
wireframe.setBoundingSphere(Volume.Isosurface.getBoundingSphere(volume, props.isoValue));
|
||||
wireframe.setBoundingSphere(Grid.getIsosurfaceBoundingSphere(volume.grid, Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue));
|
||||
|
||||
return wireframe;
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ function getSampledImage(volume: Volume, theme: Theme, info: SamplingInfo, isoVa
|
||||
const isoLevel = clamp(normalize(Volume.IsoValue.toAbsolute(isoValue, stats).absoluteValue, min, max), 0, 1);
|
||||
|
||||
const im = Image.create(imageTexture, corners, groupTexture, valueTexture, trim, isoLevel, image);
|
||||
im.setBoundingSphere(Volume.isPeriodic(volume) ? Volume.getBoundingSphere(volume) : Grid.getBoundingSphere(volume.grid));
|
||||
im.setBoundingSphere(Volume.isPeriodic(volume) ? Volume.getBoundingSphere(volume, Interval.ofLength(volume.instances.length as Volume.InstanceIndex)) : Grid.getBoundingSphere(volume.grid));
|
||||
im.meta.mapping = mapping;
|
||||
|
||||
return im;
|
||||
@@ -480,7 +480,7 @@ async function createGridImage(ctx: VisualContext, volume: Volume, key: number,
|
||||
const isoLevel = clamp(normalize(Volume.IsoValue.toAbsolute(isoValue, stats).absoluteValue, min, max), 0, 1);
|
||||
|
||||
const im = Image.create(imageTexture, corners, groupTexture, valueTexture, trim, isoLevel, image);
|
||||
im.setBoundingSphere(Volume.isPeriodic(volume) ? Volume.getBoundingSphere(volume) : Grid.getBoundingSphere(volume.grid));
|
||||
im.setBoundingSphere(Volume.isPeriodic(volume) ? Volume.getBoundingSphere(volume, Interval.ofLength(volume.instances.length as Volume.InstanceIndex)) : Grid.getBoundingSphere(volume.grid));
|
||||
im.meta.mapping = mapping;
|
||||
|
||||
return im;
|
||||
|
||||
Reference in New Issue
Block a user