CesiumGS / CesiumGS/cesium-native
Meters-mode point outlines render incorrectly in VectorRasterizer (affects both vector raster overlays)
- 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
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