Skip to content

feat: Arc canvas #5883

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

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open

feat: Arc canvas #5883

wants to merge 8 commits into from

Conversation

Vinci10
Copy link

@Vinci10 Vinci10 commented Aug 6, 2025

Description:

As per #856 issue

This PR adds a new canvas.Arc object to Fyne, enabling the drawing of filled arcs and annular sectors (ring segments) with optional stroke and rounded corners. The Arc object supports:

  • Customizable fill color and stroke (color and width)
  • Configurable inner radius (for rings/annular sectors)
  • Start and end angles (in degrees, positive is counter-clockwise, negative is clockwise)
  • Corner radius for rounded arc ends
  • Full circle and partial arc rendering
  • Public API with Since: 2.7 annotation

Use cases:

  • Doughnut and Pie Charts
  • Loading indicator
  • Progress indicator

Examples:
arc

Checklist:

  • Tests included.
  • Lint and formatter run with no errors.
  • Tests all pass.

Where applicable:

  • Public APIs match existing style and have Since: line.
  • Any breaking changes have a deprecation path or have been discussed.
  • Check for binary size increases when importing new modules.

Known limitations:

  • macOS test failures (to be investigated) Done
  • corner radius not implemented yet in software painter Done

@andydotxyz
Copy link
Member

Thanks so much this is completely epic!

Just FYI I ran the Darwin test locally to show what is failing and here is a comparison of the draw vs expected:

Screenshot 2025-08-07 at 09 48 09

One thought - if the curved corners are not supported in software this could be landed without them and that could be added later (if that's easier for you).

Thanks so much :)

@Vinci10
Copy link
Author

Vinci10 commented Aug 8, 2025

Thank you for sharing failed data. I was able to fix macos unit tests.

@andydotxyz
Copy link
Member

Thank you for sharing failed data. I was able to fix macos unit tests.

Thanks for this. Just one thing remains:

arc.CornerRadius = 0 // rounded corners are supported in gl painter but not yet in software painter

is this fixed, or should it land without CornerRadius initially?

@Vinci10
Copy link
Author

Vinci10 commented Aug 9, 2025

It's not fixed. I can't figure out how to implement it properly.
If CornerRadius doesn't work in the software painter, does it only affect unit tests? If that's not a problem, it can be added later.

@Vinci10 Vinci10 marked this pull request as ready for review August 9, 2025 20:50
@dweymouth
Copy link
Contributor

dweymouth commented Aug 10, 2025

I'm not entirely sure whether any platform actually uses the software painter in "production", but nonetheless as Andy mentioned, we can land it without CornerRadius at all and implement it in a follow-up for all painters. Regardless of if the software painter is only used in tests, I don't think we want to introduce a known difference between the painters.

You can probably even leave the corner radius handling code in the GL painters, and just always set the uniform to 0 for now, and remove the public CornerRadius property from the Arc struct.

@andydotxyz
Copy link
Member

andydotxyz commented Aug 11, 2025

I'm not entirely sure whether any platform actually uses the software painter in "production",

The new "noos"/tinygo/tamago addition will be using it in prod.
Plus other devs like those building for reMarkable will be using the software driver.

@Vinci10
Copy link
Author

Vinci10 commented Aug 11, 2025

Thanks for update. I implemented rounded corners in software painter. Please check from your end whether the new canvas works fine and let me know if something should be fixed/changed.

@dweymouth
Copy link
Contributor

It looks like this has merge conflicts, probably with #5884. They shouldn't be too hard to resolve though, that linked PR introduced a helper function for passing uniforms to GL where it avoids the GL call if the uniform is being set to the same value.

@coveralls
Copy link

coveralls commented Aug 11, 2025

Coverage Status

coverage: 61.745% (-0.5%) from 62.216%
when pulling e46a6b9 on Vinci10:feature/arc_canvas
into 34b5b8b on fyne-io:develop.

@Vinci10
Copy link
Author

Vinci10 commented Aug 11, 2025

Conflicts resolved. Now I see some check failing Coverage decreased (-0.5%) to 61.704%. Not sure how to fix it.

@andydotxyz
Copy link
Member

Conflicts resolved. Now I see some check failing Coverage decreased (-0.5%) to 61.704%. Not sure how to fix it.

The only thing that stands out in the report is that markup_renderer does not have tests for the new arc.
(Yes it's a bit silly because it is then used everywhere - but the analysis is per-package I think)

@Vinci10
Copy link
Author

Vinci10 commented Aug 12, 2025

Conflicts resolved. Now I see some check failing Coverage decreased (-0.5%) to 61.704%. Not sure how to fix it.

The only thing that stands out in the report is that markup_renderer does not have tests for the new arc. (Yes it's a bit silly because it is then used everywhere - but the analysis is per-package I think)

Oh ok. Right it is tested but in canvas_test package and not directly but via test.AssertObjectRendersToMarkup

@Vinci10
Copy link
Author

Vinci10 commented Aug 12, 2025

The code I used to create the simple Doughnut and Pie Charts (may help during testing)

type Slice struct {
	Value float32
	Color color.Color
}

func NewPieChart(cornerRadius, innerRadius, pad, strokeWidth float32, varyInnerRadius bool, data []Slice) *fyne.Container {
	total := float32(0)
	for _, s := range data {
		total += s.Value
	}

	objects := make([]fyne.CanvasObject, len(data))
	start := float32(-90) // 0° = right; we start at top (-90°)

	for i, s := range data {
		angle := 360 * s.Value / total
		end := start + angle

		// vary inner radius per slice for visual effect
		inner := innerRadius
		if varyInnerRadius && innerRadius > 0 {
			variation := innerRadius * (0.8 + 0.4*float32(i%3)/2)
			inner = variation
		}

		arc := &canvas.Arc{
			InnerRadius:  inner,
			StartAngle:   start + pad,
			EndAngle:     end,
			FillColor:    s.Color,
			CornerRadius: cornerRadius,
			StrokeColor:  color.White,
			StrokeWidth:  strokeWidth,
		}
		objects[i] = arc
		start = end
	}

	return container.NewStack(objects...)
}

// -- And the simple charts --

// Pie chart data:
data := []Slice{
    {Value: 30, Color: color.NRGBA{R: 255, G: 99, B: 71, A: 255}},  // Tomato Red
    {Value: 20, Color: color.NRGBA{R: 50, G: 205, B: 50, A: 255}},  // Lime Green
    {Value: 25, Color: color.NRGBA{R: 255, G: 120, B: 0, A: 255}},  // Orange
    {Value: 15, Color: color.NRGBA{R: 255, G: 215, B: 0, A: 255}},  // Gold
    {Value: 10, Color: color.NRGBA{R: 138, G: 43, B: 226, A: 255}}, // Blue Violet
}

chart := NewPieChart(15, 80, 8, 0, true, data)
chart2 := NewPieChart(0, 0, 0, 0, false, data)
chart3 := NewPieChart(0, 90, 1, 0, false, data)
chart4 := NewPieChart(20, 80, 1, 2, true, data)

@andydotxyz
Copy link
Member

Oh ok. Right it is tested but in canvas_test package and not directly but via test.AssertObjectRendersToMarkup

I'd say that is usage, not test - and the coveralls report is showing the same. See test/markup_renderer_test.go

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.

5 participants