CesiumGS / CesiumGS/cesium-native

Meters-mode point outlines render incorrectly in VectorRasterizer (affects both vector raster overlays)

Open
#1,413 1 comment 1 reaction 0 assignees View on GitHub
bug
Dominant language
C++
Stars
623
Forks
277
PR merge metrics
No merged PRs in 30d

Description

## Summary

When a point's outline uses `LineWidthMode::Meters` and the resulting outline width exceeds the point's fill `radius`, the point renders incorrectly (a broken/blobby ring instead of a clean outlined circle).

This affects both `CesiumVectorOverlays::GeoJsonDocumentRasterOverlay` and `CesiumVectorOverlays::VectorTilesRasterOverlay`, because both share the same rendering code path.

## Where it lives

The bug is in the shared rasterizer, not in either overlay's bounds
logic:

`CesiumVectorData/src/VectorRasterizer.cpp` -> `drawPointsImpl`:

if (style.outline) {
setStrokeWidth(context, *style.outline, ellipsoid, bounds);
for (size_t i = 0; i < vertices.size(); i++) {
context.strokeCircle(
BLCircle(vertices[i].x, vertices[i].y, style.radius),
BLRgba32(style.outline->getColor(seeds[i] ^ 31).toRgba32()));
}
}

The outline is stroked centered on the circle of radius `style.radius`. `setStrokeWidth` converts a meters-mode width into a pixel stroke width:

context.setStrokeWidth(
(context.targetWidth() * style.width) /
(bounds.computeWidth() * ellipsoid.getRadii().x));

Once that pixel stroke width exceeds `style.radius`, the stroke's inner edge crosses the circle center and the annulus self-overlaps. Combined with the context's `BL_FILL_RULE_EVEN_ODD`, this produces the broken appearance rather than a filled/outlined disc.

## Steps to reproduce

1. Add a `Point` (or `MultiPoint`) to a vector raster overlay (GeoJsonDocumentRasterOverlay or VectorTilesRasterOverlay).
2. Set the point's outline `widthMode` to `Meters` with a width whose pixel-equivalent is larger than the point's `radius`.
3. Observe the rendered tiles.

## Expected

The point renders as a filled circle with an outline, matching how a pixel-mode outline of comparable width renders.

## Actual

A broken/self-overlapping ring (see screenshot on PR #1400 discussion).

## Notes

- Reproduces in both overlays because they share `VectorRasterizer::drawPoints` / `drawPointsImpl`.
- Discovered during review of PR #1400 (Rasterize Point/MultiPoint in GeoJsonDocumentRasterOverlay). That PR was intentionally narrowed to the "points now rasterize" fix; this shared rendering bug was left out of scope and is tracked here.

## Possible directions

- Inset the stroked circle radius by half the pixel stroke width so the outline stays outside the fill rather than overrunning the center, and clamp so the radius does not go negative.
- Or clamp the effective outline width relative to the radius.
- Whatever the fix, it belongs in the shared VectorRasterizer so both overlays benefit, and should be covered by a render test in TestVectorRasterizer.

Contributor guide

Open the contributing guide

Research direction

Start in CesiumVectorData/src/VectorRasterizer.cpp at drawPointsImpl, then inspect setStrokeWidth and the strokeCircle call to reproduce the meters-mode case where the stroke exceeds style.radius. Run or extend the render coverage in TestVectorRasterizer. Done means points render as clean filled circles with outlines for both shared overlay paths, including the oversized meters-mode case.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
computer-graphics
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.