graphics2d

package module
v0.0.0-...-08f6348 Latest Latest
Warning

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

Go to latest
Published: Dec 11, 2025 License: Apache-2.0 Imports: 14 Imported by: 3

README

Yet Another 2D Graphics Package For Go

Go Reference Go Report Card Ask DeepWiki

Splash image created with graphics2d

1. Introduction

Graphics2D is a vector based drawing package that leverages golang.org/x/image/vector to render shapes into an image.

The vector package extends image/draw to create a mask that a source image is rendered through into the destination image. Graphics2D follows this convention.

All the pictures and diagrams in this README were created with this package. Clicking on one will take you to the code that created it.

Paths

The primary type in the package is the Path. A path represents a single movement of a pen, from pen down to pen up. Paths are composed of steps with some number of points in them. The number of points determines the order of the Bezier curve generated. The path methods LineTo and CurveTo are just synonyms for AddStep. Once created, a path can be left as is (open), or closed Close. A closed path can no longer be extended and a line is automatically created from the first point in the path to its last.

Shapes

Shapes allow multiple paths to be combined to produce more complex drawings. For example, the figure 8 is composed of three paths; its external outline, and the two holes in it.

Rendering

Shapes are rendered to an image using a source filler image and the mask generated from the shape by RenderShape. If the filler is all one color, then RenderColoredShape can be used.

2. Basic Shapes

Fig1 image created with graphics2d

These shapes were created using Line, RegularPolygon, Circle, and Oval. These are just some of the constructors available for the Path type.

3. Bezier Curves

Fig2 image created with graphics2d

Bezier curves are polynomial curves. Most vector packages support first, second and third order curves; lines, quadratic and cubic curves respectively. The path AddStep method has no upper limit on the number of control points that can be specified so higher order curves can be created. The last example on the right is a quartic curve.

4. Arcs And ArcStyles

Fig3 image created with graphics3d

Various arc path constructors are available and typically take a start angle and a sweep angle. The sign of the sweep angle determines whether it goes clockwise or counter-clockwise. The arcs are approximated from cubic Bezier curves. Arcs must have an ArcStyle associated with them, one of ArcOpen, ArcPie or ArcChord as shown above.

5. Reentrant Shapes

Fig4 image created with graphics2d

Examples of different regular reentrant polygons made with ReentrantPolygon. The degree of reentrancy is controlled by a value in the range [0,1) where 0 represents a regular polygon and 1, a polygon with no area. A value of 0.5 was used for these polygons.

6. Using Path Processors

Fig5 image created with graphics2d

Paths have a Process method that allows a PathProcessor to be applied to them which will generate one or more new paths. This example shows what the effect of the CurveProc looks like applied to both a closed and an open path made up of multiple line segments. CurveProc requires a CurveStyle to be specified too, one of Quad, Bezier or CatmullRom (L to R in the example).

Fig6 image created with graphics2d

Another path processor that can be used to create curved paths is RoundedProc. This example uses increasing curve radii from L to R.

Shapes have a similar function ProcessPaths which runs a path processor over all of the paths in a shape.

7. Using Fonts

Fig7 image created with graphics2d

The golang.org/x/image/font/sfnt package can read in and parse TrueType and OpenType fonts. Strings can be turned into shapes using a parsed font and StringToShape. The shape will be in font units. ScaleAndInset can be used to fit the result to the desired location. This example also uses path processors to show the control points for the font curves.

8. Dashing With Path Processors

Fig8 image created with graphics2d

These were created from the paths in the earlier example using the DashProc path processor. The dash patterns are {4, 2}, {8, 2, 2, 2} and {10, 4}. The bottom row also uses another path processor, CapsProc, on the paths from running DashProc, to add the arrow heads.

9. Tracing With Path Processors

Fig9 image created with graphics2d

The TraceProc path processor traces a path, either to the left or the right of the original, depending on the offset value supplied. How path steps are joined is specified by a join function that the processor calls. The following functions are shown: JoinButt, JoinRound, and JoinMiter.

Fig10 image created with graphics2d

A variable width trace path processor, VWTraceProc, uses the distance along the path, t (range [0,1]), to figure the current offset of the trace. This example uses (1-t) * offset as the offset function. The joins are all miter joins, implicitly.

10. Outlining With Stroke Path Processor

Fig11 image created with graphics2d

The StrokeProc is used to convert open paths to closed ones, since only closed paths can be filled by the renderer.

A stroke is comprised of left and right trace path processors, and functions that define the start and end caps of the path.

This example shows the CapButt, CapSquare, CapRoundedSquare, CapInvRound and CapRound, CapInvOval and CapOval, and CapInvPoint and CapPoint, from left to right.

End caps are only used when the path is open. When a stroke processor is applied to a closed path, two closed paths are created forming an outline of the original.

Pens

A Pen is a convenient abstraction that ties together a filler image, a path processor, and a transform. The path processor creates the closed paths needed for rendering, so that the user doesn't have to write the mechanics of outlining for every shape. The transform is applied to the shape prior to the path processor so a pen with width 1, for example, will draw paths with width 1 in the image and not the shape coordinates. A predefined collection of colored pens is available here. Dashed pens can be constructed by concatenating DashProc with the path processor. See the pen example.

Convenience functions that take a pen argument are:

11. Gradients

Fig12 image created with graphics2d

Gradients aren't strictly part of the graphics2d package since what's used to fill a shape is just an image. Gradient images can be created using the texture package. This package supports linear, radial, elliptical and conic gradients with convenience functions for gray scale and RGBA images. The gradients can be set to repeat and to mirror. The RGBA gradients are just Colorizer wrappers around their Gray16 counterparts, allowing for multiple color stops in the gradient.

List of gradient image functions:

Documentation

Overview

Package graphics2d contains types and functions for 2D graphics rendering.

A comprehensive overview is presented in the README, cick on the expand link above to read it.

Example (Fig01)

Generates a series of outlined regular shapes.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	paths := []*g2d.Path{
		g2d.Line([]float64{20, 20}, []float64{130, 130}),
		g2d.RegularPolygon(3, []float64{225, 75}, 110, g2d.HalfPi),
		g2d.RegularPolygon(4, []float64{375, 75}, 110, 0),
		g2d.RegularPolygon(5, []float64{525, 75}, 75, 0),
		g2d.Circle([]float64{675, 75}, 55),
		g2d.Ellipse([]float64{825, 75}, 70, 35, g2d.HalfPi/2)}
	pen := g2d.NewPen(color.Black, 3)

	img := image.NewRGBA(900, 150, color.White)
	for _, path := range paths {
		g2d.DrawPath(img, path, pen)
	}
	image.SaveImage(img, "fig1")

	fmt.Printf("See fig1.png")
}
Output:

See fig1.png
Example (Fig02)

Generates a series of Bezier curves of increasing order.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	// Create curves of order 2, 3 and 4
	quad := g2d.NewPath([]float64{175, 25})
	quad.AddStep([]float64{25, 25}, []float64{25, 175})
	cube := g2d.NewPath([]float64{375, 25})
	cube.AddStep([]float64{225, 25}, []float64{375, 175}, []float64{225, 175})
	quar := g2d.NewPath([]float64{575, 25})
	quar.AddStep([]float64{500, 25}, []float64{575, 175}, []float64{425, 100}, []float64{425, 175})

	// Draw curves
	img := image.NewRGBA(600, 200, color.White)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawPath(img, quad, pen)
	g2d.DrawPath(img, cube, pen)
	g2d.DrawPath(img, quar, pen)

	// Draw controls
	box := g2d.NewShape(g2d.RegularPolygon(4, []float64{0, 0}, 4, 0))
	cproc := g2d.CapsProc{box, box, box, false}
	paths := []*g2d.Path{quad, cube, quar}
	for _, path := range paths {
		// Control lines
		cpath := path.Process(g2d.StepsToLinesProc{true})[0]
		g2d.DrawPath(img, cpath, g2d.RedPen)

		// Control points
		shape := g2d.NewShape(cpath.Process(cproc)...)
		g2d.RenderColoredShape(img, shape, color.Black)
	}

	image.SaveImage(img, "fig2")
	fmt.Printf("See fig2.png")
}
Output:

See fig2.png
Example (Fig03)

Generates arcs with different ArcStyle

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	// Arcs
	paths := []*g2d.Path{
		// Top row
		g2d.Arc([]float64{100, 100}, 85, g2d.Pi*3/4, g2d.HalfPi, g2d.ArcOpen),
		g2d.Arc([]float64{300, 100}, 85, g2d.Pi*3/4, g2d.HalfPi, g2d.ArcPie),
		g2d.Arc([]float64{500, 100}, 85, g2d.Pi*3/4, g2d.HalfPi, g2d.ArcChord),
		// Bottom row
		g2d.Arc([]float64{100, 300}, 85, g2d.Pi*3/4, -3*g2d.HalfPi, g2d.ArcOpen),
		g2d.Arc([]float64{300, 300}, 85, g2d.Pi*3/4, -3*g2d.HalfPi, g2d.ArcPie),
		g2d.Arc([]float64{500, 300}, 85, g2d.Pi*3/4, -3*g2d.HalfPi, g2d.ArcChord),
	}
	ashape := g2d.NewShape(paths...)
	fashape := g2d.NewShape(paths[1], paths[2], paths[4], paths[5])

	// Circles
	x, y := 100.0, 100.0
	dx, dy := 200.0, 200.0
	circ := g2d.Circle([]float64{x, y}, 85)
	cshape := g2d.NewShape(circ)
	cshape.AddPaths(circ.Process(g2d.Translate(dx, 0))[0])
	cshape.AddPaths(circ.Process(g2d.Translate(2*dx, 0))[0])
	cshape.AddPaths(circ.Process(g2d.Translate(0, dy))[0])
	cshape.AddPaths(circ.Process(g2d.Translate(dx, dy))[0])
	cshape.AddPaths(circ.Process(g2d.Translate(2*dx, dy))[0])

	img := image.NewRGBA(600, 400, color.White)
	g2d.DrawShape(img, cshape, g2d.RedPen)
	g2d.RenderColoredShape(img, fashape, color.Green)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, ashape, pen)

	image.SaveImage(img, "fig3")
	fmt.Printf("See fig3.png")
}
Output:

See fig3.png
Example (Fig04)

Generates a series of reentrant shapes.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	shape := g2d.NewShape(g2d.ReentrantPolygon([]float64{100, 100}, 90, 3, 0.5, 0))
	shape.AddPaths(g2d.ReentrantPolygon([]float64{300, 100}, 90, 4, 0.5, 0))
	shape.AddPaths(g2d.ReentrantPolygon([]float64{500, 100}, 90, 5, 0.5, 0))
	shape.AddPaths(g2d.ReentrantPolygon([]float64{700, 100}, 90, 6, 0.5, 0))

	img := image.NewRGBA(800, 200, color.White)
	g2d.RenderColoredShape(img, shape, color.Green)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, shape, pen)
	image.SaveImage(img, "fig4")
	fmt.Printf("See fig4.png")
}
Output:

See fig4.png
Example (Fig05)

Displays the different CurveProc path processor styles.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	// A closed and open path
	closed := g2d.ReentrantPolygon([]float64{100, 100}, 90, 5, 0.5, 0)
	parts := closed.Parts()
	open := g2d.PartsToPath(parts[0 : len(parts)-2]...).Process(g2d.Translate(0, 200))[0]
	pshape := g2d.NewShape(closed, open)

	// Constructions
	c1 := closed.Process(g2d.Translate(200, 0))[0]
	o1 := open.Process(g2d.Translate(200, 0))[0]
	c2 := closed.Process(g2d.Translate(400, 0))[0]
	o2 := open.Process(g2d.Translate(400, 0))[0]
	c3 := closed.Process(g2d.Translate(600, 0))[0]
	o3 := open.Process(g2d.Translate(600, 0))[0]
	cshape := g2d.NewShape(c1, o1, c2, o2, c3, o3)

	// CurveProcs for each curve style
	qcproc := g2d.CurveProc{Scale: 0.5, Style: g2d.Quad}
	bcproc := g2d.CurveProc{Scale: 0.5, Style: g2d.Bezier}
	ccproc := g2d.CurveProc{Scale: 0.3, Style: g2d.CatmullRom}

	// Run the path processors
	pshape.AddPaths(c1.Process(qcproc)...)
	pshape.AddPaths(o1.Process(qcproc)...)
	pshape.AddPaths(c2.Process(bcproc)...)
	pshape.AddPaths(o2.Process(bcproc)...)
	pshape.AddPaths(c3.Process(ccproc)...)
	pshape.AddPaths(o3.Process(ccproc)...)

	img := image.NewRGBA(800, 400, color.White)
	g2d.DrawShape(img, cshape, g2d.RedPen)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, pshape, pen)
	image.SaveImage(img, "fig5")
	fmt.Printf("See fig5.png")
}
Output:

See fig5.png
Example (Fig06)

Displays the RoundedProc path processor with different radii.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	// A closed and open path
	closed := g2d.ReentrantPolygon([]float64{100, 100}, 90, 5, 0.5, 0)
	parts := closed.Parts()
	open := g2d.PartsToPath(parts[0 : len(parts)-2]...).Process(g2d.Translate(0, 200))[0]
	pshape := g2d.NewShape(closed, open)

	// Constructions
	c1 := closed.Process(g2d.Translate(200, 0))[0]
	o1 := open.Process(g2d.Translate(200, 0))[0]
	c2 := closed.Process(g2d.Translate(400, 0))[0]
	o2 := open.Process(g2d.Translate(400, 0))[0]
	c3 := closed.Process(g2d.Translate(600, 0))[0]
	o3 := open.Process(g2d.Translate(600, 0))[0]
	cshape := g2d.NewShape(c1, o1, c2, o2, c3, o3)

	// CurveProcs for each curve style
	r1proc := g2d.RoundedProc{5}
	r2proc := g2d.RoundedProc{10}
	r3proc := g2d.RoundedProc{50}

	// Run the path processors
	pshape.AddPaths(c1.Process(r1proc)...)
	pshape.AddPaths(o1.Process(r1proc)...)
	pshape.AddPaths(c2.Process(r2proc)...)
	pshape.AddPaths(o2.Process(r2proc)...)
	pshape.AddPaths(c3.Process(r3proc)...)
	pshape.AddPaths(o3.Process(r3proc)...)

	img := image.NewRGBA(800, 400, color.White)
	g2d.DrawShape(img, cshape, g2d.RedPen)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, pshape, pen)
	image.SaveImage(img, "fig6")
	fmt.Printf("See fig6.png")
}
Output:

See fig6.png
Example (Fig07)

Creates a string from a font file and displays the control points too.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
	"golang.org/x/image/font/gofont/goitalic"
	"golang.org/x/image/font/sfnt"
)

func main() {
	// Load font and create shapes
	ttf, err := sfnt.Parse(goitalic.TTF)
	if err != nil {
		panic(err)
	}
	str := "G2D"
	shape, _, err := g2d.StringToShape(ttf, str)
	if err != nil {
		panic(err)
	}

	// Figure bounding box and scaling transform
	bb := shape.BoundingBox()
	xfm := g2d.ScaleAndInset(500, 300, 20, 20, false, bb)
	shape = shape.ProcessPaths(xfm)

	// Render string
	img := image.NewRGBA(500, 300, color.White)
	g2d.RenderColoredShape(img, shape, color.Green)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, shape, pen)

	// Render construction
	box := g2d.NewShape(g2d.RegularPolygon(4, []float64{0, 0}, 4, 0))
	cproc := g2d.CapsProc{box, box, box, false}
	for _, path := range shape.Paths() {
		// Control lines
		cpath := path.Process(g2d.StepsToLinesProc{true})[0]
		g2d.DrawPath(img, cpath, g2d.RedPen)

		// Control points
		shape := g2d.NewShape(cpath.Process(cproc)...)
		g2d.RenderColoredShape(img, shape, color.Black)
	}
	image.SaveImage(img, "fig7")

	fmt.Printf("See fig7.png")
}
Output:

See fig7.png
Example (Fig08)

Generates a series of regular shapes with dashed outlines.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	paths := []*g2d.Path{
		g2d.Line([]float64{20, 20}, []float64{130, 130}),
		g2d.RegularPolygon(3, []float64{225, 75}, 110, g2d.HalfPi),
		g2d.RegularPolygon(4, []float64{375, 75}, 110, 0),
		g2d.RegularPolygon(5, []float64{525, 75}, 75, 0),
		g2d.Circle([]float64{675, 75}, 55),
		g2d.Ellipse([]float64{825, 75}, 70, 35, g2d.HalfPi/2)}

	// Path processors
	proc1 := g2d.NewDashProc([]float64{4, 2}, 0)
	proc2 := g2d.NewDashProc([]float64{8, 2, 2, 2}, 0)
	proc3 := g2d.NewDashProc([]float64{10, 4}, 0)
	head := g2d.NewShape(g2d.PolyLine([]float64{-2, 2}, []float64{0, 0}, []float64{-2, -2}))
	cproc := g2d.CapsProc{nil, head, nil, true}

	shape := &g2d.Shape{}
	xfm := g2d.Translate(0, 150)
	for _, path := range paths {
		shape.AddPaths(path.Process(proc1)...)
		path = path.Process(xfm)[0]
		shape.AddPaths(path.Process(proc2)...)
		path = path.Process(xfm)[0]
		lshape := g2d.NewShape(path.Process(proc3)...)
		shape.AddShapes(lshape)
		// Add arrow heads to dashes from proc3
		shape.AddShapes(lshape.ProcessPaths(cproc))
	}

	img := image.NewRGBA(900, 450, color.White)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, shape, pen)
	image.SaveImage(img, "fig8")

	fmt.Printf("See fig8.png")
}
Output:

See fig8.png
Example (Fig09)

Generates a series of path traces using different join functions.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	path := g2d.PolyLine(
		[]float64{20, 50},
		[]float64{120, 150},
		[]float64{220, 50},
		[]float64{320, 150},
		[]float64{420, 50},
		[]float64{520, 150})

	proc1 := g2d.NewTraceProc(20)
	proc2 := g2d.NewTraceProc(20)
	proc2.JoinFunc = g2d.JoinRound
	proc3 := g2d.NewTraceProc(20)
	proc3.JoinFunc = g2d.NewMiterJoin().JoinMiter

	img := image.NewRGBA(560, 600, color.White)

	g2d.DrawPath(img, path, g2d.RedPen)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawPath(img, path.Process(proc1)[0], pen)

	path = path.Process(g2d.Translate(0, 200))[0]
	g2d.DrawPath(img, path, g2d.RedPen)
	g2d.DrawPath(img, path.Process(proc2)[0], pen)

	path = path.Process(g2d.Translate(0, 200))[0]
	g2d.DrawPath(img, path, g2d.RedPen)
	g2d.DrawPath(img, path.Process(proc3)[0], pen)

	image.SaveImage(img, "fig9")
	fmt.Printf("See fig9.png")
}
Output:

See fig9.png
Example (Fig10)

Generates a variable width trace of a path.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	// Line, MPD it, round it - a wriggle
	path := g2d.Line([]float64{30, 150}, []float64{530, 150})
	path = path.Process(&g2d.MPDProc{.3, 3, 0.5, false})[0]
	path = path.Process(&g2d.RoundedProc{1000})[0]

	proc := &g2d.VWTraceProc{
		Width:   -20,
		Flatten: g2d.RenderFlatten,
	}
	proc.Func = func(t, w float64) float64 {
		return (1-t)*proc.Width + 1
	}

	img := image.NewRGBA(560, 300, color.White)

	g2d.DrawPath(img, path, g2d.RedPen)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawPath(img, path.Process(proc)[0], pen)

	image.SaveImage(img, "fig10")
	fmt.Printf("See fig10.png")
}
Output:

See fig10.png
Example (Fig11)

Demonstrates the variety of stroke end caps.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	img := image.NewRGBA(900, 300, color.White)

	// Butt
	path := g2d.Line([]float64{50, 75}, []float64{250, 75})
	proc := g2d.NewStrokeProc(40)
	shape := g2d.NewShape(path.Process(proc)...)
	g2d.RenderColoredShape(img, shape, color.Green)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, shape, pen)
	g2d.DrawPath(img, path, g2d.RedPen)

	// Square
	path = g2d.Line([]float64{350, 75}, []float64{550, 75})
	proc.CapStartFunc = g2d.CapSquare
	proc.CapEndFunc = g2d.CapSquare
	shape = g2d.NewShape(path.Process(proc)...)
	g2d.RenderColoredShape(img, shape, color.Green)
	g2d.DrawShape(img, shape, pen)
	g2d.DrawPath(img, path, g2d.RedPen)

	// Rounded Square
	path = g2d.Line([]float64{650, 75}, []float64{850, 75})
	rsc := g2d.RSCap{0.5}
	proc.CapStartFunc = rsc.CapRoundedSquare
	proc.CapEndFunc = rsc.CapRoundedSquare
	shape = g2d.NewShape(path.Process(proc)...)
	g2d.RenderColoredShape(img, shape, color.Green)
	g2d.DrawShape(img, shape, pen)
	g2d.DrawPath(img, path, g2d.RedPen)

	// Round
	path = g2d.Line([]float64{50, 225}, []float64{250, 225})
	proc.CapStartFunc = g2d.CapInvRound
	proc.CapEndFunc = g2d.CapRound
	shape = g2d.NewShape(path.Process(proc)...)
	g2d.RenderColoredShape(img, shape, color.Green)
	g2d.DrawShape(img, shape, pen)
	g2d.DrawPath(img, path, g2d.RedPen)

	// Oval
	path = g2d.Line([]float64{350, 225}, []float64{550, 225})
	oc := g2d.OvalCap{2, 0}
	proc.CapStartFunc = oc.CapInvOval
	proc.CapEndFunc = oc.CapOval
	shape = g2d.NewShape(path.Process(proc)...)
	g2d.RenderColoredShape(img, shape, color.Green)
	g2d.DrawShape(img, shape, pen)
	g2d.DrawPath(img, path, g2d.RedPen)

	// Point
	path = g2d.Line([]float64{650, 225}, []float64{850, 225})
	proc.CapStartFunc = g2d.CapInvPoint
	proc.CapEndFunc = g2d.CapPoint
	shape = g2d.NewShape(path.Process(proc)...)
	g2d.RenderColoredShape(img, shape, color.Green)
	g2d.DrawShape(img, shape, pen)
	g2d.DrawPath(img, path, g2d.RedPen)

	image.SaveImage(img, "fig11")
	fmt.Printf("See fig11.png")
}
Output:

See fig11.png
Example (Fig12)

Demonstrates the different gradient types: linear, radial, elliptical and conic.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
	"github.com/jphsd/texture"
)

func main() {
	width, height := 1200, 300

	shape1 := g2d.NewShape(g2d.Circle([]float64{150, 150}, 100))
	filler1 := texture.NewLinearRGBA(width, height,
		[]float64{50, 150}, []float64{250, 150},
		color.Green, color.Red,
		texture.NewNLLinear(),
		false, false)
	shape2 := g2d.NewShape(g2d.Circle([]float64{450, 150}, 100))
	filler2 := texture.NewRadialRGBA(width, height,
		[]float64{450, 150}, 100,
		color.Red, color.Green,
		texture.NewNLLinear(),
		false, false)
	shape3 := g2d.NewShape(g2d.Circle([]float64{750, 150}, 100))
	filler3 := texture.NewEllipticalRGBA(width, height,
		[]float64{750, 150},
		100, 50, g2d.Pi/4,
		color.Red, color.Green,
		texture.NewNLLinear(),
		true, false)
	shape4 := g2d.NewShape(g2d.Circle([]float64{1050, 150}, 100))
	filler4 := texture.NewConicRGBA(width, height,
		[]float64{1050, 150}, 0,
		color.Green, color.Red,
		texture.NewNLLinear())

	img := image.NewRGBA(width, height, color.White)
	pen := g2d.NewPen(color.Black, 3)
	g2d.RenderShape(img, shape1, filler1)
	g2d.DrawShape(img, shape1, pen)
	g2d.RenderShape(img, shape2, filler2)
	g2d.DrawShape(img, shape2, pen)
	g2d.RenderShape(img, shape3, filler3)
	g2d.DrawShape(img, shape3, pen)
	g2d.RenderShape(img, shape4, filler4)
	g2d.DrawShape(img, shape4, pen)
	image.SaveImage(img, "fig12")
	fmt.Printf("See fig12.png")
}
Output:

See fig12.png
Example (Splash)

Example_splash generates a series of background images using triangles, squares, pentagons, circles and stars, and then draws an outlined string over them.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
	"golang.org/x/image/font/gofont/gobold"
	"golang.org/x/image/font/sfnt"
	"image/draw"
	"math/rand"
)

// Example_splash generates a series of background images using triangles, squares, pentagons,
// circles and stars, and then draws an outlined string over them.
func main() {
	// Make background fillers
	bg := make([]*image.RGBA, 5)
	bg[0] = makeRegularBackground(300, 3, 10, 30)
	bg[1] = makeRegularBackground(200, 4, 10, 30)
	bg[2] = makeRegularBackground(200, 5, 10, 30)
	bg[3] = makeCircleBackground(200, 10, 30)
	bg[4] = makeStarBackground(200, 5, 10, 30)

	// Create image with backgrounds
	img := image.NewRGBA(1000, 200, color.Transparent)
	for i, _ := range bg {
		rect := image.Rect(i*200, 0, i*200+200, 200)
		draw.Draw(img, rect, bg[i], image.Point{}, draw.Src)
	}

	// Load font and create shapes
	ttf, err := sfnt.Parse(gobold.TTF)
	if err != nil {
		panic(err)
	}
	str := "Graphics2D"
	shape, _, err := g2d.StringToShape(ttf, str)
	if err != nil {
		panic(err)
	}

	// Figure bounding box and scaling transform
	bb := shape.BoundingBox()
	xfm := g2d.ScaleAndInset(1000, 200, 20, 20, false, bb)
	shape = shape.ProcessPaths(xfm)

	// Render string to image
	pen := g2d.NewPen(color.White, 8)
	g2d.RenderColoredShape(img, shape, color.Black)
	g2d.DrawShape(img, shape, pen)
	image.SaveImage(img, "splash")

	fmt.Printf("See splash.png")
}

func makeRegularBackground(n, s int, min, max float64) *image.RGBA {
	img := image.NewRGBA(200, 200, color.Black)

	dl := max - min
	for range n {
		c := []float64{rand.Float64() * 200, rand.Float64() * 200}
		l := min + rand.Float64()*dl
		col := color.HSL{rand.Float64(), 1, 0.5, 1}
		th := rand.Float64() * g2d.TwoPi
		shape := g2d.NewShape(g2d.RegularPolygon(s, c, l, th))
		g2d.RenderColoredShape(img, shape, col)
	}

	return img
}

func makeCircleBackground(n int, min, max float64) *image.RGBA {
	img := image.NewRGBA(200, 200, color.Black)

	dr := max - min
	for range n {
		c := []float64{rand.Float64() * 200, rand.Float64() * 200}
		r := min + rand.Float64()*dr
		col := color.HSL{rand.Float64(), 1, 0.5, 1}
		shape := g2d.NewShape(g2d.Circle(c, r))
		g2d.RenderColoredShape(img, shape, col)
	}

	return img
}

func makeStarBackground(n, s int, min, max float64) *image.RGBA {
	img := image.NewRGBA(200, 200, color.Black)

	dr := max - min
	for range n {
		c := []float64{rand.Float64() * 200, rand.Float64() * 200}
		r := min + rand.Float64()*dr
		col := color.HSL{rand.Float64(), 1, 0.5, 1}
		th := rand.Float64() * g2d.TwoPi
		shape := g2d.NewShape(g2d.ReentrantPolygon(c, r, s, 0.5, th))
		g2d.RenderColoredShape(img, shape, col)
	}

	return img
}
Output:

See splash.png

Index

Examples

Constants

View Source
const (
	Pi     = math.Pi
	TwoPi  = 2 * Pi
	HalfPi = Pi / 2
	Sqrt3  = 1.7320508075688772935274463415058723669428052538103806280558069794519330169088
)

Mathematical constants.

View Source
const DefaultRenderFlatten = 0.6

DefaultRenderFlatten is the standard curve flattening value.

Variables

View Source
var (
	BlackPen     = NewPen(color.Black, 1)
	DarkGrayPen  = NewPen(color.DarkGray, 1)
	GrayPen      = NewPen(color.MidGray, 1)
	LightGrayPen = NewPen(color.LightGray, 1)
	WhitePen     = NewPen(color.White, 1)
	RedPen       = NewPen(color.Red, 1)
	GreenPen     = NewPen(color.Green, 1)
	BluePen      = NewPen(color.Blue, 1)
	YellowPen    = NewPen(color.Yellow, 1)
	MagentaPen   = NewPen(color.Magenta, 1)
	CyanPen      = NewPen(color.Cyan, 1)
	OrangePen    = NewPen(color.Orange, 1)
	BrownPen     = NewPen(color.Brown, 1)
)

Predefined pens.

View Source
var RenderFlatten = DefaultRenderFlatten

RenderFlatten is the curve flattening value used when rendering.

View Source
var SafeFraction float64 = -1

SafeFraction if greater than 0 causes Simplify to perform a check of the mid-point against the part centroid. If the two are within SafeFraction of the distance from p[0] to the centroid then no further subdivision of the curve is performed.

Functions

func CPSafe

func CPSafe(part Part) bool

CPSafe returns true if all the control points are on the same side of the line formed by start and the last part points and the point at t = 0.5 is close to the centroid of the part.

func DrawArc

func DrawArc(dst draw.Image, start, center []float64, radians float64, pen *Pen)

DrawArc renders an arc with the pen into the destination image. radians +ve CCW, -ve CW

func DrawClippedShape

func DrawClippedShape(dst draw.Image, shape, clip *Shape, pen *Pen)

DrawClippedShape renders a shape with the pen against a clip shape into the destination image.

func DrawLine

func DrawLine(dst draw.Image, start, end []float64, pen *Pen)

DrawLine renders a line with the pen into the destination image.

func DrawPath

func DrawPath(dst draw.Image, path *Path, pen *Pen)

DrawPath renders a path with the pen into the destination image.

func DrawPoint

func DrawPoint(dst draw.Image, at []float64, pen *Pen)

DrawPoint renders a point with the pen into the destination image.

func DrawShape

func DrawShape(dst draw.Image, shape *Shape, pen *Pen)

DrawShape renders a shape with the pen into the destination image.

func FillClippedShape

func FillClippedShape(dst draw.Image, shape, clip *Shape, pen *Pen)

FillClippedShape renders a shape with the pen filler against a clipe shape and transform into the destination image.

func FillPath

func FillPath(dst draw.Image, path *Path, pen *Pen)

FillPath renders a path with the pen filler image and transform into the destination image.

func FillShape

func FillShape(dst draw.Image, shape *Shape, pen *Pen)

FillShape renders a shape with the pen filler and transform into the destination image.

func I266ToF64

func I266ToF64(fi fixed.Int26_6) float64

I266ToF64 converts a fixed.Int26_6 to float64

func InvalidPoint

func InvalidPoint(p []float64) bool

InvalidPoint checks that both values are valid (i.e. not NaN)

func Lerp

func Lerp(t float64, p1, p2 []float64) []float64

Lerp performs a linear interpolation between two points.

func PartLength

func PartLength(d float64, part Part) float64

PartLength returns the approximate length of a part by flattening it to the supplied degree of flatness.

func PartsIntersection

func PartsIntersection(part1, part2 Part, d float64) []float64

PartsIntersection returns the location of where the two parts intersect or nil. Assumes the parts are the result of simplification. Uses a brute force approach for curves with d as the flattening value.

func RenderClippedShape

func RenderClippedShape(dst draw.Image, shape, clip *Shape, filler image.Image)

RenderClippedShape renders the supplied shape with the fill image into the destination image as masked by the clip shape.

func RenderColoredShape

func RenderColoredShape(dst draw.Image, shape *Shape, fill color.Color)

RenderColoredShape renders the supplied shape with the fill color into the destination image.

func RenderRenderable

func RenderRenderable(img draw.Image, rend *Renderable, xfm Transform)

func RenderShape

func RenderShape(dst draw.Image, shape *Shape, filler image.Image)

RenderShape renders the supplied shape with the fill image into the destination image.

func RenderShapeExt

func RenderShapeExt(dst draw.Image, drect image.Rectangle, shape *Shape, filler image.Image, fp image.Point, mask *image.Alpha, mp image.Point, op draw.Op)

RenderShapeExt renders the supplied shape with the fill and clip images into the destination image region using op.

func StringToShape

func StringToShape(tfont *sfnt.Font, str string) (*Shape, []*Shape, error)

StringToShape returns the string rendered as both a single shape, and as individual shapes, correctly offset in font units. Glyphs with no paths are not returned (e.g. space etc.).

Types

type Aff3

type Aff3 [6]float64

Aff3 is a 3x3 affine transformation matrix in row major order, where the bottom row is implicitly [0 0 1].

m[3*r+c] is the element in the r'th row and c'th column.

func BBTransform

func BBTransform(bb1, bb2 [][]float64) *Aff3

BBTransform produces an affine transform that maps bounding box bb1 to bb2.

func BoxTransform

func BoxTransform(x1, y1, x2, y2, h, x1p, y1p, x2p, y2p, hp float64) *Aff3

BoxTransform produces an affine transform that maps the line {p1, p2} to {p1', p2'} and scales the perpendicular by hp / h. Assumes neither of the lines nor h are degenerate.

func CreateAffineTransform

func CreateAffineTransform(x, y, scale, rotation float64) *Aff3

CreateAffineTransform returns an affine transform that performs the requested translation, scaling and rotation based on {0, 0}.

func FlipY

func FlipY(height float64) *Aff3

FlipY is a convenience function to create an affine transform that has +ve Y point up rather than down.

func LineTransform

func LineTransform(x1, y1, x2, y2, x1p, y1p, x2p, y2p float64) *Aff3

LineTransform produces an affine transform that maps the line {p1, p2} to {p1', p2'}. Assumes neither of the lines are degenerate.

func NewAff3

func NewAff3() *Aff3

NewAff3 creates the identity affine transform.

func Reflect

func Reflect(x1, y1, x2, y2 float64) *Aff3

Reflect creates an affine reflection transform.

func Rotate

func Rotate(th float64) *Aff3

Rotate creates an affine rotation transform.

func RotateAbout

func RotateAbout(th, ax, ay float64) *Aff3

RotateAbout creates an affine rotation transform about a point.

func Scale

func Scale(sx, sy float64) *Aff3

Scale creates an affine scale transform.

func ScaleAbout

func ScaleAbout(sx, sy, ax, ay float64) *Aff3

ScaleAbout creates an affine scale transform about a point.

func ScaleAndInset

func ScaleAndInset(width, height, iwidth, iheight float64, fix bool, bb [][]float64) *Aff3

ScaleAndInset produces an affine transform that will scale and translate a set of points bounded by bb so they fit inside the inset box described by width, height, iwidth, iheight located at {0, 0}. If fix is true then the aspect ratio of bb is maintained.

func Shear

func Shear(shx, shy float64) *Aff3

Shear creates an affine shear transform.

func ShearAbout

func ShearAbout(shx, shy, ax, ay float64) *Aff3

ShearAbout creates an affine shear transform about a point.

func Translate

func Translate(x, y float64) *Aff3

Translate creates an affine translation transform.

func (*Aff3) Apply

func (a *Aff3) Apply(pts ...[]float64) [][]float64

Apply implements the Transform interface.

func (*Aff3) Concatenate

func (a *Aff3) Concatenate(aff Aff3) *Aff3

Concatenate concatenates an affine transform to the affine transform.

func (*Aff3) Copy

func (a *Aff3) Copy() *Aff3

Copy returns a copy of the affine transform.

func (*Aff3) Determinant

func (a *Aff3) Determinant() float64

Determinant calculates the affine transform's matrix determinant.

func (*Aff3) Identity

func (a *Aff3) Identity() bool

Identity returns true if the affine transform is the identity.

func (*Aff3) InverseOf

func (a *Aff3) InverseOf() (*Aff3, error)

InverseOf returns the inverse of the affine transform.

func (*Aff3) Invert

func (a *Aff3) Invert() error

Invert inverts the affine transform.

func (*Aff3) PreConcatenate

func (a *Aff3) PreConcatenate(aff Aff3) *Aff3

PreConcatenate preconcatenates an affine transform to the affine transform.

func (*Aff3) Process

func (a *Aff3) Process(p *Path) []*Path

Process implements the PathProcessor interface.

func (*Aff3) QuadrantRotate

func (a *Aff3) QuadrantRotate(n int) *Aff3

QuadrantRotate adds a rotation (n * 90 degrees) to the affine transform. The rotation is about {0, 0}. It avoids rounding issues with the trig functions.

func (*Aff3) QuadrantRotateAbout

func (a *Aff3) QuadrantRotateAbout(n int, ax, ay float64) *Aff3

QuadrantRotateAbout adds a rotation (n * 90 degrees) about a point to the affine transform. It avoids rounding issues with the trig functions.

func (*Aff3) Reflect

func (a *Aff3) Reflect(x1, y1, x2, y2 float64) *Aff3

Reflect performs a reflection along the axis defined by the two non-coincident points.

func (*Aff3) Rotate

func (a *Aff3) Rotate(th float64) *Aff3

Rotate adds a rotation to the affine transform. The rotation is about {0, 0}.

func (*Aff3) RotateAbout

func (a *Aff3) RotateAbout(th, ax, ay float64) *Aff3

RotateAbout adds a rotation about a point to the affine transform.

func (*Aff3) Scale

func (a *Aff3) Scale(sx, sy float64) *Aff3

Scale adds a scaling to the affine transform centered on {0, 0}.

func (*Aff3) ScaleAbout

func (a *Aff3) ScaleAbout(sx, sy, ax, ay float64) *Aff3

ScaleAbout adds a scale about a point to the affine transform.

func (*Aff3) Shear

func (a *Aff3) Shear(shx, shy float64) *Aff3

Shear adds a shear to the affine transform centered on {0, 0}.

func (*Aff3) ShearAbout

func (a *Aff3) ShearAbout(shx, shy, ax, ay float64) *Aff3

ShearAbout adds a shear about a point to the affine transform.

func (*Aff3) String

func (a *Aff3) String() string

String converts the affine transform into a string.

func (*Aff3) Translate

func (a *Aff3) Translate(x, y float64) *Aff3

Translate adds a translation to the affine transform.

type ArcStyle

type ArcStyle int

ArcStyle defines the type of arc - open, chord (closed) and pie (closed).

const (
	ArcOpen ArcStyle = iota
	ArcChord
	ArcPie
)

Constants for arc styles.

type BoxerProc

type BoxerProc struct {
	Width float64 // Width of box
	Offs  float64 // Offset from path of box
	Flat  float64 // Flatten value
}

BoxerProc is a path processor that converts a path into a set of boxes along the path with the specified width. If the offset is 0 then the box is centered on the path.

func NewBoxerProc

func NewBoxerProc(width, offs float64) BoxerProc

NewBoxerProc returns a new BoxerProc path processor.

func (BoxerProc) Process

func (bp BoxerProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type BucketProc

type BucketProc struct {
	N     int
	Style BucketStyle
}

BucketProc aggregates paths into N shapes using the specificed style.

Example

Demonstrates using a shape processor by plotting a series of different colored stars along a circular path.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	path := g2d.Circle([]float64{250, 250}, 200)
	star := g2d.NewShape(g2d.ReentrantPolygon([]float64{0, 0}, 20, 5, 0.5, 0))

	// Use ShapesProc path processor to draw a star every 50 pixels along the path
	pp := g2d.NewShapesProc([]*g2d.Shape{star}, 50, g2d.RotRandom)
	stars := g2d.NewShape(path.Process(pp)...)

	// Use BucketProc shape processor to take each path in stars and add it to one of n output shapes
	n := 6
	sp := g2d.BucketProc{N: n, Style: g2d.RoundRobin} // Other styles are g2d.Chunk and g2d.Random
	shapes := stars.Process(sp)

	img := image.NewRGBA(500, 500, color.White)
	for i, shape := range shapes {
		// Color shape by index into shapes
		pen := g2d.NewPen(color.HSL{float64(i+1) / float64(n), 1, 0.5, 1}, 3)
		g2d.DrawShape(img, shape, pen)
	}
	image.SaveImage(img, "stars")

	fmt.Printf("See stars.png")
}
Output:

See stars.png

func (BucketProc) Process

func (bp BucketProc) Process(s *Shape) []*Shape

Process implements the ShapeProcessor interface.

type BucketStyle

type BucketStyle int
const (
	Chunk BucketStyle = iota
	RoundRobin
	Random
)

type CapsProc

type CapsProc struct {
	Start  *Shape
	End    *Shape
	Mid    *Shape
	Rotate bool
}

CapsProc contains a trio of shapes, one which will be placed using the start at the path, one at each step and the last, at the end. If any shape is nil, then it is skipped. The rotation flag indicates if the shapes should be rotated relative to the path's tangent at that point.

func (CapsProc) Process

func (cp CapsProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type CircularJitterProc

type CircularJitterProc struct {
	Radius float64
}

CircularJitterProc takes a path and jitters its internal step points by a random amount within the defined radius.

func (CircularJitterProc) Process

func (sp CircularJitterProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type CompoundProc

type CompoundProc struct {
	Procs       []PathProcessor
	Concatenate bool // If set, concatenate processed paths into a single path after every processor
}

CompoundProc applies a collection of PathProcessors to a path.

func NewCompoundProc

func NewCompoundProc(procs ...PathProcessor) CompoundProc

NewCompoundProc creates a new CompoundProcessor with the supplied path processors.

func (CompoundProc) Process

func (cp CompoundProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type CurveProc

type CurveProc struct {
	Scale float64
	Style CurveStyle
}

CurveProc replaces the steps on a path with cubics. The locations of the control points are controlled by the Style setting and whether or not the path is closed.

func (CurveProc) Process

func (cp CurveProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type CurveStyle

type CurveStyle int

CurveStyle determines how the curve behaves relative to the path points. With Bezier, the path will intersect the mid-point of each path step. With Catmul, the path will intersect point.

const (
	Bezier CurveStyle = iota
	Quad
	CatmullRom
)

Constants for curve styles.

type DashProc

type DashProc struct {
	Snip *FSnipProc
}

DashProc contains the dash pattern and offset. The dash pattern represents lengths of pen down, pen up, ... and is in the same coordinate system as the path. The offset provides the ability to start from anywhere in the pattern.

func NewDashProc

func NewDashProc(pattern []float64, offs float64) DashProc

NewDashProc creates a new dash path processor with the supplied pattern and offset. If the pattern is odd in length then it is replicated to create an even length pattern.

func (DashProc) Process

func (d DashProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type FSnipProc

type FSnipProc struct {
	N       int
	Pattern []float64
	Flatten float64
	State   int
	// contains filtered or unexported fields
}

FSnipProc contains the snip pattern and offset. The snip pattern represents lengths of state0, state1, ... stateN-1, and is in the same coordinate system as the path. The offset provides the ability to start from anywhere in the pattern.

func NewFSnipProc

func NewFSnipProc(n int, pattern []float64, offs float64) *FSnipProc

NewFSnipProc creates a new snip path processor with the supplied pattern and offset. If the pattern is not N in length then it is replicated to create a mod N length pattern.

func (*FSnipProc) Offset

func (sp *FSnipProc) Offset(offs float64)

Offset determines where in the pattern the path processor will start.

func (*FSnipProc) Process

func (sp *FSnipProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type FlattenProc

type FlattenProc struct {
	Flatten float64
}

FlattenProc is a wrapper around Path.Flatten() and contains the minimum required distance to the control points.

func (FlattenProc) Process

func (fp FlattenProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type HandDrawnProc

type HandDrawnProc struct {
	Comp CompoundProc
}

HandDrawnProc contains the compound path processor used to create a hand drawn look.

func NewHandDrawnProc

func NewHandDrawnProc(l float64) HandDrawnProc

NewHandDrawnProc takes the segment length to apply the MPD path processor to and returns a new HandDrawnProc path processor.

func (HandDrawnProc) Process

func (h HandDrawnProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type HandyProc

type HandyProc struct {
	N int     // Repetitions
	R float64 // Jitter radius
}

HandyProc applies a modified form of line rendering as outlined in Wood12. Note the lines are not smoothed and closed paths are not preserved.

func (HandyProc) Process

func (hp HandyProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type JitterProc

type JitterProc struct {
	Perc float64 // Percentage
}

JitterProc contains the percentage degree to which segment endpoints will be moved to their left or right (relative to their tangents) based on their length. Can be used with MunchProc to get a hand drawn look.

func (JitterProc) Process

func (j JitterProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type LimitProc

type LimitProc struct {
	Limit float64 // Maximum allowed length of any path part
}

LimitProc takes a path and chunks it up into at most Limit length parts. Smaller parts are left alone. Similar to FSnipProc but without the pattern.

func (LimitProc) Process

func (lp LimitProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type MPDProc

type MPDProc struct {
	Perc  float64 // Percentage of step length used as initial displacement
	Itrs  int     // Number of iterations to perform
	Scale float64 // Multiplier (Hurst) used on displacement per iteration
	Rel   bool    // if set, normal is the relative one and not the original
}

MPDProc contains the variables that control the degree to which a step is chopped up into smaller line segments. Unlike JitterProc, the step end points don't vary. Can be used with MunchProc to get a hand drawn look.

func NewMPDProc

func NewMPDProc(l float64) MPDProc

NewMPDProc creates an MPDProc with sensible parameters for iterations and Hurst.

func (MPDProc) MPD

func (m MPDProc) MPD(a, b []float64) [][]float64

MPD takes two points and adds points between them using the mid-point displacement algorithm driven by the parameters stored in the MPDProc structure.

func (MPDProc) Process

func (m MPDProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type MiterJoin

type MiterJoin struct {
	MiterLimit   float64
	MiterAltFunc func(Part, []float64, Part) []Part
}

MiterJoin describes the limit and alternative function to use when the limit is exceeded for a miter join.

func NewMiterJoin

func NewMiterJoin() MiterJoin

NewMiterJoin creates a default MiterJoin with the limit set to 10 degrees and the alternative function to JoinBevel.

func (MiterJoin) JoinMiter

func (mj MiterJoin) JoinMiter(p1 Part, p []float64, p2 Part) []Part

JoinMiter creates a miter join from e1 to s2 unless the moter limit is exceeded in which case the alternative function is used to perform the join.

type MunchProc

type MunchProc struct {
	Comp CompoundProc
}

MunchProc contains the munching compound path processor.

func NewMunchProc

func NewMunchProc(l float64) MunchProc

NewMunchProc creates a munching path processor. It calculates points along a path spaced l apart and creates new paths that join the points with lines.

func (MunchProc) Process

func (mp MunchProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type OvalCap

type OvalCap struct {
	Rxy  float64 // Ratio of Rx to Ry
	Offs float64 // Offset from center line [-1,1] -1 = LHS, 0 = centerline, 1 = RHS
}

OvalCap contains the ratio of rx to ry for the oval and a center line offset

func (OvalCap) CapInvOval

func (oc OvalCap) CapInvOval(p1 Part, p []float64, p2 Part) []Part

CapInvOval creates an inverted half oval with rx = w/2 and ry = rxy * rx Offs is ignored

func (OvalCap) CapOval

func (oc OvalCap) CapOval(p1 Part, p []float64, p2 Part) []Part

CapOval creates a half oval with ry = w/2 and rx = Rxy * ry

type Part

type Part [][]float64

Part represents a path step with the previous step's end point prepended.

func CapButt

func CapButt(p1 Part, p []float64, p2 Part) []Part

CapButt draws a line from e1 to s1.

func CapHead

func CapHead(p1 Part, p []float64, p2 Part) []Part

CapHead draws an extended arrow head from e1 to extended p and then to s1.

func CapInvPoint

func CapInvPoint(p1 Part, p []float64, p2 Part) []Part

CapInvPoint extends e1 and s1 and draws an arrow tail that passes through p.

func CapInvRound

func CapInvRound(p1 Part, p []float64, p2 Part) []Part

CapInvRound extends e1 and s1 and draws a semicircle that passes through p.

func CapPoint

func CapPoint(p1 Part, p []float64, p2 Part) []Part

CapPoint draws an arrow head from e1 to s1 centered on p.

func CapRound

func CapRound(p1 Part, p []float64, p2 Part) []Part

CapRound draws a semicircle from e1 to s1 centered on p.

func CapSquare

func CapSquare(p1 Part, p []float64, p2 Part) []Part

CapSquare draws an extended square (stroke width/2) from e1 and s1.

func CapTail

func CapTail(p1 Part, p []float64, p2 Part) []Part

CapTail draws an extended arrow tail (stroke width/2) from extended e1 to p and then to extended s1.

func FlattenPart

func FlattenPart(d float64, part Part) []Part

FlattenPart works by subdividing the curve until its control points are within d^2 (d squared) of the line through the end points.

func JoinBevel

func JoinBevel(p1 Part, p []float64, p2 Part) []Part

JoinBevel creates a bevel join from e1 to s2.

func JoinRound

func JoinRound(p1 Part, p []float64, p2 Part) []Part

JoinRound creates a round join from e1 to s2, centered on p.

func MakeArcParts

func MakeArcParts(cx, cy, r, offs, ang float64) []Part

MakeArcParts creates at least one cubic bezier that describes a curve from offs to offs+ang centered on {cx, cy} with radius r.

func MakeRoundedParts

func MakeRoundedParts(p1, p2, p3 []float64, r float64) []Part

MakeRoundedParts uses the tangents p1-p2 and p2-p3, and the radius r to figure an arc between them.

func PointCircle

func PointCircle(pt []float64, w float64) []Part

PointCircle renders points as circles/

func PointDiamond

func PointDiamond(pt []float64, w float64) []Part

PointDiamond renders points as diamonds aligned in x/y.

func PointSquare

func PointSquare(pt []float64, w float64) []Part

PointSquare renders points as squares aligned in x/y.

func ReverseParts

func ReverseParts(parts []Part) []Part

ReverseParts reverses the order (and points) of the supplied part slice.

func ReversePoints

func ReversePoints(cp Part) Part

[pts]x/y

func SimplifyExtremities

func SimplifyExtremities(part Part) []Part

SimplifyExtremities chops curve into pieces based on maxima, minima and inflections in x and y.

func SimplifyPart

func SimplifyPart(part Part) []Part

SimplifyPart recursively cuts the curve in half until CPSafe is satisfied.

func (Part) String

func (p Part) String() string

String converts a part into a string.

type Path

type Path struct {
	// contains filtered or unexported fields
}

Path contains the housekeeping necessary for path building.

func Arc

func Arc(c []float64, r, offs, ang float64, s ArcStyle) *Path

Arc returns a path with an arc centered on c with radius r from offs in the direction and length of ang.

func ArcFromPoint

func ArcFromPoint(pt, c []float64, ang float64, s ArcStyle) *Path

ArcFromPoint returns a path describing an arc starting from pt based on c and ang.

func ArcFromPoints

func ArcFromPoints(a, b, c []float64, s ArcStyle) *Path

ArcFromPoints returns a path describing an arc passing through a, b and c such that the arc starts at a, passes through b and ends at c.

func Circle

func Circle(c []float64, r float64) *Path

Circle returns a closed path describing a circle centered on c with radius r.

func ConcatenatePaths

func ConcatenatePaths(paths ...*Path) (*Path, error)

ConcatenatePaths concatenates all the paths into a new path. If any path is closed then an error is returned. If the paths aren't coincident, then they are joined with a line.

func Curve

func Curve(pts ...[]float64) *Path

Curve returns a path describing the polynomial curve.

func Egg

func Egg(c []float64, w, h, d, xang float64) *Path

Egg uses IrregularEllipse to generate an egg shape with the specified width and height. The waist is specified as a percentage distance along the height axis (from the base). The egg is rotated by xang.

Example

Demonstrates various eggs with different waists.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	width, height := 1500, 300

	n := 5
	dx := float64(width / n)
	bb := [][]float64{{10, 10}, {dx - 10, float64(height) - 10}}
	c := []float64{0, 0}
	dt := 1.0 / float64(n+1)
	t := dt
	shape := &g2d.Shape{}
	for range n {
		path := g2d.Egg(c, 120, 200, t, 0)
		pbb := path.BoundingBox()
		// Fix pbb width
		pbb[0][0] = -100
		pbb[1][0] = 100
		xfm := g2d.BBTransform(pbb, bb)
		path = path.Process(xfm)[0]
		shape.AddPaths(path)
		bb[0][0] += dx
		bb[1][0] += dx
		t += dt
	}

	img := image.NewRGBA(width, height, color.White)
	g2d.RenderColoredShape(img, shape, color.Green)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, shape, pen)

	image.SaveImage(img, "egg")
	fmt.Println("Check egg.png")
}
Output:

Check egg.png

func Ellipse

func Ellipse(c []float64, rx, ry, xang float64) *Path

Ellipse returns a closed path describing an ellipse with rx and ry rotated by xang from the x axis.

func EllipseFromPoints

func EllipseFromPoints(p1, p2, c []float64) *Path

EllipseFromPoints returns a path describing the smallest ellipse containing points p1 and p2. If p1, p2 and c are colinear and not equidistant then nil is returned.

func EllipticalArc

func EllipticalArc(c []float64, rx, ry, offs, ang, xang float64, s ArcStyle) *Path

EllipticalArc returns a path describing an arc starting at offs and ending at offs+ang on the ellipse defined by rx and ry rotated by xang from the x axis.

Example

Demonstrates using EllipticalArc to create a logo

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	width, height := 400, 400

	// Make first half
	c := []float64{200, 220}
	r := 150.0
	rxy := 0.4
	path1 := g2d.EllipticalArc(c, r, r, 0, g2d.Pi, 0, g2d.ArcOpen)
	path2 := g2d.EllipticalArc(c, r, r*rxy, g2d.Pi, -g2d.Pi, 0, g2d.ArcOpen)
	path3 := g2d.EllipticalArc(c, r, r*rxy, g2d.Pi, -g2d.TwoPi*0.7, 0, g2d.ArcOpen)
	p1 := []float64{c[0] - r, c[1]}
	// Pull p2 from path3
	p2 := path3.Current()
	r2 := r * 0.85
	path4 := g2d.EllipticalArcFromPoints2(p1, p2, r2, r2*rxy, 0, true, false, g2d.ArcOpen)

	// Combine paths into a shape
	shape := g2d.NewShape(path1, path2, path3, path4)

	// Rotate by 180 to get second half and add it
	xfm := g2d.RotateAbout(g2d.Pi, 200, 200)
	shape.AddShapes(shape.ProcessPaths(xfm))

	// Rotate shape CCW by 60
	xfm = g2d.RotateAbout(-g2d.Pi/6, 200, 200)
	shape = shape.ProcessPaths(xfm)

	img := image.NewRGBA(width, height, color.White)
	g2d.DrawShape(img, shape, g2d.BlackPen)
	image.SaveImage(img, "logo")

	fmt.Println("Check logo.png")
}
Output:

Check logo.png

func EllipticalArcFromPoint

func EllipticalArcFromPoint(pt, c []float64, rxy, ang, xang float64, s ArcStyle) *Path

EllipticalArcFromPoint returns a path describing an ellipse arc from a point. The ratio of rx to ry is specified by rxy.

func EllipticalArcFromPoints

func EllipticalArcFromPoints(p1, p2, c []float64, s ArcStyle) *Path

EllipticalArcFromPoints returns a path describing the smallest ellipse arc from a point p1 to p2 (ccw). If p1, p2 and c are colinear and not equidistant then nil is returned.

func EllipticalArcFromPoints2

func EllipticalArcFromPoints2(p1, p2 []float64, rx, ry, xang float64, arc, swp bool, s ArcStyle) *Path

EllipticalArcFromPoints2 provides a specification similar to that found in the SVG11 standard where the center is calculated from the given rx and ry values and flag specifications. See https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes

func ExtendLine

func ExtendLine(pt1, pt2 []float64, bounds [][]float64) *Path

ExtendLine returns the line that passes through the bounds (or nil) defined by the line equation of pt1 and pt2.

func IrregularEllipse

func IrregularEllipse(c []float64, rx1, rx2, ry1, ry2, disp, xang float64) *Path

IrregularEllipse uses different rx and ry values for each quadrant of an ellipse. disp (-1,1) determines how far along either rx1 (+ve) or rx2 (-ve), ry2 extends from (ry1 extends from c).

func IrregularPolygon

func IrregularPolygon(c []float64, r float64, n int, nr bool) *Path

IrregularPolygon returns an n sided polgon guaranteed to be located within a circle of radius r centered on cp. If nr is set to true then polygon is forced to be non-reentrant.

func Line

func Line(pt1, pt2 []float64) *Path

Line returns a path describing the line.

func Lune

func Lune(c []float64, r0, r1, r2, th float64) *Path

Lune returns a closed path made up of two arcs with end points at c plus/minus r0 in y, all rotated by th. The arcs are calculated from the circumcircles of the two triangles defined by the end points, and c displaced by r1 or r2 in x.

Example

Demonstrates various degrees of lunes.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	width, height := 1600, 300

	n := 6
	dx := float64(width / n)
	cx, cy := dx/2, float64(height)/2
	dt := 1.0 / float64(n-1)
	t := 0.0
	shape := &g2d.Shape{}
	for range n {
		c := []float64{cx, cy}
		shape.AddPaths(g2d.Lune(c, 50, t*100+10, t*50+15, 0))
		cx += dx
		t += dt
	}

	img := image.NewRGBA(width, height, color.White)
	g2d.RenderColoredShape(img, shape, color.Green)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, shape, pen)

	image.SaveImage(img, "lune")
	fmt.Println("Check lune.png")
}
Output:

Check lune.png

func Lune2

func Lune2(p1, p2 []float64, r1, r2 float64) *Path

Lune2 returns a closed path made up of two arcs with the supplied end points. The arcs are calculated from the circumcircles of the two triangles defined by the end points, and their midpoint displaced by r1 or r2.

func Lune3

func Lune3(c1 []float64, r1 float64, c2 []float64, r2 float64) *Path

Lune3 returns a closed path made up of the two arcs created at the intersection of circles c1,r1 and c2/r2. If they don't intesect or if one circle is fully contained in the other, nil is returned. The arc selected is determined by the sign of the radius: +ve major; -ve minor.

Example
package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	width, height := 1200, 300

	dx := 300.0
	c1, c2 := []float64{100, 150}, []float64{200, 150}
	r := 75.0
	shape, cshape := &g2d.Shape{}, &g2d.Shape{}
	shape.AddPaths(g2d.Lune3(c1, r, c2, r))
	cshape.AddPaths(g2d.Circle(c1, r), g2d.Circle(c2, r))
	c1[0] += dx
	c2[0] += dx
	shape.AddPaths(g2d.Lune3(c1, r, c2, -r))
	cshape.AddPaths(g2d.Circle(c1, r), g2d.Circle(c2, r))
	c1[0] += dx
	c2[0] += dx
	shape.AddPaths(g2d.Lune3(c1, -r, c2, r))
	cshape.AddPaths(g2d.Circle(c1, r), g2d.Circle(c2, r))
	c1[0] += dx
	c2[0] += dx
	shape.AddPaths(g2d.Lune3(c1, -r, c2, -r))
	cshape.AddPaths(g2d.Circle(c1, r), g2d.Circle(c2, r))

	img := image.NewRGBA(width, height, color.White)
	g2d.DrawShape(img, cshape, g2d.RedPen)
	g2d.RenderColoredShape(img, shape, color.Green)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, shape, pen)

	image.SaveImage(img, "lune3")
	fmt.Println("Check lune3.png")
}
Output:

Check lune3.png

func Nellipse

func Nellipse(l float64, foci ...[]float64) *Path

Nellipse takes a slice of ordered foci, assumed to be on the hull of a convex polygon, and a length, and uses them to construct a Gardener's ellipse (an approximation that ignores foci within the hull). The closed path will be made up of twice as many arcs as there are foci. If the length isn't sufficient to wrap the foci, then nil is returned.

func NewPath

func NewPath(start []float64) *Path

NewPath creates a new path starting at start.

func PartsToPath

func PartsToPath(parts ...Part) *Path

PartsToPath constructs a new path by concatenating the parts.

func Point

func Point(pt []float64) *Path

Point returns a path containing the point.

func PolyArcFromPoint

func PolyArcFromPoint(pt []float64, cs [][]float64, angs []float64) *Path

PolyArcFromPoint returns a path concatenating the arcs.

func PolyCurve

func PolyCurve(pts ...[][]float64) *Path

PolyCurve returns a path describing the polynomial curves.

func PolyLine

func PolyLine(pts ...[]float64) *Path

PolyLine returns a path with lines joining successive points.

func Polygon

func Polygon(pts ...[]float64) *Path

Polygon returns a closed path with lines joining successive points.

func Rectangle

func Rectangle(c []float64, w, h float64) *Path

Rectangle returns a closed path describing a rectangle with sides w and h, centered on c.

func ReentrantPolygon

func ReentrantPolygon(c []float64, r float64, n int, t, ang float64) *Path

ReentrantPolygon returns a closed path describing an n pointed star.

func RegularPolygon

func RegularPolygon(n int, c []float64, s, th float64) *Path

RegularPolygon returns a closed path describing an n-sided polygon centered on c rotated by th. th = 0 => polygon sits on its base.

func RightEgg

func RightEgg(c []float64, h, d, xang float64) *Path

RightEgg uses IrregularEllipse to generate an egg shape with the specified height and a semicircular base. The waist is specified as a percentage distance along the height axis (from the base). The egg is rotated by xang.

Example

Demonstrates various right eggs.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
	"math"
)

func main() {
	width, height := 1500, 300

	h := 200.0
	c := []float64{0, 0}
	dvals := []float64{
		// Values from Mathographics, Robert Dixon, 1987
		1.0 / (4.0 - math.Sqrt2),      // Moss
		2.0 / (3.0 + g2d.Sqrt3),       // Cundy Rollett
		7.0 / 16.0,                    // Thom1
		3.0 / (2.0 + math.Sqrt(10.0)), // Thom2
		1.0 / (1.0 + math.Phi),        // Golden
	}
	dx := float64(width / len(dvals))
	bb := [][]float64{{10, 10}, {dx - 10, float64(height) - 10}}

	shape := &g2d.Shape{}
	for _, d := range dvals {
		path := g2d.RightEgg(c, h, d, g2d.Pi) // Flip Y
		pbb := path.BoundingBox()
		// Fix pbb width
		pbb[0][0] = -100
		pbb[1][0] = 100
		xfm := g2d.BBTransform(pbb, bb)
		path = path.Process(xfm)[0]
		shape.AddPaths(path)
		bb[0][0] += dx
		bb[1][0] += dx
	}

	img := image.NewRGBA(width, height, color.White)
	g2d.RenderColoredShape(img, shape, color.Green)
	pen := g2d.NewPen(color.Black, 3)
	g2d.DrawShape(img, shape, pen)

	image.SaveImage(img, "regg")
	fmt.Println("Check regg.png")
}
Output:

Check regg.png

func StringToPath

func StringToPath(str string) *Path

StringToPath converts a string created using path.String() back into a path. Returns nil if the string isn't parsable into a path.

func (*Path) AddParts

func (p *Path) AddParts(parts ...Part) *Path

AddParts adds parts to the path and returns it.

func (*Path) AddStep

func (p *Path) AddStep(points ...[]float64) error

AddStep takes an array of points and treats n-1 of them as control points and the last as a point on the curve. Adding a step to a closed path will cause an error as will adding an invalid point.

func (*Path) ArcTo

func (p *Path) ArcTo(p1, p2 []float64, r float64) *Path

ArcTo is a chain wrapper around MakeRoundedParts. If r is too large for the supplied tangents, then it is truncated.

func (*Path) BoundingBox

func (p *Path) BoundingBox() [][]float64

BoundingBox calculates a bounding box that the Path is guaranteed to fit within. It's unlikely to be the minimal bounding box for the path since the control points are also included. If a tight bounding box is required then use CalcExtremities().

func (*Path) Bounds

func (p *Path) Bounds() image.Rectangle

Bounds calculates a rectangle that the Path is guaranteed to fit within. It's unlikely to be the minimal bounding rectangle for the path since the control points are also included. If a tight bounding rectangle is required then use CalcExtremities().

func (*Path) Close

func (p *Path) Close() *Path

Close marks the path as closed.

func (*Path) Closed

func (p *Path) Closed() bool

Closed returns true if the path is closed.

func (*Path) Concatenate

func (p *Path) Concatenate(paths ...*Path) error

Concatenate adds the paths to this path. If any path is closed then an error is returned. If the paths aren't coincident, then they are joined with a line.

func (*Path) Copy

func (p *Path) Copy() *Path

Copy performs a deep copy

func (*Path) Current

func (p *Path) Current() []float64

Current returns the last point in the path.

func (*Path) CurveTo

func (p *Path) CurveTo(points ...[]float64) *Path

CurveTo is a chain wrapper around AddStep.

func (*Path) Flatten

func (p *Path) Flatten(d float64) *Path

Flatten works by recursively subdividing the path until the control points are within d of the line through the end points.

func (*Path) Length

func (p *Path) Length(flat float64) float64

Length returns the approximate length of a path by flattening it to the desired degree and summing the line steps.

func (*Path) LineTo

func (p *Path) LineTo(point []float64) *Path

LineTo is a chain wrapper around AddStep.

func (*Path) MarshalJSON

func (p *Path) MarshalJSON() ([]byte, error)

MarshalJSON implements the encoding/json.Marshaler interface

func (*Path) Parent

func (p *Path) Parent() *Path

Parent returns the path's parent

func (*Path) Parts

func (p *Path) Parts() []Part

Parts returns the steps of a path, each prepended with its start.

func (*Path) PointInPath

func (p *Path) PointInPath(pt []float64) bool

PointInPath returns if a point is contained within a closed path according to the setting of util.WindingRule. If the path is not closed then false is returned, regardless.

func (*Path) PolyLine

func (p *Path) PolyLine() ([][]float64, bool)

PolyLine converts a path into a polygon line. If the second result is true, the result is a polygon.

func (*Path) Process

func (p *Path) Process(proc PathProcessor) []*Path

Process applies a processor to a path.

func (*Path) ProjectPoint

func (p *Path) ProjectPoint(pt []float64) ([]float64, float64, float64)

ProjectPoint returns the point, it's t on the path closest to pt and the distance^2. Note t can be very non-linear.

func (*Path) Reverse

func (p *Path) Reverse() *Path

Reverse returns a new path describing the current path in reverse order (i.e start and end switched).

func (*Path) Simplify

func (p *Path) Simplify() *Path

Simplify breaks up a path into steps where for any step, its control points are all on the same side and its midpoint is well behaved. If a step doesn't meet the criteria, it is recursively subdivided in half until it does.

func (*Path) Steps

func (p *Path) Steps() [][][]float64

Steps returns a shallow copy of all the steps in the path.

func (*Path) String

func (p *Path) String() string

String converts a path into a string. P %f,%f [S %d [%f,%f ]][C]

func (*Path) Tangents

func (p *Path) Tangents() [][][]float64

Tangents returns the normalized start and end tangents of every part in the path. [part]start/end[normalized x/y]

func (*Path) UnmarshalJSON

func (p *Path) UnmarshalJSON(b []byte) error

UnmarshalJSON implements the encoding/json.Unmarshaler interface

type PathProcessor

type PathProcessor interface {
	Process(p *Path) []*Path
}

PathProcessor defines the interface required for function passed to the Process function in Path.

type PathSnipProc

type PathSnipProc struct {
	Flatten float64
	Path    *Path
}

PathSnipProc contains the snip path.

func NewPathSnipProc

func NewPathSnipProc(path *Path) PathSnipProc

NewPathSnipProc creates a new path snip path processor with the supplied path.

func (PathSnipProc) Process

func (psp PathSnipProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type PathToLineProc

type PathToLineProc struct{}

PathToLineProc reduces a path to a single line, or point if closed.

func (PathToLineProc) Process

func (plp PathToLineProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type PathsProc

type PathsProc struct{}

PathsProc converts each path in a shape into its own shape.

func (PathsProc) Process

func (pp PathsProc) Process(s *Shape) []*Shape

Process implements the ShapeProcessor interface.

type Pen

type Pen struct {
	Filler image.Image
	Stroke PathProcessor
	Xfm    Transform
}

Pen describes the color/image, stroke and shape to image transform to use when rendering shapes. If Stroke is nil then the shape's paths are used as is and forced closed (i.e. this is a fill). If Xfm is nil then the identity xfm is assumed.

Example

Generates a series of regular shapes with a dashed pen.

package main

import (
	"fmt"
	g2d "github.com/jphsd/graphics2d"
	"github.com/jphsd/graphics2d/color"
	"github.com/jphsd/graphics2d/image"
)

func main() {
	shape := g2d.NewShape(
		g2d.Line([]float64{20, 20}, []float64{130, 130}),
		g2d.RegularPolygon(3, []float64{225, 75}, 110, g2d.HalfPi),
		g2d.RegularPolygon(4, []float64{375, 75}, 110, 0),
		g2d.RegularPolygon(5, []float64{525, 75}, 75, 0),
		g2d.Circle([]float64{675, 75}, 55),
		g2d.Ellipse([]float64{825, 75}, 70, 35, g2d.HalfPi/2))

	img := image.NewRGBA(900, 150, color.White)
	dash := g2d.NewDashProc([]float64{8, 2, 2, 2}, 0)
	pen := g2d.NewProcessorPen(color.Black, 3, dash)
	g2d.DrawShape(img, shape, pen)
	image.SaveImage(img, "pen")

	fmt.Printf("See pen.png")
}
Output:

See pen.png

func NewPen

func NewPen(color color.Color, width float64) *Pen

NewPen returns a pen that will render a shape with the given pen width and color into an image. It uses the Stroke path processor to accomplish this with the bevel join and butt cap functions.

func NewProcessorPen

func NewProcessorPen(color color.Color, width float64, proc PathProcessor) *Pen

NewProcessorPen returns a pen that will render a shape with the given pen width and color into an image after applying the supplied path processor.

func NewStrokedPen

func NewStrokedPen(color color.Color, width float64,
	join func(Part, []float64, Part) []Part,
	cap func(Part, []float64, Part) []Part) *Pen

NewStrokedPen returns a pen that will render a shape with the given pen width and color into an image using the supplied join and cap functions.

type PointRot

type PointRot int

PointRot specifies the type of shape rotation

const (
	// RotFixed no rotation
	RotFixed PointRot = iota
	// RotRelative rotation relative to the tangent of the path step
	RotRelative
	// RotRandom rotation is randomized
	RotRandom
)

type PointsProc

type PointsProc struct {
	Points []*Shape
	Rotate PointRot
}

PointsProc contains a slice of shapes, one of which will be placed at the start of each step in the path and at the path end, if not closed. If any shape is nil, then it is skipped. The rotation flag indicates if the shapes should be rotated relative to the path's tangent at that point.

func NewPointsProc

func NewPointsProc(shapes []*Shape, rot PointRot) PointsProc

NewPointsProc creates a new points path processor with the supplied shapes and rotation flag.

func (PointsProc) Process

func (pp PointsProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type RSCap

type RSCap struct {
	Perc float64
}

RSCap contains the percentage [0,1] of the corner taken up by an arc. Perc = 1 is equivalent to CapRound, Perc = 0, to CapSquare.

func (RSCap) CapRoundedSquare

func (rc RSCap) CapRoundedSquare(p1 Part, p []float64, p2 Part) []Part

CapRoundedSquare creates a square cap with rounded corners

type Renderable

type Renderable struct {
	Shapes  []*Shape
	Clips   []*Shape
	Fillers []image.Image
}

Renderable represents a set of shapes and the images to fill them. In other words, enough information to be able to render something. This structure is used to build complex multicolored objects in a composable way.

func NewRenderable

func NewRenderable(shape *Shape, filler image.Image, xfm Transform) *Renderable

NewRenderable creates a new instance with the given shape and filler image.

func (*Renderable) AddClippedColoredShape

func (r *Renderable) AddClippedColoredShape(shape, clip *Shape, col color.Color, xfm Transform) *Renderable

AddClippedColoredShape adds the given shape, clip and color to the Renderable after being transformed.

func (*Renderable) AddClippedPennedShape

func (r *Renderable) AddClippedPennedShape(shape, clip *Shape, pen *Pen, xfm Transform) *Renderable

AddClippedPennedShape adds the given shape, clip and pen to the Renderable after being transformed.

func (*Renderable) AddClippedShape

func (r *Renderable) AddClippedShape(shape, clip *Shape, filler image.Image, xfm Transform) *Renderable

AddClippedShape adds the given shape, clip and filler to the Renderable after being transformed.

func (*Renderable) AddColoredShape

func (r *Renderable) AddColoredShape(shape *Shape, col color.Color, xfm Transform) *Renderable

AddColoredShape adds the given shape and color to the Renderable after being transformed.

func (*Renderable) AddPennedShape

func (r *Renderable) AddPennedShape(shape *Shape, pen *Pen, xfm Transform) *Renderable

AddPennedShape adds the given shape and pen to the Renderable after being transformed.

func (*Renderable) AddRenderable

func (r *Renderable) AddRenderable(rend *Renderable, xfm Transform) *Renderable

AddRenderable allows another renderable to be concatenated (post transform) to the current one.

func (*Renderable) AddShape

func (r *Renderable) AddShape(shape *Shape, filler image.Image, xfm Transform) *Renderable

AddShape adds the given shape and filler to the Renderable after being transformed.

func (*Renderable) Bounds

func (r *Renderable) Bounds() image.Rectangle

Bounds returns the extent of the renderable.

func (*Renderable) Image

func (r *Renderable) Image() *image.RGBA

Image renders the shapes in the renderable with their respective fillers.

func (*Renderable) Render

func (r *Renderable) Render(img draw.Image, xfm Transform)

Render renders the shapes in the renderable with their respective fillers after being transformed.

type ReverseProc

type ReverseProc struct{}

ReverseProc replaces a path with its reverse.

func (ReverseProc) Process

func (rp ReverseProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type RoundedEdgeProc

type RoundedEdgeProc struct {
	Dist float64
	Abs  bool
	Elip bool
}

RoundedEdgeProc is a path processor that replaces the parts of a path with an arc defined by the end points of the path and a third point normal to the part midpoint at either an absolute or relative (to the part length) distance from the midpoint. If Elip is set, then an elliptical arc of ry = d, rx = edge length / 2 is used instead.

func (RoundedEdgeProc) Process

func (rp RoundedEdgeProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type RoundedProc

type RoundedProc struct {
	Radius float64
}

RoundedProc replaces adjacent line segments in a path with line-arc-line where the radius of the arc is the minimum of Radius or the maximum allowable for the length of the shorter line segment. This ensures that the rounded corner doesn't end beyond the mid point of either line.

func (RoundedProc) Process

func (rp RoundedProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type Shape

type Shape struct {
	// contains filtered or unexported fields
}

Shape is a fillable collection of paths. For a path to be fillable, it must be closed, so paths added to the shape are forced closed on rendering.

func GlyphIndexToShape

func GlyphIndexToShape(font *sfnt.Font, x sfnt.GlyphIndex) (*Shape, error)

GlyphIndexToShape returns a shape containing the paths for glyph index x as found in the font. The path is in font units. Use font.UnitsPerEm() to calculate scale factors.

func GlyphToShape

func GlyphToShape(font *sfnt.Font, r rune) (*Shape, error)

GlyphToShape returns a shape containing the paths for rune r as found in the font. The path is in font units. Use font.UnitsPerEm() to calculate scale factors.

func NewShape

func NewShape(paths ...*Path) *Shape

NewShape constructs a shape from the supplied paths.

func (*Shape) AddPaths

func (s *Shape) AddPaths(paths ...*Path)

AddPaths adds paths to the shape.

func (*Shape) AddShapes

func (s *Shape) AddShapes(shapes ...*Shape)

AddShapes adds the paths from the supplied shapes to this shape.

func (*Shape) BoundingBox

func (s *Shape) BoundingBox() [][]float64

BoundingBox calculates a bounding box that the Shape is guaranteed to fit within.

func (*Shape) Bounds

func (s *Shape) Bounds() image.Rectangle

Bounds calculates the union of the bounds of the paths the shape contains.

func (*Shape) Contains

func (s *Shape) Contains(pts ...[]float64) bool

Contains returns true if the points are contained within the shape, false otherwise.

func (*Shape) Copy

func (s *Shape) Copy() *Shape

Copy creates a new instance of this shape with a copy of its paths.

func (*Shape) MarshalJSON

func (s *Shape) MarshalJSON() ([]byte, error)

MarshalJSON implements the encoding/json.Marshaler interface

func (*Shape) Mask

func (s *Shape) Mask() *image.Alpha

Mask returns an Alpha image defined by the shape's bounds, containing the result of rendering the shape.

func (*Shape) Paths

func (s *Shape) Paths() []*Path

Paths returns a shallow copy of the paths contained by this shape.

func (*Shape) PointInShape

func (s *Shape) PointInShape(pt []float64) bool

PointInShape returns true if the point is contained within any path within the shape.

func (*Shape) Process

func (s *Shape) Process(proc ShapeProcessor) []*Shape

Process applies a shape processor to the shape and returns a collection of new shapes.

func (*Shape) ProcessPaths

func (s *Shape) ProcessPaths(proc PathProcessor) *Shape

ProcessPaths applies a path processor to the shape and returns a new shape containing the processed paths.

func (*Shape) String

func (s *Shape) String() string

String converts a shape into a string.

func (*Shape) Transform

func (s *Shape) Transform(xfm Transform) *Shape

Transform applies a transform to all the paths in the shape and returns a new shape. JH deprecate this

func (*Shape) UnmarshalJSON

func (s *Shape) UnmarshalJSON(b []byte) error

UnmarshalJSON implements the encoding/json.Unmarshaler interface

type ShapeProcessor

type ShapeProcessor interface {
	Process(s *Shape) []*Shape
}

ShapeProcessor defines the interface required for function passed to the Process function in Shape.

type ShapesProc

type ShapesProc struct {
	Comp   CompoundProc
	Shapes PointsProc
}

ShapesProc contains a slice of shapes, which will be placed sequentially along the path, starting at the beginning and spaced there after by the spacing value, and at the path end, if not closed. If any shape is nil, then it is skipped. The rotation flag indicates if the shapes should be rotated relative to the path's tangent at that point.

func NewShapesProc

func NewShapesProc(shapes []*Shape, spacing float64, rot PointRot) ShapesProc

NewShapesProc creates a new shapes path processor with the supplied shapes, spacing and rotation flag.

func (ShapesProc) Process

func (sp ShapesProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type SimpleStrokeProc

type SimpleStrokeProc struct {
	Width float64
}

SimpleStrokeProc implements the simplest stroke path processor - flatten everything to RenderFlatten tolerance and turn each flattened line step into a rectangle Width wide. No joins or caps, just lots of little rectangles.

func (SimpleStrokeProc) Process

func (cp SimpleStrokeProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type SimplifyProc

type SimplifyProc struct{}

SimplifyProc is a wrpper around Path.Simplify().

func (SimplifyProc) Process

func (sp SimplifyProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type SnipProc

type SnipProc struct {
	N       int
	Pattern []float64
	Flatten float64
	State   int
	// contains filtered or unexported fields
}

SnipProc contains the snip pattern and offset. The snip pattern represents lengths of state0, state1, ... stateN-1, and is in the same coordinate system as the path. The offset provides the ability to start from anywhere in the pattern.

func NewSnipProc

func NewSnipProc(n int, pattern []float64, offs float64) *SnipProc

NewSnipProc creates a new snip path processor with the supplied pattern and offset. If the pattern is not N in length then it is replicated to create a mod N length pattern.

func (*SnipProc) Offset

func (sp *SnipProc) Offset(offs float64)

Offset determines where in the pattern the path processor will start.

func (*SnipProc) Process

func (sp *SnipProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type SquareWaveProc

type SquareWaveProc struct {
	HalfLambda float64 // Half wave length
	Scale      float64 // Ratio of amplitude to lambda
	KeepZero   bool    // Keeps internal zero-point crossings if set
	Flip       bool    // Flips the wave phase by 180 (pi) if set
}

SquareWaveProc applies a square wave along a path with a defined wave length and amplitude. The wave starts and ends on zero-crossing points and the last half wave is truncated to the path length remaining. The internal zero-crossing points can be optionally preserved.

func NewSquareWaveProc

func NewSquareWaveProc(lambda, amplitude float64) SquareWaveProc

NewSquareWaveProc creates a new SquareWaveProc with the supplied wave length and amplitude.

func (SquareWaveProc) Process

func (sp SquareWaveProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type StepsProc

type StepsProc struct{}

StepsProc converts each path step into its own path.

func (StepsProc) Process

func (sp StepsProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type StepsToLinesProc

type StepsToLinesProc struct {
	IncCP bool
}

StepsToLinesProc takes a path and converts all of the points to lines.

func (StepsToLinesProc) Process

func (clp StepsToLinesProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type StrandedProc

type StrandedProc struct {
	Traces []TraceProc
}

StrandedProc takes a path and converts it into a number of parallel paths.

func NewStrandedProc

func NewStrandedProc(n int, w float64) StrandedProc

NewStrandedProc returns a new stranded path processor.

func (StrandedProc) Process

func (sp StrandedProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type StrokeProc

type StrokeProc struct {
	RHSProc      PathProcessor
	LHSProc      PathProcessor
	PostSideProc PathProcessor
	Width        float64 // Default
	// point(pt, r) []part
	PointFunc func([]float64, float64) []Part
	// cap(part1, pt, part2) []part
	CapStartFunc func(Part, []float64, Part) []Part
	CapEndFunc   func(Part, []float64, Part) []Part
}

StrokeProc defines the default width, side path processors and cap types of the stroke.

func NewStrokeProc

func NewStrokeProc(w float64) *StrokeProc

NewStrokeProc creates a trace stroke path processor with width w, the bevel join and butt cap types.

func NewStrokeProcExt

func NewStrokeProcExt(rw, lw float64,
	jf func(Part, []float64, Part) []Part,
	d float64,
	cf func(Part, []float64, Part) []Part) *StrokeProc

NewStrokeProcExt creates a trace stroke path processor where the widths are specified separately for each side of the stroke. This allows the stroke to be offset to the left or right of the path being processed.

func (*StrokeProc) Process

func (sp *StrokeProc) Process(p *Path) []*Path

Process implements the PathProcessor interface and will return either one or two paths depending on whether the path is open or closed.

type TraceProc

type TraceProc struct {
	Width    float64
	Flatten  float64
	JoinFunc func(Part, []float64, Part) []Part
}

TraceProc defines the width and join types of the trace. The gap between two adjacent steps must be greater than MinGap for the join function to be called.

func NewTraceProc

func NewTraceProc(w float64) TraceProc

NewTraceProc creates a trace path processor with width w, the bevel join and butt cap types.

func (TraceProc) Process

func (tp TraceProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

func (TraceProc) ProcessParts

func (tp TraceProc) ProcessParts(p *Path) []Part

ProcessParts returns the processed path as a slice of parts, rather a path so other path processors don't have to round trip path -> parts -> path -> parts (e.g. StrokeProc).

type Transform

type Transform interface {
	Apply(points ...[]float64) [][]float64
}

Transform interface provides the Apply function to transform a set of points.

type TransformProc

type TransformProc struct {
	Transform Transform
}

TransformProc produces a new path by applying the supplied transform to the path.

func (TransformProc) Process

func (tp TransformProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type TriangleWaveProc

type TriangleWaveProc struct {
	HalfLambda float64 // Half wave length
	Scale      float64 // Ratio of amplitude to lambda
	KeepZero   bool    // Keeps internal zero-point crossings if set
	Flip       bool    // Flips the wave phase by 180 (pi) if set
}

TriangleWaveProc applies a triangle wave along a path with a defined wave length and amplitude. The wave starts and ends on zero-crossing points and the last half wave is truncated to the path length remaining. The internal zero-crossing points can be optionally preserved.

func NewTriangleWaveProc

func NewTriangleWaveProc(lambda, amplitude float64) TriangleWaveProc

NewTriangleWaveProc creates a new TriangleWaveProc with the supplied wave length and amplitutde.

func (TriangleWaveProc) Process

func (tp TriangleWaveProc) Process(p *Path) []*Path

Process implements the PathProcessor interface.

type VWTraceProc

type VWTraceProc struct {
	Width   float64                        // Distance from path
	Flatten float64                        // See Path.Flatten
	Func    func(float64, float64) float64 // Func(t, Width) where t [0,1]
}

VWTraceProc path processor creates a variable width trace of a path using a function to determine the length of a line from the point where two path parts meet, at an angle that bisects the angle between the two parts. The line end points are used to create path that will always be open.

func (VWTraceProc) Process

func (s VWTraceProc) Process(p *Path) []*Path

Process implements the path processor interface.

Directories

Path Synopsis
Package color contains types and functions for color management.
Package color contains types and functions for color management.
Package image contains utility functions for reading, writing and converting images.
Package image contains utility functions for reading, writing and converting images.
Package util contains various geometry functions used by the graphics2d package.
Package util contains various geometry functions used by the graphics2d package.

Jump to

Keyboard shortcuts

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