Skip to content

Implement line cap and join rendering in LineStroker#193

Draft
Copilot wants to merge 4 commits intomasterfrom
copilot/fix-line-cap-and-join-issue
Draft

Implement line cap and join rendering in LineStroker#193
Copilot wants to merge 4 commits intomasterfrom
copilot/fix-line-cap-and-join-issue

Conversation

Copy link
Contributor

Copilot AI commented Feb 7, 2026

SetLineCap() and SetLineJoin() had no effect on stroke rendering. The LineStroker accepted cap/join parameters but never applied them when generating stroke outlines.

Changes

Core implementation (draw2dbase/stroker.go):

  • Modified End() to call cap methods at stroke endpoints
  • Added applyStartCap() and applyEndCap() for ButtCap, SquareCap, RoundCap
  • Added processJoin() for BevelJoin, RoundJoin, MiterJoin
  • Store centerline points (center []float64) for accurate cap/join geometry
  • Add 0.5px epsilon to SquareCap extension to handle rasterization boundaries

Test updates:

Before:

// All cap styles rendered identically
gc.SetLineCap(draw2d.ButtCap)   // No effect
gc.SetLineCap(draw2d.SquareCap) // No effect
gc.SetLineCap(draw2d.RoundCap)  // No effect

After:

// ButtCap: flush at endpoint
// SquareCap: extends HalfLineWidth with rectangular end
// RoundCap: extends with semicircular arc

Test verification shows distinct pixel values:

  • ButtCap at x=160: 255 (white, no extension)
  • SquareCap at x=160: 127 (gray, has coverage)

Affects raster backends (draw2dimg, draw2dgl). PDF/SVG backends unaffected as they handle caps/joins natively.

Original prompt

Bug Description

SetLineCap() and SetLineJoin() have no visual effect on stroke rendering in the draw2dimg backend. All line cap styles (RoundCap, ButtCap, SquareCap) and join styles (RoundJoin, BevelJoin, MiterJoin) produce identical visual output.

Issues: #155, #27, #171

Root Cause Analysis

The LineStroker in draw2dbase/stroker.go receives the Cap and Join parameters but never uses them:

  1. End() method (line 61-80): When a path segment ends (no Close), it simply traces the vertices forward and rewind backward to create a parallel polygon, but never applies line caps at the start and end of the stroke. It should use l.Cap to determine how to cap the line endpoints.

  2. Close() method (line 55-59): When a path is closed, it simply appends the first vertex pair, but never applies line joins properly. The LineJoin() method (line 39-41) is empty.

  3. In draw2dimg/ftgc.go, the functions toFtCap() (line 374-384) and toFtJoin() (line 386-394) exist and correctly map draw2d caps/joins to freetype raster cappers/joiners, but they are never called anywhere.

Required Fix

The LineStroker.End() method must apply proper line caps at the start and end of open paths. Currently it creates a closed polygon by simply connecting vertices[0] back to vertices[0] at the end, which effectively acts as a round/default cap for all cap styles.

The fix should:

  1. In draw2dbase/stroker.go: Modify the End() method to apply the appropriate cap style (ButtCap, SquareCap, or RoundCap) at both the start and end of the stroke outline. The current implementation already creates a rough "round-like" cap by closing the polygon, but it doesn't differentiate between cap styles.

    • ButtCap: The stroke ends flush at the endpoint. The current parallel lines already end at the right position, so the cap is just connecting the last vertex to the last rewind vertex directly (straight line across).
    • SquareCap: The stroke extends by HalfLineWidth beyond the endpoint. Add extension vertices at both ends of the stroke.
    • RoundCap: The stroke has a semicircular cap at both ends. Add arc vertices at both ends.
  2. In draw2dbase/stroker.go: Implement proper line joins in the LineJoin() method or integrate join logic into the LineTo() method when consecutive segments meet.

  3. Update bug exposure tests: The tests TestBugExposure_Issue155_LineCapVisualComparison in bug_exposure_test.go and TestIssue155_SetLineCapDoesNotWork in known_issues_test.go should now PASS. Convert them from failing/skipped tests to passing tests.

  4. Update KNOWN_ISSUES.md: Remove or update the Issue SetLineCap does not work #155 entry to reflect the fix.

Important Implementation Notes

  • The LineStroker is used by all raster backends (draw2dimg and draw2dgl). The fix in stroker.go will benefit all backends.
  • The PDF and SVG backends handle caps/joins differently (through their native PDF/SVG instructions), so they are not affected.
  • The toFtCap() and toFtJoin() functions in draw2dimg/ftgc.go are currently unused dead code. They were likely intended to be used with the freetype rasterizer directly, but the current architecture uses the custom LineStroker instead. They can remain as reference or be removed.
  • Be careful not to break the existing polygon rendering - the Close() path (for closed shapes like rectangles, circles) should still work correctly.
  • Use math package for trigonometric calculations needed for RoundCap arcs.

Testing

# Run the bug exposure tests - they should PASS after the fix
go test -v -run "TestBugExposure_Issue155" .
go test -v -run "TestIssue155" .

# Run all tests to make sure nothing is broken
go test ./...

Files Likely to Modify

This pull request was created from Copilot chat.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits February 7, 2026 11:52
- Add applySt

artCap and applyEndCap methods to handle ButtCap, SquareCap, and RoundCap
- Store centerline points for accurate cap positioning
- Update test expectations to check at correct pixel positions
- All cap-related tests now pass

Co-authored-by: llgcode <524083+llgcode@users.noreply.github.com>
- Implement processJoin() for handling BevelJoin, RoundJoin, and MiterJoin
- Add pendingJoin flag to track when joins are needed
- Update KNOWN_ISSUES.md to reflect that Issues #155 and #171 are now fixed
- All tests pass including the linecapjoin sample

Co-authored-by: llgcode <524083+llgcode@users.noreply.github.com>
- Add clarifying comment for testX calculation
- Rename squareIsDarker to squareHasCoverage for better clarity
- All tests pass, no security issues detected

Co-authored-by: llgcode <524083+llgcode@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix line cap and join styles in draw2dimg backend Implement line cap and join rendering in LineStroker Feb 7, 2026
Copilot AI requested a review from llgcode February 7, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants