Skip to content
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] Support dashed lines in ArcLayer #8447

Open
dbrnz opened this issue Jan 23, 2024 · 4 comments
Open

[Feat] Support dashed lines in ArcLayer #8447

dbrnz opened this issue Jan 23, 2024 · 4 comments
Labels

Comments

@dbrnz
Copy link

dbrnz commented Jan 23, 2024

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?

@dbrnz dbrnz added the feature label Jan 23, 2024
@chrisgervang
Copy link
Collaborator

chrisgervang commented Feb 13, 2024

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.

@dbrnz
Copy link
Author

dbrnz commented Feb 13, 2024

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?

@chrisgervang
Copy link
Collaborator

Oh cool. I'd love to see your solution, whatever's convenient for you.

@dbrnz
Copy link
Author

dbrnz commented Feb 14, 2024

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()],
        })
    }
}

//==============================================================================

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants