New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat] Masking based on a polygon #6552
Comments
TODO
|
What happens if you need to apply the same mask to multiple layers? Is it more efficient to define the mask with a polygon layer? |
One of my long-standing roadmap ideas is to allow masks to be created from arbitrary sets of layers (not just polygons) and then applied to other layers. This goes part of the way and I am excited to see it coming along. |
Good point. Currently it will work, but it is wasteful as the FBO is created again for each layer. I see two options:
@ibgreen I agree it would be really neat to allow masks from arbitrary layers and indeed this approach is pretty close. For this feature I would like to restrict to polygons only as they are the major use-case and I worry that allowing arbitrary layers would expand the scope too much right now. |
I prefer the option of maskPolygonLayer, I think it fits better with state updates (if the mask is updated). I'd name it |
I mean to support other types of layers in the future not in this first version |
We've done some tests and it works really fast. These are some problems we have found.
On the other hand, works really well with GeoJsonLayer, ScatterplotLayer and AggregationLayer |
I think it's perfectly fine to support only polygons in the initial release, though we should design the API with the eventual full-featured extension in mind. Having a stable API that incrementally expands its capabilities is better than introducing new APIs frequently. To work off of @alasarr 's idea, maybe something like:
|
Makes sense for me |
Support the idea to have a future proof API. I was imagining something more like: const maskLayer = new SolidPolygonLayer({
id: 'mask',
data: <polygon_data>
});
layers: [
new ArcLayer({
id: 'links',
extensions: new MaskExtension(),
maskLayer
}),
new ScatterplotLayer({
id: 'nodes',
extensions: new MaskExtension(),
maskLayer
}),
] Having Regarding placing the |
FWIW, I always envisioned the masking feature to support an Perhaps we could offer an overloaded prop
Worth keeping in mind:
|
@ibgreen is this your idea? Just to be sure we're following you: const maskLayer = new SolidPolygonLayer({
id: 'mask',
data: <polygon_data>
});
layers: [
maskLayer,
new ArcLayer({
id: 'links',
extensions: new MaskExtension(),
mask: maskLayer
}),
new ScatterplotLayer({
id: 'nodes',
extensions: new MaskExtension(),
mask: 'mask'
}),
] @Pessimistress why do you want to create the new MaskLayer? Is this because you want to define a common interface for the mask? For example maskLayer.getFBO() |
No that doesn't solve for the fully declarative case (JSON etc). I would imagine a single list of layers where some layers are marked as masks: layers: [
new SolidPolygonLayer({
id: 'mask',
mask: true,
data: <polygon_data>
});,
new ArcLayer({
id: 'links',
extensions: new MaskExtension(),
mask: maskLayer
}),
new ScatterplotLayer({
id: 'nodes',
extensions: new MaskExtension(),
mask: 'mask'
}),
] While I am not sure if extensions are currently supported in deck.gl/json, this would set the stage for a JSON description: "layers": [
{
"type": "SolidPolygonLayer",
"id": "mask",
"mask": true,
"data": []
},
{
"type": "ArcLayer",
"id": "links",
"extensions": ["mask"],
"mask": maskLayer
},
{
"type": "ScatterplotLayer",
"id": "nodes",
"extensions": ["mask"],
"mask": "mask"
}
] |
Without changing how the core functions, a layer must be in the So one solution is to wrap the mask with a new layer class We can also add a parent layer for both the mask and the masked: layers: [
// layer instances that are not affected
new GeoJsonLayer(),
// mask and masked
new MaskGroup({
maskLayer: new SolidPolygonLayer(),
layers: [
new ScatterplotLayer()
]
})
] An alternative is to create a |
In my proposal I added a prop
I actually wrote an RFC on layer groups a long time ago, it may be outdated but it did explore some of these idea. I hesitated to bring it up due to the scope creep.
I want to agree but won't that be hard to avoid in this case as clipping against the output of another layer is kind of the whole point? |
Are you proposing that we add a |
OK I see your point. I suppose it depends on how fundamental we feel that clipping is. One indication is that stencil operations are built into all low-level GPU apis.
Good question. To avoid a prop explosion one could generalize it to say |
Thanks for the input. I'm on board with the layers: [
new SolidPolygonLayer({
id: 'mask',
operation: 'mask',
data: <polygon_data>
}),
new ArcLayer({
id: 'links',
extensions: new MaskExtension(),
maskId: 'mask'
}),
new ScatterplotLayer({
id: 'nodes',
extensions: new MaskExtension(),
maskId: 'mask'
}),
] Some thoughts on the above discussion:
|
Thanks a lot to everyone for giving the feedback here, I think we're getting a really good approach! I also like the idea of @felixpalmer can't wait to see this implementation 😄 |
PR has been updated: #6554 |
Just to update on the API: Following input from @Pessimistress I have modified the approach so that the masking is implemented as an const maskEffect = new MaskEffect();
new Deck({
effects: [maskEffect],
layers: [
new SolidPolygonLayer({
id: 'mask',
operation: 'mask',
data: <polygon_data>
}),
new ArcLayer({
maskId: 'mask',
...
}),
new ScatterplotLayer({
maskId: 'mask',
...
})
]
}); |
Another update on the API: The import {OPERATION} from '@deck.gl/core';
import {MaskExtension} from '@deck.gl/extensions';
const maskExtension = new MaskExtension();
new Deck({
layers: [
new SolidPolygonLayer({
id: 'mask',
operation: OPERATION.MASK,
data: <polygon_data>
}),
new ArcLayer({
...
extensions: [maskExtension], // Enables masking and adds support for props below
maskEnabled: true | false // Optional (defaults to true),
maskByInstance: true | false // Optional (inferred by layer data)
}),
new ScatterplotLayer({
...
extensions: [maskExtension]
})
]
}); |
Nit: I think it is a bit inconvenient to add a new export OPERATION. Importing an additional top-level symbol is rather inconvenient and it doesn't really add much value. I would just accept string values, i.e. Once we move to typescript, we can just type it as type LayerProps = {
operation?: 'mask' | '...' | '...';
} FIWI, this is how I am designing the new luma.gl v9 API. |
Hello @felixpalmer , I am trying to use the Mask Extension with a HeatmapLayer, but it does not seem to work. I recently opened an issue: Can you please confirm if the extension is meant to work with HeatmapLayer? Thanks |
Target Use Case
Currently deck.gl has a ClipExtension which allows rendering of layers clipped to rectangular region. This feature proposes adds a new extension,
MaskExtension
, which performs masking based on an arbitrary polygon region.The
MaskExtension
would enable richer visualizations as clipping only by a rectangle is of limited use.Proposal
The
MaskExtension
can be added to a deck.gl layer in the standard fashion:The
MaskExtension
adds the following props:maskPolygon
: polygon outline (format the same as that used by the SolidPolygonLayer data prop)maskEnabled
: set to false to disable maskingmaskByInstance
: whether to clip by pixel or by the geometry instance (analogous toClipExtension.clipByInstance
)Below is a short video showing the
MaskExtension
in action.The demo is composed of a number of different layers using the
MaskExtension
in different fashions:GeoJsonLayer
with US states. When a state is selected, its geometry is used for themaskPolygon
SolidPolygonLayer
covering the entire USA with a solid green fill. This is masked using themaskPolygon
. Thus when Texas is selected, it appears to have a green backgroundArcLayer
showing the links between sources and target (flights between cities). As themaskByInstance
prop istrue
, all arcs originating inside the mask are showing in their entiretyScatterplotLayer
showing a buffer around each target. As themaskByInstance
prop is set tofalse
, these regions (in white) are clipped to the mask by pixel, and thus appear to be within the selected state.ScatterplotLayers
showing the sources and targets for the sources and targets. As themaskByInstance
prop istrue
, the red and green circles are not clipped by pixelThe text was updated successfully, but these errors were encountered: