diff --git a/typescript/packages/subsurface-viewer/src/extensions/collision-modifier-extension.ts b/typescript/packages/subsurface-viewer/src/extensions/collision-modifier-extension.ts new file mode 100644 index 0000000000..2a5f7cdfb4 --- /dev/null +++ b/typescript/packages/subsurface-viewer/src/extensions/collision-modifier-extension.ts @@ -0,0 +1,20 @@ +import type { Layer } from "@deck.gl/core"; +import { CollisionFilterExtension } from "@deck.gl/extensions"; + +const injectionVs = { + "vs:DECKGL_FILTER_COLOR": ` + if (collision_fade != 0.0) { + color.a = 1.0 / collision_fade; // Note: this will counteract the fading of the labels caused by deck.gl's CollisionFilterExtension + } + `, +}; + +export class CollisionModifierExtension extends CollisionFilterExtension { + getShaders(this: Layer) { + const superShaders = super.getShaders(); + return { + ...superShaders, + inject: injectionVs, + }; + } +} diff --git a/typescript/packages/subsurface-viewer/src/layers/wells/wellsLayer.ts b/typescript/packages/subsurface-viewer/src/layers/wells/wellsLayer.ts index a1b059d0dd..d0436607c5 100644 --- a/typescript/packages/subsurface-viewer/src/layers/wells/wellsLayer.ts +++ b/typescript/packages/subsurface-viewer/src/layers/wells/wellsLayer.ts @@ -7,9 +7,8 @@ import type { Position, UpdateParameters, } from "@deck.gl/core"; - +import { CollisionModifierExtension } from "../../extensions/collision-modifier-extension"; import { CompositeLayer, OrbitViewport } from "@deck.gl/core"; - import type { ExtendedLayerProps, LayerPickInfo, @@ -53,6 +52,10 @@ import { invertPath, splineRefine, } from "./utils/spline"; +import { create, all } from "mathjs"; + +const math = create(all, { randomSeed: "a" }); +const randomFunc = math?.random ? math.random : Math.random; type StyleAccessorFunction = ( object: Feature, @@ -115,6 +118,10 @@ export interface WellsLayerProps extends ExtendedLayerProps { wellNameAtTop: boolean; wellNameSize: number; wellNameColor: Color; + /** If true will prevent well name cluttering by not displaying overlapping names. + * default false. + */ + hideOverlappingWellNames: boolean; isLog: boolean; depthTest: boolean; /** If true means that input z values are interpreted as depths. @@ -151,6 +158,7 @@ const defaultProps = { wellNameAtTop: false, wellNameSize: 14, wellNameColor: [0, 0, 0, 255], + hideOverlappingWellNames: false, selectedWell: "@@#editedData.selectedWells", // used to get data from deckgl layer depthTest: true, ZIncreasingDownwards: true, @@ -618,7 +626,37 @@ export default class WellsLayer extends CompositeLayer { }) ); - // well name + // Reduced cluttering properties + const clutterProps = { + background: true, + collisionEnabled: true, + getCollisionPriority: (d: Feature) => { + const labelSize = d.properties?.["name"].length ?? 1; + if (is3d) { + // In 3D prioritize according to label size. + return labelSize; + } else { + // In 2D prioritize according z height. + const labelPosition = getAnnotationPosition( + d, + this.props.wellNameAtTop, + true, + this.props.lineStyle?.color + ); + + const priority = labelPosition + ? (labelPosition?.[2] ?? 1) / 10 + randomFunc() // priority must be in [-1000, 1000] + : labelSize; + return priority; + } + }, + collisionTestProps: { + sizeScale: 1, + }, + collisionGroup: "nobodys", + extensions: [new CollisionModifierExtension()], + }; + const namesLayer = new TextLayer( this.getSubLayerProps({ id: "names", @@ -644,6 +682,8 @@ export default class WellsLayer extends CompositeLayer { }, parameters, visible: this.props.wellNameVisible && !fastDrawing, + + ...(this.props.hideOverlappingWellNames ? clutterProps : {}), }) ); @@ -799,8 +839,8 @@ function getAnnotationPosition( color_accessor: ColorAccessor ): Position | null { if (name_at_top) { + // Read top position from Point geometry, if not present, read it from LineString geometry let top; - // Read top position from Point geometry, if not present, read it from LineString geometry const well_head = getWellHeadPosition(well_data); if (well_data) top = well_head; diff --git a/typescript/packages/subsurface-viewer/src/storybook/examples/__image_snapshots__/subsurfaceviewer-examples-camera--scale-factor-hook.png b/typescript/packages/subsurface-viewer/src/storybook/examples/__image_snapshots__/subsurfaceviewer-examples-camera--scale-factor-hook.png index 6d93daecec..3377fc0f23 100644 Binary files a/typescript/packages/subsurface-viewer/src/storybook/examples/__image_snapshots__/subsurfaceviewer-examples-camera--scale-factor-hook.png and b/typescript/packages/subsurface-viewer/src/storybook/examples/__image_snapshots__/subsurfaceviewer-examples-camera--scale-factor-hook.png differ diff --git a/typescript/packages/subsurface-viewer/src/storybook/layers/WellsLayer.stories.tsx b/typescript/packages/subsurface-viewer/src/storybook/layers/WellsLayer.stories.tsx index 26105bbc3a..469957e746 100644 --- a/typescript/packages/subsurface-viewer/src/storybook/layers/WellsLayer.stories.tsx +++ b/typescript/packages/subsurface-viewer/src/storybook/layers/WellsLayer.stories.tsx @@ -17,7 +17,11 @@ import { NativeSelect } from "@equinor/eds-core-react"; import type { SubsurfaceViewerProps } from "../../SubsurfaceViewer"; import SubsurfaceViewer from "../../SubsurfaceViewer"; -import type { MapMouseEvent } from "../../components/Map"; +import type { + MapMouseEvent, + Point3D, + BoundingBox2D, +} from "../../components/Map"; import AxesLayer from "../../layers/axes/axesLayer"; import WellsLayer from "../../layers/wells/wellsLayer"; @@ -614,6 +618,8 @@ const SimplifiedRenderingComponent: React.FC = ( layers: [ new WellsLayer({ data: "./gullfaks.json", + wellNameVisible: true, + wellNameAtTop: true, wellHeadStyle: { size: 4 }, refine: true, outline: true, @@ -652,6 +658,112 @@ export const SimplifiedRendering: StoryObj = { render: (args) => , }; +type ClutterProps = { + hideOverlappingWellNames: boolean; + wellNameAtTop: boolean; +}; + +const ReducedWellNameClutterComponent: React.FC = ( + props: ClutterProps +) => { + const propsWithLayers = { + id: "clutter", + layers: [ + new WellsLayer({ + data: "./gullfaks.json", + wellNameVisible: true, + wellNameAtTop: props.wellNameAtTop, + wellHeadStyle: { size: 4 }, + hideOverlappingWellNames: props.hideOverlappingWellNames, + refine: true, + outline: true, + ZIncreasingDownwards: false, + }), + new AxesLayer({ + id: "axes-layer", + bounds: [450000, 6781000, 0, 464000, 6791000, 3500], + }), + ], + cameraPosition: { + rotationOrbit: 45, + rotationX: 45, + zoom: -4, + target: [ + (450000 + 464000) / 2, + (6781000 + 6791000) / 2, + -3500 / 2, + ] as Point3D, + }, + views: { + layout: [1, 1] as [number, number], + viewports: [ + { + id: "view_1", + show3D: true, + }, + ], + }, + }; + + return ; +}; + +export const ReducedWellNameClutter3D: StoryObj< + typeof ReducedWellNameClutterComponent +> = { + args: { + hideOverlappingWellNames: false, + wellNameAtTop: true, + }, + render: (args) => , +}; + +const ReducedWellNameClutterComponent2D: React.FC = ( + props: ClutterProps +) => { + const propsWithLayers = { + id: "clutter", + layers: [ + new WellsLayer({ + data: "./gullfaks.json", + wellNameVisible: true, + wellNameAtTop: props.wellNameAtTop, + wellHeadStyle: { size: 4 }, + hideOverlappingWellNames: props.hideOverlappingWellNames, + refine: true, + outline: true, + ZIncreasingDownwards: false, + }), + new AxesLayer({ + id: "axes-layer", + bounds: [450000, 6781000, 0, 464000, 6791000, 3500], + }), + ], + bounds: [450000, 6781000, 464000, 6791000] as BoundingBox2D, + views: { + layout: [1, 1] as [number, number], + viewports: [ + { + id: "view_1", + show3D: false, + }, + ], + }, + }; + + return ; +}; + +export const ReducedWellNameClutter2D: StoryObj< + typeof ReducedWellNameClutterComponent +> = { + args: { + hideOverlappingWellNames: false, + wellNameAtTop: true, + }, + render: (args) => , +}; + export const Wells3dDashed: StoryObj = { args: { ...defaultProps, diff --git a/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-axes2dlayer--font-size.png b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-axes2dlayer--font-size.png index 9be3932d05..ae484df970 100644 Binary files a/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-axes2dlayer--font-size.png and b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-axes2dlayer--font-size.png differ diff --git a/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--custom-colored-wells.png b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--custom-colored-wells.png index 47661d661d..b15ca901e6 100644 Binary files a/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--custom-colored-wells.png and b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--custom-colored-wells.png differ diff --git a/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--reduced-well-name-clutter-2-d.png b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--reduced-well-name-clutter-2-d.png new file mode 100644 index 0000000000..9ca930ca58 Binary files /dev/null and b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--reduced-well-name-clutter-2-d.png differ diff --git a/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--reduced-well-name-clutter-3-d.png b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--reduced-well-name-clutter-3-d.png new file mode 100644 index 0000000000..fc2fa87680 Binary files /dev/null and b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--reduced-well-name-clutter-3-d.png differ diff --git a/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--simplified-rendering.png b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--simplified-rendering.png index e160a789e6..5d9fbb980a 100644 Binary files a/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--simplified-rendering.png and b/typescript/packages/subsurface-viewer/src/storybook/layers/__image_snapshots__/subsurfaceviewer-wells-layer--simplified-rendering.png differ