diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts index eb2c3af88ad8c3de56c9ca3a3f9a516488825aeb..5032bef59feeebf06e2b78c8a41b4ddad4644495 100644 --- a/src/mol-repr/representation.ts +++ b/src/mol-repr/representation.ts @@ -130,6 +130,7 @@ namespace Representation { create(): S update(state: S, update: Partial<S>): void } + export const StateBuilder: StateBuilder<State> = { create: createState, update: updateState } export type Any = Representation<any, any, any> export const Empty: Any = { diff --git a/src/mol-repr/volume/isosurface-mesh.ts b/src/mol-repr/volume/isosurface-mesh.ts index f0d25305018d0e13c9469ff2badaff1bf5a2fb2d..94c7200386880ad4aac2b7353d53e34c28392d96 100644 --- a/src/mol-repr/volume/isosurface-mesh.ts +++ b/src/mol-repr/volume/isosurface-mesh.ts @@ -10,18 +10,21 @@ import { VolumeVisual, VolumeRepresentation, VolumeRepresentationProvider } from import { EmptyLoci } from 'mol-model/loci'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; -import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm'; +import { computeMarchingCubesMesh, computeMarchingCubesLines } from 'mol-geo/util/marching-cubes/algorithm'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { VisualUpdateState } from 'mol-repr/util'; -import { RepresentationContext, RepresentationParamsGetter } from 'mol-repr/representation'; +import { RepresentationContext, RepresentationParamsGetter, Representation } from 'mol-repr/representation'; import { Theme, ThemeRegistryContext } from 'mol-theme/theme'; import { VisualContext } from 'mol-repr/visual'; import { NullLocation } from 'mol-model/location'; +import { Lines } from 'mol-geo/geometry/lines/lines'; interface VolumeIsosurfaceProps { isoValue: number } +// + export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: VolumeData, theme: Theme, props: VolumeIsosurfaceProps, mesh?: Mesh) { ctx.runtime.update({ message: 'Marching cubes...' }); @@ -43,11 +46,8 @@ export const IsosurfaceMeshParams = { isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }), } export type IsosurfaceMeshParams = typeof IsosurfaceMeshParams -export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: VolumeData) { - return PD.clone(IsosurfaceMeshParams) -} -export function IsosurfaceVisual(): VolumeVisual<IsosurfaceMeshParams> { +export function IsosurfaceMeshVisual(): VolumeVisual<IsosurfaceMeshParams> { return VolumeVisual<Mesh, IsosurfaceMeshParams>({ defaultProps: PD.getDefaultValues(IsosurfaceMeshParams), createGeometry: createVolumeIsosurfaceMesh, @@ -61,16 +61,73 @@ export function IsosurfaceVisual(): VolumeVisual<IsosurfaceMeshParams> { }) } -export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceMeshParams>): VolumeRepresentation<IsosurfaceMeshParams> { - return VolumeRepresentation('Isosurface ', ctx, getParams, IsosurfaceVisual) +// + +export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume: VolumeData, theme: Theme, props: VolumeIsosurfaceProps, lines?: Lines) { + ctx.runtime.update({ message: 'Marching cubes...' }); + + const params = { + isoLevel: props.isoValue, + scalarField: volume.data + } + const wireframe = await computeMarchingCubesLines(params, lines).runAsChild(ctx.runtime) + + const transform = VolumeData.getGridToCartesianTransform(volume); + Lines.transformImmediate(wireframe, transform) + + return wireframe; +} + +export const IsosurfaceWireframeParams = { + ...Lines.Params, + isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }), +} +export type IsosurfaceWireframeParams = typeof IsosurfaceWireframeParams + +export function IsosurfaceWireframeVisual(): VolumeVisual<IsosurfaceWireframeParams> { + return VolumeVisual<Lines, IsosurfaceWireframeParams>({ + defaultProps: PD.getDefaultValues(IsosurfaceWireframeParams), + createGeometry: createVolumeIsosurfaceWireframe, + createLocationIterator: (volume: VolumeData) => LocationIterator(1, 1, () => NullLocation), + getLoci: () => EmptyLoci, + mark: () => false, + setUpdateState: (state: VisualUpdateState, newProps: PD.Values<IsosurfaceWireframeParams>, currentProps: PD.Values<IsosurfaceWireframeParams>) => { + if (newProps.isoValue !== currentProps.isoValue) state.createGeometry = true + }, + geometryUtils: Lines.Utils + }) +} + +// + +const IsosurfaceVisuals = { + 'solid': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceMeshParams>) => VolumeRepresentation('Isosurface mesh', ctx, getParams, IsosurfaceMeshVisual), + 'wireframe': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceWireframeParams>) => VolumeRepresentation('Isosurface wireframe', ctx, getParams, IsosurfaceWireframeVisual), +} +type IsosurfaceVisualName = keyof typeof IsosurfaceVisuals +const IsosurfaceVisualOptions = Object.keys(IsosurfaceVisuals).map(name => [name, name] as [IsosurfaceVisualName, string]) + +export const IsosurfaceParams = { + ...IsosurfaceMeshParams, + ...IsosurfaceWireframeParams, + visuals: PD.MultiSelect<IsosurfaceVisualName>(['solid'], IsosurfaceVisualOptions), +} +export type IsosurfaceParams = typeof IsosurfaceParams +export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: VolumeData) { + return PD.clone(IsosurfaceParams) +} + +export type IsosurfaceRepresentation = VolumeRepresentation<IsosurfaceParams> +export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceParams>): IsosurfaceRepresentation { + return Representation.createMulti('Isosurface', ctx, getParams, Representation.StateBuilder, IsosurfaceVisuals as unknown as Representation.Def<VolumeData, IsosurfaceParams>) } -export const IsosurfaceRepresentationProvider: VolumeRepresentationProvider<IsosurfaceMeshParams> = { +export const IsosurfaceRepresentationProvider: VolumeRepresentationProvider<IsosurfaceParams> = { label: 'Isosurface', description: 'Displays an isosurface of volumetric data.', factory: IsosurfaceRepresentation, getParams: getIsosurfaceParams, - defaultValues: PD.getDefaultValues(IsosurfaceMeshParams), + defaultValues: PD.getDefaultValues(IsosurfaceParams), defaultColorTheme: 'uniform', defaultSizeTheme: 'uniform' } \ No newline at end of file