circle

package module
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 6 Imported by: 0

README

circle

A circle drawn as four cubic Bézier segments, for Vibrant Gio, a design system for native desktop applications on macOS, Windows and Linux, written in pure Go on Gio. Three functions, no state.

Gio ships clip.Ellipse, and it takes an image.Rectangle — integer pixels, axis-aligned, sized by its bounds. That is exactly right for a rounded avatar in a layout and exactly wrong for a particle: a physics simulation produces a float32 centre and a float32 radius many times a second, and rounding both to a pixel grid every frame makes small circles jitter as they move and makes a scaled scene snap between sizes.

CirclePath takes an f32.Point centre and a float32 radius and returns a clip.PathSpec, so the geometry stays in floating point all the way to the rasteriser. It is the reason traer's particle demos render smoothly under continuous zoom.

The Bézier constant is the other reason this module exists. Approximating a quarter circle with a cubic needs a control-point offset of c·r, and the number everyone quotes is 0.55228475 — 4·(√2−1)/3, derived by making the curve pass exactly through the quarter point. That choice leaves the maximum radial error where it is largest. This module uses Spencer Mortensen's 0.5519150244935105707435627, which minimises the maximum error instead, and is the better constant for a circle you will scale.

Where it sits

Tier 0 of the stack — mvu → theme → components → effects → patterns → markdown — a leaf whose only dependency is Gio. The organization page has the full tier table.

It sits alongside backdrop and gradient, the other two drawing leaves. Nothing inside the design system imports it — components draws its own shapes — and its consumers are demos: mvu/example/circles, and all four of traer's gio particle programs.

go get github.com/vibrantgio/circle

Every module in the organization is on gioui.org v0.10.2 and Go 1.25.1.

Packages

One package, at the module root.

Symbol
CirclePath(ops, p, r) The clip.PathSpec: four cubics around centre p with radius r, closed. Use it with clip.Outline — the path is a single closed contour and relies on non-zero winding.
FillCircle(ops, p, r, fill) CirclePath clipped and painted in one call. Takes color.Color and converts through color.NRGBAModel.
Circle(p, r, fill) FillCircle as a layout.Widget. Returns gtx.Constraints.Max — see Status.

Usage

The path is the interesting one, because it composes. This is traer/gio/arboretum, drawing the root node of a spring-laid-out tree: the particle's position and the current zoom are both floats, and neither is rounded on the way to the rasteriser.

particle := ps.Particles[0]
p := f32.Point{X: float32(particle.Position.X), Y: float32(particle.Position.Y)}
cstack := clip.Outline{Path: circle.CirclePath(ops, to(p), 3*nodesize*scale)}.Op().Push(ops)
paint.ColorOp{Color: DeepPurple800}.Add(ops)
paint.PaintOp{}.Add(ops)
cstack.Pop()

Pushing the clip yourself is what buys you the composition — anything painted between the push and the pop is masked by the circle, so the same call fills with a solid, a gradient or an image.

When you just want a disc, FillCircle is the whole thing. From mvu/example/circles, which grows a circle under the pointer while it is dragged:

for _, c := range circles {
	vcircle.FillCircle(gtx.Ops, c.Center, c.Radius, colornames.Yellow800)
}

c.Center there comes straight off pointer.Event.Position, a f32.Point — no rounding, and the circle tracks the cursor sub-pixel.

For coding assistants

Read the canonical guide before writing code against this module — the module inventory with current tags, the application skeleton, MVU and rx semantics, typography, and the pitfalls that are not guessable:

https://raw.githubusercontent.com/vibrantgio/workbench/master/llms.txt

AGENTS.md in this repository has the build and test commands.

Status

Honest about what does not work yet. Every count below is measured.

  • Circle, the widget, has no consumer anywhere in the organization. Every one of the five call sites uses CirclePath (three) or FillCircle (two); the layout.Widget form has never been called. It is also the one form with a design problem: it returns layout.Dimensions{Size: gtx.Constraints.Max} whatever the radius is, and its centre p is an absolute point in the widget's own coordinate space rather than a position within the space it was given. So in a flex it claims all the room and draws wherever it was told, which is not what a widget is for. Use FillCircle inside a widget you wrote.
  • Gio can already do this, less precisely. clip.Ellipse is an image.Rectangle and gives you an axis-aligned ellipse inscribed in it. For a static circle in a layout that is the simpler call and this module adds nothing; the float32 centre and radius are the whole reason to reach for CirclePath instead. Nothing in the repository says so, and there is no benchmark comparing them.
  • Circles only — no arcs, no ellipses, no strokes. The four cubics are hard-coded as a closed loop, so there is no way to draw three quarters of a circle, an ellipse with different radii, or an outline of a given width. A ring today is two filled circles, the inner one in the background colour.
  • Nothing in the design system uses it. components, effects, patterns, markdown and theme all draw their own shapes; the consumers are mvu/example/circles and the four traer/gio demos. No component in the stack renders a circle through this module, so its behaviour under the theme, at high DPI, and against the golden-image tests the rest of the stack has is entirely unexercised.
  • The Bézier claim is not tested. The constant is right and the comment explaining it is right, but nothing measures the resulting radial error, and go test ./... reports "no test files". The precision this module exists for is asserted, not pinned.
  • There is no LICENSE file. Eighteen of the organization's twenty repositories ship one — MIT for most, BSD, Apache or Unlicense for the ported libraries. This one and gradient are the two that do not, and no phase of the current plan adds them.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Circle

func Circle(p f32.Point, r float32, fill color.Color) layout.Widget

func CirclePath

func CirclePath(ops *op.Ops, p f32.Point, r float32) clip.PathSpec

CirclePath returns a clip.PathSpec for a circle constructed from Cubic Bezier segments. Note that originally the bezier circle const was 0.55228475 equal to 4*(sqrt(2)-1)/3 A better bezier circle const to use is 0.551915024494 according to the following article: http://spencermortensen.com/articles/bezier-circle/

func FillCircle

func FillCircle(ops *op.Ops, p f32.Point, r float32, fill color.Color)

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL