deck.gl
deck.gl copied to clipboard
[Feat] Support dashed lines in ArcLayer
Target Use Case
We use MapLibre to show cartoon maps of anatomical features on which neuron paths are drawn, using both plain and dashed coloured lines. The resulting map looks cluttered when there is a large number of neuron paths -- going into 3D, using Deck's ArcLayer, simply works (thanks!), nicely separating the paths. We just now need a layer with arcs drawn as dashed lines...
Proposal
Is this a case of extending the path-styles extension to ArcLayer? Or rather, what limits this extension to PathLayer?
While the ArcLayer doesn't support the PathExtension, you could generate arcs in JS with the PathLayer and use the PathExtension. Here's a codepen demonstrating this concept. While it won't be as performant as the shader-based arcs, the shape can be customized and its dashed.
Thanks!
I've now got dashed lines in a custom ArcLayer (by creating TRIANGLES instead of a TRIANGLE_STRIP and patching the shaders). Quite happy to show how I've done this but it won't be for a week or three as am about to go on leave -- would you like a PR or have code pasted here?
Oh cool. I'd love to see your solution, whatever's convenient for you.
Here's the relevant bits:
//==============================================================================
const transparencyCheck = '|| length(vColor) == 0.0'
class ArcMapLayer extends ArcLayer
{
static layerName = 'ArcMapLayer'
constructor(...args)
{
super(...args)
}
getShaders()
//==========
{
const shaders = super.getShaders()
shaders.fs = `#version 300 es\n${shaders.fs}`
.replace('isValid == 0.0', `isValid == 0.0 ${transparencyCheck}`)
shaders.vs = `#version 300 es\n${shaders.vs}`
return shaders
}
redraw()
//======
{
this.internalState.changeFlags.dataChanged = true
this.setNeedsUpdate()
}
}
//==============================================================================
const makeDashedTriangles = ` float alpha = floor(fract(float(gl_VertexID)/12.0)+0.5);
if (vColor.a != 0.0) vColor.a *= alpha;
`
class ArcDashedLayer extends ArcMapLayer
{
static layerName = 'ArcDashedLayer'
constructor(...args)
{
super(...args)
}
getShaders()
//==========
{
const shaders = super.getShaders()
shaders.vs = shaders.vs.replace('DECKGL_FILTER_COLOR(', `${makeDashedTriangles}\n DECKGL_FILTER_COLOR(`)
return shaders
}
_getModel(gl)
//===========
{
const {numSegments} = this.props
let positions = []
for (let i = 0; i < numSegments; i++) {
positions = positions.concat([i, 1, 0, i, -1, 0, i+1, 1, 0,
i, -1, 0, i+1, 1, 0, i+1, -1, 0])
}
const model = new Model(gl, {
...this.getShaders(),
id: this.props.id,
geometry: new Geometry({
drawMode: GL.TRIANGLES,
attributes: {
positions: new Float32Array(positions)
}
}),
isInstanced: true,
})
model.setUniforms({numSegments: numSegments})
return model
}
}
//==============================================================================
export class Paths3DLayer
{
#arcLayers = new Map()
#layerOptions(pathType)
//=====================
{
const pathData = [...this.#pathData.values()]
.filter(ann => (this.#knownTypes.includes(ann.kind) && (ann.kind === pathType)
|| !this.#knownTypes.includes(ann.kind) && (pathType === 'other')))
return {
id: `arc-${pathType}`,
data: pathData,
pickable: true,
autoHighlight: true,
numSegments: 400,
// Styles
getSourcePosition: f => f.pathStartPosition,
getTargetPosition: f => f.pathEndPosition,
getSourceColor: this.#pathColour.bind(this),
getTargetColor: this.#pathColour.bind(this),
highlightColor: o => this.#pathColour(o.object),
opacity: 1.0,
getWidth: 3,
}
}
#addArcLayer(pathType)
//====================
{
const layer = this.#pathStyles.get(pathType).dashed
? new ArcDashedLayer(this.#layerOptions(pathType))
: new ArcMapLayer(this.#layerOptions(pathType))
this.#arcLayers.set(pathType, layer)
}
#setupDeckOverlay()
//=================
{
[...this.#pathStyles.values()].filter(style => this.#pathManager.pathTypeEnabled(style.type))
.forEach(style => this.#addArcLayer(style.type))
this.#deckOverlay = new DeckOverlay({
layers: [...this.#arcLayers.values()],
})
}
}
//==============================================================================