From 3ffdc5ef90ccf3d5699186f02c8807caadf70e3a Mon Sep 17 00:00:00 2001 From: felixpalmer Date: Tue, 22 Nov 2022 10:17:48 +0100 Subject: [PATCH] Add maskInverted prop to MaskExtension (#7440) --- .../extensions/mask-extension.md | 4 ++++ modules/extensions/src/mask/mask.ts | 10 ++++++++-- modules/extensions/src/mask/shader-module.ts | 8 +++++++- test/apps/mask/app.js | 18 +++++++++++++++--- test/modules/extensions/mask.spec.js | 10 ++++++++++ .../golden-images/mask-effect-inverted.png | Bin 0 -> 42554 bytes test/render/test-cases/effects.js | 10 ++++++---- 7 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 test/render/golden-images/mask-effect-inverted.png diff --git a/docs/api-reference/extensions/mask-extension.md b/docs/api-reference/extensions/mask-extension.md index 702d70ff6b5..a4fb0b29589 100644 --- a/docs/api-reference/extensions/mask-extension.md +++ b/docs/api-reference/extensions/mask-extension.md @@ -89,6 +89,10 @@ Id of the layer that defines the mask. The mask layer must use the prop `operati ![maskByInstance](https://raw.githubusercontent.com/visgl/deck.gl-data/master/images/docs/mask-by-instance.png) +##### `maskInverted` (Boolean, optional) + +When `maskInverted` is true the result of the masking operation is inverted. Inversion is applied when reading the mask, thus it is possible to use the same mask normally on some layers and inverted on other layers. Defaults to `false`. + ## Limitations - The current implementation supports up to 4 masks at the same time. diff --git a/modules/extensions/src/mask/mask.ts b/modules/extensions/src/mask/mask.ts index 11b9b8ef43c..18ffccaca9e 100644 --- a/modules/extensions/src/mask/mask.ts +++ b/modules/extensions/src/mask/mask.ts @@ -5,7 +5,8 @@ import type {Layer} from '@deck.gl/core'; const defaultProps = { maskId: '', - maskByInstance: undefined + maskByInstance: undefined, + maskInverted: false }; export type MaskExtensionProps = { @@ -19,6 +20,10 @@ export type MaskExtensionProps = { * If not specified, it is automatically deduced from the layer. */ maskByInstance?: boolean; + /** + * Inverts the masking operation + */ + maskInverted?: boolean; }; /** Allows layers to show/hide objects by a geofence. */ @@ -43,7 +48,7 @@ export default class MaskExtension extends LayerExtension { /* eslint-disable camelcase */ draw(this: Layer, {uniforms, context, moduleParameters}: any) { uniforms.mask_maskByInstance = this.state.maskByInstance; - const {maskId = ''} = this.props; + const {maskId = '', maskInverted = false} = this.props; const {maskChannels} = moduleParameters; const {viewport} = context; if (maskChannels && maskChannels[maskId]) { @@ -51,6 +56,7 @@ export default class MaskExtension extends LayerExtension { let {coordinateSystem: fromCoordinateSystem} = maskChannels[maskId]; uniforms.mask_enabled = true; uniforms.mask_channel = index; + uniforms.mask_inverted = maskInverted; if (fromCoordinateSystem === COORDINATE_SYSTEM.DEFAULT) { fromCoordinateSystem = viewport.isGeospatial diff --git a/modules/extensions/src/mask/shader-module.ts b/modules/extensions/src/mask/shader-module.ts index 0e9ca572baa..0506d9af851 100644 --- a/modules/extensions/src/mask/shader-module.ts +++ b/modules/extensions/src/mask/shader-module.ts @@ -14,6 +14,7 @@ const fs = ` uniform sampler2D mask_texture; uniform int mask_channel; uniform bool mask_enabled; +uniform bool mask_inverted; bool mask_isInBounds(vec2 texCoords) { if (!mask_enabled) { return true; @@ -29,7 +30,12 @@ bool mask_isInBounds(vec2 texCoords) { } else if (mask_channel == 3) { maskValue = maskColor.a; } - return maskValue < 0.5; + + if (mask_inverted) { + return maskValue >= 0.5; + } else { + return maskValue < 0.5; + } } `; diff --git a/test/apps/mask/app.js b/test/apps/mask/app.js index 1218b1a11b8..3a49cf2c4ff 100644 --- a/test/apps/mask/app.js +++ b/test/apps/mask/app.js @@ -114,6 +114,7 @@ function getLayerData(data) { export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}) { const {arcs, targets, sources} = useMemo(() => getLayerData(data), [data]); const [maskEnabled, setMaskEnabled] = useState(true); + const [maskInverted, setMaskInverted] = useState(false); const [showLayers, setShowLayers] = useState(true); const [selectedCounty, selectCounty] = useState(null); const [selectedCounty2, selectCounty2] = useState(null); @@ -202,7 +203,8 @@ export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}) { parameters: {depthTest: false}, extensions: [new MaskExtension()], maskId: maskEnabled && 'mask2', - maskByInstance: false + maskByInstance: false, + maskInverted }), new ScatterplotLayer({ id: 'sources', @@ -210,7 +212,8 @@ export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}) { radiusScale: 3000, getFillColor: d => (d.gain > 0 ? TARGET_COLOR : SOURCE_COLOR), extensions: [new MaskExtension()], - maskId: maskEnabled && 'mask2' + maskId: maskEnabled && 'mask2', + maskInverted }), new ScatterplotLayer({ id: 'targets', @@ -224,7 +227,8 @@ export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}) { radiusScale: 3000, getFillColor: d => (d.net > 0 ? TARGET_COLOR : SOURCE_COLOR), extensions: [new MaskExtension()], - maskId: maskEnabled && 'mask2' + maskId: maskEnabled && 'mask2', + maskInverted }), new ArcLayer({ id: 'arc', @@ -259,6 +263,14 @@ export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}) { /> Use mask +