render/scene.go

Functions Structs

Functions

func Circle

Circle adds a circle to the current path using cubic Bezier approximation.

func (s *Scene) Circle(cx, cy, r float64) {
	// Cubic Bezier circle approximation using kappa = 4 * (sqrt(2) - 1) / 3
	const kappa = 0.5522847498307936

	k := r * kappa

	s.MoveTo(cx+r, cy)
	s.CubicTo(cx+r, cy+k, cx+k, cy+r, cx, cy+r)
	s.CubicTo(cx-k, cy+r, cx-r, cy+k, cx-r, cy)
	s.CubicTo(cx-r, cy-k, cx-k, cy-r, cx, cy-r)
	s.CubicTo(cx+k, cy-r, cx+r, cy-k, cx+r, cy)
}

func Clear

Clear adds a clear operation to fill the entire target.

func (s *Scene) Clear(c color.Color) {
	s.commands = append(s.commands, drawCommand{
		op:	opClear,
		color:	c,
	})
}

func ClearDirty

ClearDirty resets the dirty state after rendering.

This should be called after each render pass.

func (s *Scene) ClearDirty() {
	s.dirtyRects = s.dirtyRects[:0]
	s.fullRedraw = false
}

func ClosePath

ClosePath closes the current subpath.

func (s *Scene) ClosePath() {
	s.currentPath.verbs = append(s.currentPath.verbs, raster.Close)
}

func CommandCount

CommandCount returns the number of drawing commands in the scene.

func (s *Scene) CommandCount() int {
	return len(s.commands)
}

func CubicTo

CubicTo draws a cubic Bezier curve.

func (s *Scene) CubicTo(c1x, c1y, c2x, c2y, x, y float64) {
	s.currentPath.verbs = append(s.currentPath.verbs, raster.CubicTo)
	s.currentPath.points = append(s.currentPath.points,
		float32(c1x), float32(c1y),
		float32(c2x), float32(c2y),
		float32(x), float32(y))
}

func DirtyRects

DirtyRects returns the accumulated dirty rectangles.

Returns nil if the scene needs a full redraw (check NeedsFullRedraw first).

The returned slice should not be modified by the caller.

func (s *Scene) DirtyRects() []DirtyRect {
	if s.fullRedraw {
		return nil
	}
	return s.dirtyRects
}

func Fill

Fill fills the current path and clears it.

func (s *Scene) Fill() {
	if len(s.currentPath.verbs) == 0 {
		return
	}

	// Snapshot the current path
	path := s.snapshotPath()

	s.commands = append(s.commands, drawCommand{
		op:		opFill,
		path:		path,
		color:		s.currentFillColor,
		fillRule:	s.currentFillRule,
	})

	// Clear the current path
	s.currentPath = pathBuilder{}
}

func HasDirtyRegions

HasDirtyRegions returns true if there are any dirty regions to redraw.

This includes both individual rects and full redraw state.

func (s *Scene) HasDirtyRegions() bool {
	return s.fullRedraw || len(s.dirtyRects) > 0
}

func Invalidate

Invalidate marks a rectangular region as needing redraw.

This is used for damage tracking to enable efficient partial redraws.

If the accumulated dirty rects exceed maxDirtyRects, the scene switches

to full redraw mode for efficiency.

func (s *Scene) Invalidate(rect DirtyRect) {
	if s.fullRedraw {
		return	// Already in full redraw mode
	}

	// Validate rect has positive dimensions
	if rect.Width <= 0 || rect.Height <= 0 {
		return
	}

	s.dirtyRects = append(s.dirtyRects, rect)

	// If too many rects, switch to full redraw
	if len(s.dirtyRects) > maxDirtyRects {
		s.fullRedraw = true
		s.dirtyRects = s.dirtyRects[:0]	// Clear rects since we're doing full redraw
	}
}

func InvalidateAll

InvalidateAll marks the entire scene as needing redraw.

This forces a full redraw on the next render pass.

func (s *Scene) InvalidateAll() {
	s.fullRedraw = true
	s.dirtyRects = s.dirtyRects[:0]	// Clear individual rects
}

func IsEmpty

IsEmpty returns true if the scene has no commands.

func (s *Scene) IsEmpty() bool {
	return len(s.commands) == 0
}

func IsEmpty

pathBuilder implements raster.PathLike for use with EdgeBuilder.

func (p *pathBuilder) IsEmpty() bool {
	return len(p.verbs) == 0
}

func LineTo

LineTo draws a line from the current point to the given point.

func (s *Scene) LineTo(x, y float64) {
	s.currentPath.verbs = append(s.currentPath.verbs, raster.LineTo)
	s.currentPath.points = append(s.currentPath.points, float32(x), float32(y))
}

func MoveTo

MoveTo starts a new subpath at the given point.

func (s *Scene) MoveTo(x, y float64) {
	s.currentPath.verbs = append(s.currentPath.verbs, raster.MoveTo)
	s.currentPath.points = append(s.currentPath.points, float32(x), float32(y))
}

func NeedsFullRedraw

NeedsFullRedraw returns true if the scene should be fully redrawn.

This is true when InvalidateAll was called or when too many dirty rects

have accumulated (more than maxDirtyRects).

func (s *Scene) NeedsFullRedraw() bool {
	return s.fullRedraw
}

func NewScene

NewScene creates a new empty Scene.

func NewScene() *Scene {
	return &Scene{
		commands:		make([]drawCommand, 0, 16),
		currentFillColor:	color.Black,
		currentStrokeColor:	color.Black,
		currentStrokeWidth:	1.0,
		currentFillRule:	raster.FillRuleNonZero,
	}
}

func Points

func (p *pathBuilder) Points() []float32 {
	return p.points
}

func QuadTo

QuadTo draws a quadratic Bezier curve.

func (s *Scene) QuadTo(cx, cy, x, y float64) {
	s.currentPath.verbs = append(s.currentPath.verbs, raster.QuadTo)
	s.currentPath.points = append(s.currentPath.points,
		float32(cx), float32(cy),
		float32(x), float32(y))
}

func Rectangle

Rectangle adds a rectangle to the current path.

func (s *Scene) Rectangle(x, y, width, height float64) {
	s.MoveTo(x, y)
	s.LineTo(x+width, y)
	s.LineTo(x+width, y+height)
	s.LineTo(x, y+height)
	s.ClosePath()
}

func Reset

Reset clears the scene for reuse.

func (s *Scene) Reset() {
	s.commands = s.commands[:0]
	s.currentPath = pathBuilder{}
	s.currentFillColor = color.Black
	s.currentStrokeColor = color.Black
	s.currentStrokeWidth = 1.0
	s.currentFillRule = raster.FillRuleNonZero
	s.ClearDirty()
}

func SetFillColor

SetFillColor sets the color for subsequent fill operations.

func (s *Scene) SetFillColor(c color.Color) {
	s.currentFillColor = c
}

func SetFillRule

SetFillRule sets the fill rule for subsequent fill operations.

func (s *Scene) SetFillRule(rule raster.FillRule) {
	s.currentFillRule = rule
}

func SetStrokeColor

SetStrokeColor sets the color for subsequent stroke operations.

func (s *Scene) SetStrokeColor(c color.Color) {
	s.currentStrokeColor = c
}

func SetStrokeWidth

SetStrokeWidth sets the width for subsequent stroke operations.

func (s *Scene) SetStrokeWidth(width float64) {
	s.currentStrokeWidth = width
}

func Stroke

Stroke strokes the current path and clears it.

func (s *Scene) Stroke() {
	if len(s.currentPath.verbs) == 0 {
		return
	}

	// Snapshot the current path
	path := s.snapshotPath()

	s.commands = append(s.commands, drawCommand{
		op:	opStroke,
		path:	path,
		color:	s.currentStrokeColor,
		width:	s.currentStrokeWidth,
	})

	// Clear the current path
	s.currentPath = pathBuilder{}
}

func Verbs

func (p *pathBuilder) Verbs() []raster.PathVerb {
	return p.verbs
}

Structs

type DirtyRect struct

DirtyRect represents a region that needs redraw.

Used for damage tracking to enable efficient partial redraws.

type DirtyRect struct {
	X, Y, Width, Height float64
}

type Scene struct

Scene represents a retained-mode drawing tree.

 

Unlike immediate-mode drawing (Context.DrawCircle, etc.), a Scene captures

drawing commands that can be:

- Rendered multiple times without rebuilding

- Partially invalidated for efficient UI updates

- Optimized by the renderer for batching

 

Scene is designed to work with the Renderer interface, allowing both

CPU and GPU rendering backends to process the same scene data.

 

Example:

 

scene := render.NewScene()

scene.SetFillColor(color.RGBA{255, 0, 0, 255})

scene.MoveTo(100, 50)

scene.LineTo(150, 150)

scene.LineTo(50, 150)

scene.ClosePath()

scene.Fill()

 

// Render to any target

renderer.Render(target1, scene)

renderer.Render(target2, scene)

type Scene struct {
	// commands stores the drawing command stream.
	commands	[]drawCommand

	// currentPath builds the current path being constructed.
	currentPath	pathBuilder

	// currentFillColor is the color for fill operations.
	currentFillColor	color.Color

	// currentStrokeColor is the color for stroke operations.
	currentStrokeColor	color.Color

	// currentStrokeWidth is the width for stroke operations.
	currentStrokeWidth	float64

	// currentFillRule is the fill rule for fill operations.
	currentFillRule	raster.FillRule

	// Damage tracking for efficient partial redraws
	dirtyRects	[]DirtyRect
	fullRedraw	bool
}