Paths API

Advanced path operations, Bézier curves, and vector graphics.

Basic Path Operations

Path Creation

// Start a new path
dc.NewPath()

// Move to a point without drawing
dc.MoveTo(x, y)

// Draw a line to a point
dc.LineTo(x, y)

// Close the current path
dc.ClosePath()

// Example: Triangle
dc.NewPath()
dc.MoveTo(100, 100)
dc.LineTo(200, 100)
dc.LineTo(150, 50)
dc.ClosePath()
dc.Fill()

Arcs and Circles

// Draw arc (clockwise)
dc.Arc(centerX, centerY, radius, startAngle, endAngle)

// Draw arc (counter-clockwise)
dc.ArcNegative(centerX, centerY, radius, startAngle, endAngle)

// Example: Pac-Man shape
dc.MoveTo(200, 200)
dc.Arc(200, 200, 80, math.Pi/6, -math.Pi/6)
dc.ClosePath()
dc.Fill()

Bézier Curves

Quadratic Bézier Curves

// Quadratic Bézier curve (one control point)
dc.QuadraticTo(controlX, controlY, endX, endY)

// Example: Smooth curve
dc.MoveTo(100, 300)
dc.QuadraticTo(200, 100, 300, 300)
dc.Stroke()

Cubic Bézier Curves

// Cubic Bézier curve (two control points)
dc.CubicTo(control1X, control1Y, control2X, control2Y, endX, endY)

// Example: S-curve
dc.MoveTo(100, 400)
dc.CubicTo(200, 300, 300, 500, 400, 400)
dc.Stroke()

Smooth Curves

// Create smooth curves through points
points := []Point{
    {100, 200}, {200, 100}, {300, 250}, {400, 150}, {500, 200},
}

dc.MoveTo(points[0].X, points[0].Y)
for i := 1; i < len(points)-2; i++ {
    // Calculate control points for smooth curve
    cp1x := points[i].X + (points[i+1].X - points[i-1].X) / 6
    cp1y := points[i].Y + (points[i+1].Y - points[i-1].Y) / 6
    cp2x := points[i+1].X - (points[i+2].X - points[i].X) / 6
    cp2y := points[i+1].Y - (points[i+2].Y - points[i].Y) / 6
    
    dc.CubicTo(cp1x, cp1y, cp2x, cp2y, points[i+1].X, points[i+1].Y)
}
dc.Stroke()

Path2D Support

Creating Path2D Objects

// Create a new Path2D object
path := advancegg.NewPath2D()

// Build the path
path.MoveTo(100, 100)
path.LineTo(200, 100)
path.LineTo(200, 200)
path.LineTo(100, 200)
path.ClosePath()

// Use the path with context
dc.DrawPath(path)
dc.Fill()

Path Operations

// Get path bounds
bounds := path.GetBounds()
fmt.Printf("Bounds: %v\n", bounds)

// Check if point is inside path
contains := path.Contains(150, 150)
fmt.Printf("Contains point: %v\n", contains)

// Get path length
length := path.GetLength()
fmt.Printf("Path length: %f\n", length)

// Sample point at distance along path
point := path.GetPointAtDistance(length * 0.5) // Midpoint

Path Transformations

// Transform path
matrix := advancegg.Identity()
matrix = matrix.Scale(2, 2)
matrix = matrix.Rotate(math.Pi / 4)
matrix = matrix.Translate(100, 100)

path.Transform(matrix)

// Or use individual transformations
path.Scale(2, 2)
path.Rotate(math.Pi / 4)
path.Translate(100, 100)

Path Combining

// Combine paths
path1 := advancegg.NewPath2D()
path1.MoveTo(100, 100)
path1.LineTo(200, 200)

path2 := advancegg.NewPath2D()
path2.MoveTo(300, 100)
path2.LineTo(400, 200)

// Add path2 to path1
path1.AddPath(path2)

// Clone a path
clonedPath := path1.Clone()

Advanced Path Features

Path Simplification

// Simplify path (reduce points while maintaining shape)
simplified := path.Simplify(tolerance)

// Smooth path (reduce sharp corners)
smoothed := path.Smooth(iterations)

Path Offsetting

// Create offset path (parallel path at distance)
offsetPath := path.Offset(distance)

// Inward offset (negative distance)
insetPath := path.Offset(-10)

Path Boolean Operations

// Union of two paths
union := path1.Union(path2)

// Intersection of two paths
intersection := path1.Intersection(path2)

// Difference (subtract path2 from path1)
difference := path1.Difference(path2)

// Exclusive or
xor := path1.XOR(path2)

Path Stroking

// Convert stroke to filled path
strokePath := path.Stroke(width, lineCap, lineJoin)

// Stroke with dash pattern
dashedStroke := path.StrokeDashed(width, dashPattern, lineCap, lineJoin)

SVG Path Support

SVG Path Strings

// Parse SVG path string
svgPath := "M 100 100 L 200 100 L 200 200 L 100 200 Z"
path := advancegg.ParseSVGPath(svgPath)

// Convert path to SVG string
svgString := path.ToSVG()

// Complex SVG path with curves
complexPath := "M 100 200 C 100 100 250 100 250 200 S 400 300 400 200"
path2 := advancegg.ParseSVGPath(complexPath)

SVG Path Commands

Command Description Parameters
M x y Move to x, y coordinates
L x y Line to x, y coordinates
H x Horizontal line x coordinate
V y Vertical line y coordinate
C x1 y1 x2 y2 x y Cubic Bézier control points and end point
Q x1 y1 x y Quadratic Bézier control point and end point
A rx ry rotation large-arc sweep x y Arc radii, rotation, flags, end point
Z Close path none

Complete Examples

Complex Path Drawing

package main

import (
    "math"
    "github.com/GrandpaEJ/advancegg"
)

func main() {
    dc := advancegg.NewContext(800, 600)
    
    // Background
    dc.SetRGB(0.95, 0.95, 0.98)
    dc.Clear()
    
    // Draw a flower using paths
    drawFlower(dc, 400, 300, 100)
    
    // Draw a spiral
    drawSpiral(dc, 200, 150, 80, 3)
    
    // Draw a heart shape
    drawHeart(dc, 600, 450, 60)
    
    dc.SavePNG("complex-paths.png")
}

func drawFlower(dc *advancegg.Context, centerX, centerY, size float64) {
    dc.SetRGB(1, 0.2, 0.4)
    
    // Draw 8 petals
    for i := 0; i < 8; i++ {
        angle := float64(i) * math.Pi / 4
        
        dc.Push()
        dc.Translate(centerX, centerY)
        dc.Rotate(angle)
        
        // Petal shape using Bézier curves
        dc.MoveTo(0, 0)
        dc.CubicTo(0, -size*0.3, size*0.3, -size*0.8, 0, -size)
        dc.CubicTo(-size*0.3, -size*0.8, 0, -size*0.3, 0, 0)
        dc.Fill()
        
        dc.Pop()
    }
    
    // Center
    dc.SetRGB(1, 1, 0.2)
    dc.DrawCircle(centerX, centerY, size*0.2)
    dc.Fill()
}

func drawSpiral(dc *advancegg.Context, centerX, centerY, maxRadius float64, turns int) {
    dc.SetRGB(0.2, 0.4, 0.8)
    dc.SetLineWidth(3)
    
    steps := turns * 100
    dc.MoveTo(centerX, centerY)
    
    for i := 1; i <= steps; i++ {
        t := float64(i) / float64(steps)
        angle := t * float64(turns) * 2 * math.Pi
        radius := t * maxRadius
        
        x := centerX + radius*math.Cos(angle)
        y := centerY + radius*math.Sin(angle)
        dc.LineTo(x, y)
    }
    
    dc.Stroke()
}

func drawHeart(dc *advancegg.Context, centerX, centerY, size float64) {
    dc.SetRGB(0.8, 0.1, 0.2)
    
    // Heart shape using two circles and a triangle
    dc.MoveTo(centerX, centerY+size*0.3)
    
    // Left curve
    dc.CubicTo(centerX-size*0.8, centerY-size*0.3,
               centerX-size*0.8, centerY-size*0.8,
               centerX, centerY-size*0.2)
    
    // Right curve
    dc.CubicTo(centerX+size*0.8, centerY-size*0.8,
               centerX+size*0.8, centerY-size*0.3,
               centerX, centerY+size*0.3)
    
    dc.Fill()
}