Add `cap` prop to ColumnLayer for dome and cone top geometry by Copilot · Pull Request #10038 · visgl/deck.gl

Adds a geometry-only cap prop to ColumnLayer that shapes the top of each column. No per-instance attributes — zero rendering overhead on instances.

Original prompt

This section details on the original issue you should resolve

<issue_title>[RFC] Trees</issue_title>
<issue_description>### Target Use Case

We started simply: in Leaflet, a CircleMarker was enough to show where each tree was. As we began extracting more structure—height, canopy area, density, fruit count, leaf color, and other derived metrics—we needed to communicate more than just location. We moved to deck.gl and adopted ColumnLayer, mapping height to a selected metric to give farmers an at-a-glance 3D view of their fields.

But a tree is more than a single scalar. It has a trunk and a canopy, a footprint and a volume, and a set of physical and biological traits that could all be visually encoded. I initially only wanted to add a dome-like bevel to the “gaps” in the field (viable row space with no tree), but while iterating on the bevel logic it became clear that there is no way today to vary the radius per column. Without per-instance radius, it’s hard to represent organic structure—canopy vs. trunk size, domed ground patches, tapered forms—and in practice the cap shape and the column radius need to be controlled together.

V0 (2020-2023) V1 (2023-2026) Proposed V2 (2026+)
V0 V1 V2

Proposal

I have #9933 as a functional prototype.

Yet, for discussion, I propose two opt-in accessors:

  • getRadius: per-instance base radius of the column

  • getBevel: per-instance cap profile (flat, dome, cone, or custom bevel parameters).

The existing radius, which I propose be renamed to radiusScale - consistent with the ScatterplotLayer - remains as a global multiplier applied on top of getRadius.

The effective radius becomes:

effectiveRadius = getRadius(d) * radiusScale(d)

For backwards compatibility and zero-cost default behavior:

If getRadius is not provided, the layer follows the current ColumnLayer path:

  • getRadius(d) = 1

  • radiusScale = 1000

If getRadius is provided, the layer switches to an opt-in shader/attribute path that uses the supplied base radius and applies radiusScale as a multiplier.

column-layer.ts

getShaders() {
  const defines: Record<string, any> = {};

  const {flatShading, getRadius} = this.props;

  if (flatShading) {
    defines.FLAT_SHADING = 1;
  }

  if (getRadius) {
    defines.HAS_RADIUS = 1;
  }

  return super.getShaders({
    vs,
    fs,
    defines,
    modules: [project32, flatShading ? phongMaterial : gouraudMaterial, picking, columnUniforms]
  });
}

column-layer-vertex.glsl.ts

#if HAS_RADIUS
in float instanceRadii;
#endif

This Gemini creation should help portray the bevel concept:

Image

Finally, I want to thank @felixpalmer for challenging me to think thoroughly though this proposal. I understand its a big change, and these things have gotten easy to do with agentic yes-men, but I know true learning happens in discussions like this, with real human feedback and disagreement.

As a little plus, it would answer this 2020 discussion question: #4324</issue_description>

<agent_instructions>Take the original idea I made in #9933 and Felix's feedback for a simple cap shape modifier on the columnlayer and make that simple addition rather than making the complex tree shape - just a column cap.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@felixpalmer For your use case, could you not use `ScenegraphLayer` with a custom tree model and then vary the radius using `getScale`?

My view on this proposal is that we should not add any new accessors - the ColumnLayer is used by other layers for rendering and often many columns are drawn. Adding accessors would slow this down even if people we not to use the feature.

I think the scope should be limited to adding a dome or cap purely by modifying the geometry as this will not impact performance but will give users a way to apply a different style to the columns.

@chrisgervang @Pessimistress @ibgreen for comment</comment_new>...

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.