Documentation
¶
Overview ¶
Package draw provides a high-level fluent API for constructing MetaPost-style paths.
This package wraps the low-level mp package with a builder pattern that makes path construction more intuitive and Go-idiomatic. It also provides equation solving for geometric constraints.
Path Building ¶
The PathBuilder provides a fluent interface for constructing paths:
path, err := draw.NewPath().
MoveTo(mp.P(0, 0)).
CurveTo(mp.P(50, 30)).
CurveTo(mp.P(100, 0)).
WithStrokeColor(mp.ColorCSS("blue")).
Solve()
Curve Types ¶
Different curve segment types are supported:
CurveTo(pt) // Smooth curve (Hobby algorithm) LineTo(pt) // Straight line segment Controls(c1, c2, pt) // Explicit Bézier control points
Direction and Tension ¶
Control the curve shape with directions and tensions:
draw.NewPath().
MoveTo(mp.P(0, 0)).
WithOutDir(45). // Leave at 45°
CurveTo(mp.P(100, 0)).
WithInDir(-45). // Arrive at -45°
Solve()
draw.NewPath().
MoveTo(mp.P(0, 0)).
WithTension(2). // Tighter curve
CurveTo(mp.P(100, 0)).
Solve()
Closed Paths ¶
Create closed paths with PathBuilder.Close:
triangle, _ := draw.NewPath().
MoveTo(mp.P(0, 0)).
LineTo(mp.P(100, 0)).
LineTo(mp.P(50, 86)).
Close().
Solve()
Styling ¶
Apply stroke, fill, and other styling:
draw.NewPath().
MoveTo(mp.P(0, 0)).
CurveTo(mp.P(100, 0)).
WithStrokeColor(mp.ColorCSS("red")).
WithFillColor(mp.ColorCSS("yellow")).
WithStrokeWidth(2.0).
WithDash([]float64{5, 3}, 0).
WithArrowEnd().
Solve()
Pictures ¶
The Picture type collects multiple paths and labels, similar to MetaPost's picture:
pic := draw.NewPicture()
pic.AddPath(path1)
pic.AddPath(path2)
pic.Label("A", mp.P(0, 0), mp.AnchorLowerLeft)
pic.DotLabel("B", mp.P(100, 0), mp.AnchorRight, mp.ColorCSS("blue"))
Label Conversion ¶
Labels can be converted to glyph paths using the font package:
import "github.com/boxesandglue/mpgo/font" face, _ := font.Load(fontFile) pic.ConvertLabelsToPathsWithFont(face)
Equation Solving ¶
The Context type provides linear equation solving for geometric constraints:
ctx := draw.NewContext() z0 := ctx.Known(0, 0) // Fixed point z1 := ctx.Unknown() // Unknown point z2 := ctx.Known(100, 100) // Fixed point ctx.Collinear(z1, z0, z2) // z1 lies on line z0--z2 ctx.EqX(z1, 50) // z1.x = 50 ctx.Solve() fmt.Println(z1.XY()) // (50, 50)
Variables can be used in path building:
path, _ := draw.NewPath().
WithContext(ctx).
MoveToVar(z0).
CurveToVar(z1).
CurveToVar(z2).
Solve()
Transformations ¶
Apply transformations to paths:
draw.NewPath().
MoveTo(mp.P(0, 0)).
LineTo(mp.P(10, 0)).
Scaled(5).
Rotated(45).
Shifted(100, 100).
Solve()
Index ¶
- type Context
- func (c *Context) Between(p, a, b *Var, t float64)
- func (c *Context) BetweenAt(a, b *Var, t float64) *Var
- func (c *Context) Collinear(p, a, b *Var)
- func (c *Context) Diff(result, a, b *Var)
- func (c *Context) Eq(v *Var, p mp.Point)
- func (c *Context) EqVar(a, b *Var)
- func (c *Context) EqVarX(a, b *Var)
- func (c *Context) EqVarY(a, b *Var)
- func (c *Context) EqX(v *Var, x float64)
- func (c *Context) EqY(v *Var, y float64)
- func (c *Context) Intersection(p, a1, a2, b1, b2 *Var) error
- func (c *Context) IntersectionOf(a1, a2, b1, b2 *Var) (*Var, error)
- func (c *Context) Known(x, y float64) *Var
- func (c *Context) LinearXY(v *Var, cx, cy, constant float64)
- func (c *Context) MidPoint(m, a, b *Var)
- func (c *Context) MidPointOf(a, b *Var) *Var
- func (c *Context) NewPath() *PathBuilder
- func (c *Context) Point() *Var
- func (c *Context) Points(n int) []*Var
- func (c *Context) Scaled(result, v *Var, t float64)
- func (c *Context) Solve() error
- func (c *Context) Sum(result, a, b *Var)
- func (c *Context) Unknown() *Var
- type PathBuilder
- func (p *PathBuilder) BuildPath() *mp.Path
- func (p *PathBuilder) Close() *PathBuilder
- func (p *PathBuilder) CurveTo(pt mp.Point) *PathBuilder
- func (p *PathBuilder) CurveToDir(pt mp.Point, outDeg, inDeg float64) *PathBuilder
- func (p *PathBuilder) CurveToVar(v *Var) *PathBuilder
- func (p *PathBuilder) CurveToWithControls(pt mp.Point, c1, c2 mp.Point) *PathBuilder
- func (p *PathBuilder) Dashed(onOff ...float64) *PathBuilder
- func (p *PathBuilder) DashedEvenly() *PathBuilder
- func (p *PathBuilder) DashedWithDots() *PathBuilder
- func (p *PathBuilder) LineTo(pt mp.Point) *PathBuilder
- func (p *PathBuilder) LineToVar(v *Var) *PathBuilder
- func (p *PathBuilder) MoveTo(pt mp.Point) *PathBuilder
- func (p *PathBuilder) MoveToVar(v *Var) *PathBuilder
- func (p *PathBuilder) ReflectedAbout(x1, y1, x2, y2 float64) *PathBuilder
- func (p *PathBuilder) Rotated(angleDeg float64) *PathBuilder
- func (p *PathBuilder) RotatedAround(cx, cy, angleDeg float64) *PathBuilder
- func (p *PathBuilder) Scaled(s float64) *PathBuilder
- func (p *PathBuilder) ScaledAround(cx, cy, s float64) *PathBuilder
- func (p *PathBuilder) Shifted(dx, dy float64) *PathBuilder
- func (p *PathBuilder) Slanted(s float64) *PathBuilder
- func (p *PathBuilder) Solve() (*mp.Path, error)
- func (p *PathBuilder) SolveWithEngine(e *mp.Engine) (*mp.Path, error)
- func (p *PathBuilder) Transformed(t mp.Transform) *PathBuilder
- func (p *PathBuilder) WithArrow() *PathBuilder
- func (p *PathBuilder) WithArrowStyle(length, angle float64) *PathBuilder
- func (p *PathBuilder) WithContext(ctx *Context) *PathBuilder
- func (p *PathBuilder) WithCurl(c float64) *PathBuilder
- func (p *PathBuilder) WithDashPattern(d *mp.DashPattern) *PathBuilder
- func (p *PathBuilder) WithDirection(deg float64) *PathBuilder
- func (p *PathBuilder) WithDoubleArrow() *PathBuilder
- func (p *PathBuilder) WithFill(c mp.Color) *PathBuilder
- func (p *PathBuilder) WithIncomingCurl(c float64) *PathBuilder
- func (p *PathBuilder) WithIncomingDirection(deg float64) *PathBuilder
- func (p *PathBuilder) WithIncomingTension(t float64) *PathBuilder
- func (p *PathBuilder) WithLineCap(cap int) *PathBuilder
- func (p *PathBuilder) WithLineJoin(join int) *PathBuilder
- func (p *PathBuilder) WithOutgoingCurl(c float64) *PathBuilder
- func (p *PathBuilder) WithOutgoingTension(t float64) *PathBuilder
- func (p *PathBuilder) WithPen(pen *mp.Pen) *PathBuilder
- func (p *PathBuilder) WithStrokeColor(c mp.Color) *PathBuilder
- func (p *PathBuilder) WithStrokeWidth(w float64) *PathBuilder
- func (p *PathBuilder) WithTension(t float64) *PathBuilder
- func (p *PathBuilder) WithTensionAtLeast(t float64) *PathBuilder
- func (p *PathBuilder) WithTensionInfinity() *PathBuilder
- func (p *PathBuilder) XScaled(s float64) *PathBuilder
- func (p *PathBuilder) YScaled(s float64) *PathBuilder
- func (p *PathBuilder) ZScaled(a, b float64) *PathBuilder
- type Picture
- func (p *Picture) AddLabel(label *mp.Label) *Picture
- func (p *Picture) AddPath(path *mp.Path) *Picture
- func (p *Picture) AddPicture(other *Picture) *Picture
- func (p *Picture) Clip(clipPath *mp.Path) *Picture
- func (p *Picture) ClipPath() *mp.Path
- func (p *Picture) ConvertLabelsToPathsWithFont(f mp.FontRenderer) error
- func (p *Picture) DotLabel(text string, pos mp.Point, anchor mp.Anchor, color mp.Color) *Picture
- func (p *Picture) Label(text string, pos mp.Point, anchor mp.Anchor) *Picture
- func (p *Picture) LabelWithStyle(text string, pos mp.Point, anchor mp.Anchor) *mp.Label
- func (p *Picture) Labels() []*mp.Label
- func (p *Picture) Paths() []*mp.Path
- type Point
- type Var
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
Context provides an equation solver for geometric constraints. It tracks variables (points) that can be known or unknown, and solves linear equations to determine unknown values.
Usage:
ctx := draw.NewContext() z0 := ctx.Known(0, 0) z1 := ctx.Unknown() z2 := ctx.Known(100, 100) ctx.Collinear(z1, z0, z2) // z1 on line z0--z2 ctx.EqX(z1, 50) // z1.x = 50 ctx.Solve() fmt.Println(z1.XY()) // (50, 50)
func (*Context) Between ¶
Between constrains p to lie at parameter t on the line from a to b. Equivalent to: p = t[a, b] = (1-t)*a + t*b
func (*Context) Collinear ¶
Collinear constrains p to lie on the line through a and b. This adds the constraint that p, a, b are collinear, but doesn't determine WHERE on the line p is - you need another constraint for that.
func (*Context) Intersection ¶
Intersection constrains p to be the intersection of line a1-a2 and line b1-b2. All of a1, a2, b1, b2 must be known.
func (*Context) IntersectionOf ¶
IntersectionOf returns a new variable at the intersection of lines a1-a2 and b1-b2.
func (*Context) LinearXY ¶ added in v0.1.5
LinearXY adds the constraint: cx*v.x + cy*v.y = constant. This allows mixing x and y coordinates in a single equation, e.g. LinearXY(z, 1, 1, 79.2) encodes x+y = 79.2 (MetaPost: x3+y3=1.1in).
func (*Context) MidPoint ¶
MidPoint constrains m to be the midpoint of a and b. Equivalent to: m = 0.5[a, b]
func (*Context) MidPointOf ¶
MidPointOf returns a new variable constrained to be the midpoint of a and b.
func (*Context) NewPath ¶
func (c *Context) NewPath() *PathBuilder
NewPath creates a PathBuilder linked to this context. Use MoveToVar/CurveToVar/LineToVar to reference context variables. Call Solve() before building the path to resolve all variables.
func (*Context) Solve ¶
Solve solves the system of equations and updates all variables. Returns an error if the system is unsolvable or underdetermined.
type PathBuilder ¶
type PathBuilder struct {
// contains filtered or unexported fields
}
PathBuilder constructs a path using outgoing/incoming directions and delegates solving to mp.Engine.
func NewPath ¶
func NewPath() *PathBuilder
func (*PathBuilder) BuildPath ¶
func (p *PathBuilder) BuildPath() *mp.Path
BuildPath constructs an mp.Path with knots configured for given directions. Directions are converted to MetaPost's scaled degrees. If the path uses context variables, the context must be solved first.
func (*PathBuilder) Close ¶
func (p *PathBuilder) Close() *PathBuilder
Close marks the path as cyclic; use the currently set outDir/inDir as the outgoing/incoming directions for the closing segment back to the start.
func (*PathBuilder) CurveTo ¶
func (p *PathBuilder) CurveTo(pt mp.Point) *PathBuilder
CurveTo adds a segment to pt with the stored directions.
func (*PathBuilder) CurveToDir ¶
func (p *PathBuilder) CurveToDir(pt mp.Point, outDeg, inDeg float64) *PathBuilder
CurveToDir adds a segment and sets outgoing/incoming directions just for this segment.
func (*PathBuilder) CurveToVar ¶
func (p *PathBuilder) CurveToVar(v *Var) *PathBuilder
CurveToVar adds a curve segment to a context variable.
func (*PathBuilder) CurveToWithControls ¶
func (p *PathBuilder) CurveToWithControls(pt mp.Point, c1, c2 mp.Point) *PathBuilder
CurveToWithControls adds a segment with explicit control points (skips solving).
func (*PathBuilder) Dashed ¶
func (p *PathBuilder) Dashed(onOff ...float64) *PathBuilder
Dashed sets a custom dash pattern. The pattern is given as alternating on/off lengths: on1, off1, on2, off2, ... Example: Dashed(6, 3) creates "on 6 off 3" (long dashes with short gaps)
func (*PathBuilder) DashedEvenly ¶
func (p *PathBuilder) DashedEvenly() *PathBuilder
DashedEvenly sets the standard "evenly" dash pattern (on 3 off 3). This mirrors MetaPost's "dashed evenly" from plain.mp.
func (*PathBuilder) DashedWithDots ¶
func (p *PathBuilder) DashedWithDots() *PathBuilder
DashedWithDots sets the "withdots" dash pattern. This creates dots when used with round linecap. Mirrors MetaPost's "dashed withdots" from plain.mp.
func (*PathBuilder) LineTo ¶
func (p *PathBuilder) LineTo(pt mp.Point) *PathBuilder
LineTo adds a straight segment (MetaPost "--", i.e., {curl 1}..{curl 1}) to (x,y).
func (*PathBuilder) LineToVar ¶
func (p *PathBuilder) LineToVar(v *Var) *PathBuilder
LineToVar adds a straight segment to a context variable.
func (*PathBuilder) MoveTo ¶
func (p *PathBuilder) MoveTo(pt mp.Point) *PathBuilder
func (*PathBuilder) MoveToVar ¶
func (p *PathBuilder) MoveToVar(v *Var) *PathBuilder
MoveToVar sets the start point from a context variable. The variable must be resolved (via ctx.Solve()) before BuildPath is called.
func (*PathBuilder) ReflectedAbout ¶
func (p *PathBuilder) ReflectedAbout(x1, y1, x2, y2 float64) *PathBuilder
ReflectedAbout adds a reflection about the line through (x1,y1) and (x2,y2). Mirrors MetaPost's "reflectedabout(z1, z2)".
func (*PathBuilder) Rotated ¶
func (p *PathBuilder) Rotated(angleDeg float64) *PathBuilder
Rotated adds a rotation transformation around the origin. Angle is in degrees (positive = counter-clockwise). Mirrors MetaPost's "path rotated angle".
func (*PathBuilder) RotatedAround ¶
func (p *PathBuilder) RotatedAround(cx, cy, angleDeg float64) *PathBuilder
RotatedAround adds a rotation around a given point. Equivalent to: shifted(-cx,-cy) rotated(angle) shifted(cx,cy)
func (*PathBuilder) Scaled ¶
func (p *PathBuilder) Scaled(s float64) *PathBuilder
Scaled adds a uniform scaling transformation around the origin. Mirrors MetaPost's "path scaled s".
func (*PathBuilder) ScaledAround ¶
func (p *PathBuilder) ScaledAround(cx, cy, s float64) *PathBuilder
ScaledAround adds a scaling around a given point. Equivalent to: shifted(-cx,-cy) scaled(s) shifted(cx,cy)
func (*PathBuilder) Shifted ¶
func (p *PathBuilder) Shifted(dx, dy float64) *PathBuilder
Shifted adds a translation transformation to be applied after solving. Mirrors MetaPost's "path shifted (dx, dy)".
func (*PathBuilder) Slanted ¶
func (p *PathBuilder) Slanted(s float64) *PathBuilder
Slanted adds a horizontal shear transformation. Mirrors MetaPost's "path slanted s".
func (*PathBuilder) Solve ¶
func (p *PathBuilder) Solve() (*mp.Path, error)
Solve builds the path, solves it with a new engine, and applies any pending transformations. For better performance when solving many paths, use SolveWithEngine to reuse an engine.
func (*PathBuilder) SolveWithEngine ¶
SolveWithEngine appends the built path to the engine, runs Solve, and applies any pending transformations.
func (*PathBuilder) Transformed ¶
func (p *PathBuilder) Transformed(t mp.Transform) *PathBuilder
Transformed adds a custom transformation.
func (*PathBuilder) WithArrow ¶
func (p *PathBuilder) WithArrow() *PathBuilder
WithArrow adds an arrowhead at the end of the path (like drawarrow).
func (*PathBuilder) WithArrowStyle ¶
func (p *PathBuilder) WithArrowStyle(length, angle float64) *PathBuilder
WithArrowStyle sets custom arrow head dimensions. length is the arrow head length (default 4), angle is the head angle in degrees (default 45).
func (*PathBuilder) WithContext ¶
func (p *PathBuilder) WithContext(ctx *Context) *PathBuilder
WithContext links this path builder to an equation context. Variables used in MoveToVar/CurveToVar/LineToVar will be resolved when the context is solved.
func (*PathBuilder) WithCurl ¶
func (p *PathBuilder) WithCurl(c float64) *PathBuilder
WithCurl sets both outgoing and incoming curl for the next segment.
func (*PathBuilder) WithDashPattern ¶
func (p *PathBuilder) WithDashPattern(d *mp.DashPattern) *PathBuilder
WithDashPattern sets a pre-created dash pattern. Use this for patterns created with mp.NewDashPattern() or mp.DashEvenly().Scaled(2), etc.
func (*PathBuilder) WithDirection ¶
func (p *PathBuilder) WithDirection(deg float64) *PathBuilder
WithDirection sets the outgoing direction in degrees for the next segment.
func (*PathBuilder) WithDoubleArrow ¶
func (p *PathBuilder) WithDoubleArrow() *PathBuilder
WithDoubleArrow adds arrowheads at both ends of the path (like drawdblarrow).
func (*PathBuilder) WithFill ¶
func (p *PathBuilder) WithFill(c mp.Color) *PathBuilder
WithFill sets a fill color for this path.
func (*PathBuilder) WithIncomingCurl ¶
func (p *PathBuilder) WithIncomingCurl(c float64) *PathBuilder
WithIncomingCurl sets incoming curl for the next segment.
func (*PathBuilder) WithIncomingDirection ¶
func (p *PathBuilder) WithIncomingDirection(deg float64) *PathBuilder
WithIncomingDirection sets the incoming direction in degrees for the next segment.
func (*PathBuilder) WithIncomingTension ¶
func (p *PathBuilder) WithIncomingTension(t float64) *PathBuilder
WithIncomingTension sets incoming tension for the next segment.
func (*PathBuilder) WithLineCap ¶
func (p *PathBuilder) WithLineCap(cap int) *PathBuilder
WithLineCap sets the line cap style for endpoints. Use mp.LineCapButt (1), mp.LineCapRounded (2), or mp.LineCapSquared (3). 0 means default (rounded).
func (*PathBuilder) WithLineJoin ¶
func (p *PathBuilder) WithLineJoin(join int) *PathBuilder
WithLineJoin sets the line join style for corners. Use mp.LineJoinMiter (0), mp.LineJoinRound (1), or mp.LineJoinBevel (2).
func (*PathBuilder) WithOutgoingCurl ¶
func (p *PathBuilder) WithOutgoingCurl(c float64) *PathBuilder
WithOutgoingCurl sets outgoing curl for the next segment.
func (*PathBuilder) WithOutgoingTension ¶
func (p *PathBuilder) WithOutgoingTension(t float64) *PathBuilder
WithOutgoingTension sets outgoing tension for the next segment.
func (*PathBuilder) WithPen ¶
func (p *PathBuilder) WithPen(pen *mp.Pen) *PathBuilder
WithPen attaches a pen to this path style (mirrors pen_p in mp.c:564).
func (*PathBuilder) WithStrokeColor ¶
func (p *PathBuilder) WithStrokeColor(c mp.Color) *PathBuilder
WithStrokeColor stores a stroke color for this path (MetaPost: withcolor).
func (*PathBuilder) WithStrokeWidth ¶
func (p *PathBuilder) WithStrokeWidth(w float64) *PathBuilder
WithStrokeWidth sets the stroke width for this path.
func (*PathBuilder) WithTension ¶
func (p *PathBuilder) WithTension(t float64) *PathBuilder
WithTension sets both outgoing and incoming tension for the next segment.
func (*PathBuilder) WithTensionAtLeast ¶
func (p *PathBuilder) WithTensionAtLeast(t float64) *PathBuilder
WithTensionAtLeast mirrors MetaPost's "tension atleast t"; currently treated by storing a negative tension to signal "atleast" to the solver (mp.c uses negative values for the flag).
func (*PathBuilder) WithTensionInfinity ¶
func (p *PathBuilder) WithTensionInfinity() *PathBuilder
WithTensionInfinity mirrors MetaPost's "tension infinity"; mapped to a large tension value.
func (*PathBuilder) XScaled ¶
func (p *PathBuilder) XScaled(s float64) *PathBuilder
XScaled adds a horizontal scaling transformation. Mirrors MetaPost's "path xscaled s".
func (*PathBuilder) YScaled ¶
func (p *PathBuilder) YScaled(s float64) *PathBuilder
YScaled adds a vertical scaling transformation. Mirrors MetaPost's "path yscaled s".
func (*PathBuilder) ZScaled ¶
func (p *PathBuilder) ZScaled(a, b float64) *PathBuilder
ZScaled adds a scaling+rotation using complex multiplication. Mirrors MetaPost's "path zscaled (a, b)".
type Picture ¶
type Picture struct {
// contains filtered or unexported fields
}
Picture mirrors MetaPost's picture container: it collects solved paths that can be drawn together. Tracks are stored as-is (no copying) similar to how MetaPost chains edge objects into a picture (mp.c around mp_make_dashes/export_dashes).
func (*Picture) AddPicture ¶
AddPicture appends all paths from another picture (no copies; mirrors MetaPost's picture addition semantics where edges are shared until output).
func (*Picture) Clip ¶
Clip sets the clipping path for this picture. Mirrors MetaPost's "clip p to q" where q is the clipping boundary. All paths in the picture will be clipped to this boundary when rendered.
func (*Picture) ConvertLabelsToPathsWithFont ¶ added in v0.1.2
func (p *Picture) ConvertLabelsToPathsWithFont(f mp.FontRenderer) error
ConvertLabelsToPathsWithFont converts all labels to glyph outline paths using the provided font. The converted paths are added to the picture's path list, and the labels are cleared. This mirrors MetaPost's behavior where text becomes a picture with glyph paths.
The font parameter must implement mp.FontRenderer. Use the font package:
import "github.com/boxesandglue/mpgo/font" face, _ := font.Load(fontReader) pic.ConvertLabelsToPathsWithFont(face)
func (*Picture) DotLabel ¶ added in v0.1.2
DotLabel adds a text label with a dot at the reference point. Mirrors MetaPost's dotlabel@#(s, z) command.
Example:
pic.DotLabel("$z_0$", z0, mp.AnchorLowerRight) // dotlabel.lrt("$z_0$", z0)
func (*Picture) Label ¶ added in v0.1.2
Label adds a text label to the picture at the given position. Mirrors MetaPost's label@#(s, z) command.
Example:
pic.Label("A", mp.P(0, 0), mp.AnchorTop) // label.top("A", origin)
pic.Label("B", mp.P(100, 0), mp.AnchorRight) // label.rt("B", z1)
func (*Picture) LabelWithStyle ¶ added in v0.1.2
LabelWithStyle adds a styled text label to the picture. Returns the created label for further customization.
type Var ¶
type Var struct {
// contains filtered or unexported fields
}
Var represents a point variable with x and y components. Components can be known (fixed value) or unknown (to be solved).