Skip to content

Commit

Permalink
Update Copy semantics for various functions, update examples
Browse files Browse the repository at this point in the history
  • Loading branch information
tdewolff committed Jun 14, 2019
1 parent ccc181e commit b809d4a
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 209 deletions.
46 changes: 23 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ Fonts

Paths

* Support Winding and EvenOdd fill rules
* Simplify polygons using the Ramer-Douglas-Peucker algorithm
* Intersection function between line, Bézier and ellipse and between themselves (for path merge, overlap/mask, clipping, etc.)
* Implement Bentley-Ottmann algorithm to find all line intersections (clipping)
Expand Down Expand Up @@ -177,33 +176,34 @@ p.ToPS() string // to PostScript
These paths can be manipulated and transformed with the following commands. Each will return a pointer to the path.

``` go
p.Copy()
p.Append(q *Path) // append path q to p
p.Join(q *Path) // join path q to p
p.Reverse() // reverse the direction of the path
p.Split() // split the path segments, ie. at Close/MoveTo
p.SplitAt(d ...float64) // split the path at certain lengths d

p.Transform(Matrix) // multiple transformations at once
p.Translate(x, y float64)

p.Flatten() // flatten Bézier and arc commands to straight lines
p.Offset(width float64) // offset the path outwards (width > 0) or inwards (width < 0), depends on FillRule
p.Stroke(width float64, capper Capper, joiner Joiner) // create a stroke from a path of certain width, using capper and joiner for caps and joins
p.Dash(offset float64, d ...float64) // create dashed path with lengths d which are alternating the dash and the space, start at an offset into the given pattern (can be negative)

p.Optimize() // optimize and shorten path
p = p.Copy()
p = p.Append(q *Path) // append path q to p
p = p.Join(q *Path) // join path q to p
p = p.Reverse() // reverse the direction of the path
ps = p.Split() []*Path // split the path segments, ie. at Close/MoveTo
ps = p.SplitAt(d ...float64) []*Path // split the path at certain lengths d

p = p.Transform(Matrix) // multiple transformations at once
p = p.Translate(x, y float64)

p = p.Flatten() // flatten Bézier and arc commands to straight lines
p = p.Offset(width float64) // offset the path outwards (width > 0) or inwards (width < 0), depends on FillRule
p = p.Stroke(width float64, capper Capper, joiner Joiner) // create a stroke from a path of certain width, using capper and joiner for caps and joins
p = p.Dash(offset float64, d ...float64) // create dashed path with lengths d which are alternating the dash and the space, start at an offset into the given pattern (can be negative)

p = p.Optimize() // optimize and shorten path
```

### Polygon paths
Some operations on paths only work when it consists of line elements only. We can either flatten an existing path or use the command coordinates only in creating a polygon path.
### Polylines
Some operations on paths only work when it consists of line elements only. We can either flatten an existing path or use the command coordinates to create a polyline.

``` go
pp := p.ToPolygon() // create by flattening p
pp = p.ToPolygonCoords() // create from the command coordinates of p
polyline := PolylineFromPath(p) // create by flattening p
polyline = PolylineFromPathCoords(p) // create from the command coordinates of p

pp.Smoothen() // smoothen it by cubic Béziers
pp.Interior(x, y float64) // TODO: replace code in Path in Filling
polyline.Smoothen() // smoothen it by cubic Béziers
polyline.FillCount() int // returns the fill count as dictated by the FillRule
polyline.Interior(x, y float64) // returns true if (x,y) is in the interior of the polyline
```


Expand Down
8 changes: 4 additions & 4 deletions canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (l pathLayer) WriteSVG(w io.Writer, h float64) {
fill := l.fillColor.A != 0
stroke := l.strokeColor.A != 0 && 0.0 < l.strokeWidth

p := l.path.Transform(Identity.ReflectYAt(h / 2.0))
p := l.path.Transform(Identity.Translate(0.0, h).ReflectY())
fmt.Fprintf(w, `<path d="%s`, p.ToSVG())

strokeUnsupported := false
Expand Down Expand Up @@ -422,7 +422,7 @@ func (l textLayer) WritePDF(w *PDFPageWriter) {
// TODO: PDF write text
paths, colors := l.ToPaths()
for i, path := range paths {
path.Transform(Identity.Rotate(l.rot).Translate(l.x, l.y))
path = path.Transform(Identity.Translate(l.x, l.y).Rotate(l.rot))
state := defaultDrawState
state.fillColor = colors[i]
pathLayer{path, state}.WritePDF(w)
Expand All @@ -433,7 +433,7 @@ func (l textLayer) WriteEPS(w *EPSWriter) {
// TODO: EPS write text
paths, colors := l.ToPaths()
for i, path := range paths {
path.Transform(Identity.Rotate(l.rot).Translate(l.x, l.y))
path = path.Transform(Identity.Translate(l.x, l.y).Rotate(l.rot))
state := defaultDrawState
state.fillColor = colors[i]
pathLayer{path, state}.WriteEPS(w)
Expand All @@ -443,7 +443,7 @@ func (l textLayer) WriteEPS(w *EPSWriter) {
func (l textLayer) WriteImage(img *image.RGBA, dpm, w, h float64) {
paths, colors := l.ToPaths()
for i, path := range paths {
path.Transform(Identity.Rotate(l.rot).Translate(l.x, l.y))
path = path.Transform(Identity.Translate(l.x, l.y).Rotate(l.rot))
state := defaultDrawState
state.fillColor = colors[i]
pathLayer{path, state}.WriteImage(img, dpm, w, h)
Expand Down
38 changes: 19 additions & 19 deletions example/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func Draw(c *canvas.C) {
if err != nil {
panic(err)
}
latex.Rotate(-30, 0, 0)
latex = latex.Transform(canvas.Identity.Rotate(-30))
c.SetFillColor(canvas.Black)
c.DrawPath(140, 65, latex)

Expand All @@ -144,27 +144,27 @@ func Draw(c *canvas.C) {
c.DrawPath(110, 40, ellipse)
c.SetStrokeColor(canvas.Transparent)

pp := canvas.PolygonPath{&canvas.Path{}}
pp.LineTo(20.0, 0.0)
pp.LineTo(20.0, 10.0)
pp.LineTo(0.0, 20.0)
pp.Close()
p = pp.Smoothen()
polyline := &canvas.Polyline{}
polyline.Add(0.0, 0.0)
polyline.Add(20.0, 0.0)
polyline.Add(20.0, 10.0)
polyline.Add(0.0, 20.0)
polyline.Add(0.0, 0.0)
c.SetFillColor(canvas.Seagreen)
c.DrawPath(170, 10, p)
c.DrawPath(170, 10, polyline.Smoothen())
c.SetFillColor(canvas.Black)
c.DrawPath(170, 10, pp.Stroke(0.25, canvas.RoundCapper, canvas.RoundJoiner))

pp = canvas.PolygonPath{&canvas.Path{}}
pp.LineTo(10.0, 5.0)
pp.LineTo(20.0, 15.0)
pp.LineTo(30.0, 20.0)
pp.LineTo(40.0, 10.0)
p = pp.Smoothen()
c.DrawPath(170, 10, polyline.ToPath().Stroke(0.25, canvas.RoundCapper, canvas.RoundJoiner))

polyline = &canvas.Polyline{}
polyline.Add(0.0, 0.0)
polyline.Add(10.0, 5.0)
polyline.Add(20.0, 15.0)
polyline.Add(30.0, 20.0)
polyline.Add(40.0, 10.0)
c.SetFillColor(canvas.Seagreen)
c.DrawPath(120, 5, p.Stroke(0.5, canvas.RoundCapper, canvas.RoundJoiner))
c.DrawPath(120, 5, polyline.Smoothen().Stroke(0.5, canvas.RoundCapper, canvas.RoundJoiner))
c.SetFillColor(canvas.Black)
for _, coord := range pp.Coords() {
c.DrawPath(120, 5, canvas.Circle(coord.X, coord.Y, 1.0).Stroke(0.5, canvas.RoundCapper, canvas.RoundJoiner))
for _, coord := range polyline.Coords() {
c.DrawPath(120, 5, canvas.Circle(1.0).Translate(coord.X, coord.Y).Stroke(0.5, canvas.RoundCapper, canvas.RoundJoiner))
}
}
5 changes: 3 additions & 2 deletions example/stroke_example.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"image/color"
"image/png"
"math"
"os"

"github.com/tdewolff/canvas"
Expand Down Expand Up @@ -74,8 +75,8 @@ func Draw(c *canvas.C) {
drawStrokedPath(c, 80.0, 25.0, pathJoiner, canvas.ButtCapper, canvas.BevelJoiner)

drawText(c, 123.0, 42.0, "MiterJoiner")
drawStrokedPath(c, 130.0, 25.0, pathJoiner, canvas.ButtCapper, canvas.MiterJoiner)
drawStrokedPath(c, 130.0, 25.0, pathJoiner, canvas.ButtCapper, canvas.MiterClipJoiner(canvas.BevelJoiner, math.NaN()))

drawText(c, 173.0, 42.0, "ArcsJoiner")
drawStrokedPath(c, 180.0, 25.0, pathJoiner, canvas.ButtCapper, canvas.ArcsJoiner)
drawStrokedPath(c, 180.0, 25.0, pathJoiner, canvas.ButtCapper, canvas.ArcsClipJoiner(canvas.BevelJoiner, math.NaN()))
}
Binary file modified example/stroke_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion example/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func Draw(c *canvas.C) {
//c.DrawPath(40.0, 10.0, p)

//c.SetFillColor(canvas.Transparent)
f := p.Copy().Flatten()
f := p.Flatten()
p = p.Stroke(1.0, canvas.ButtCapper, canvas.MiterJoiner)
f = f.Stroke(1.0, canvas.ButtCapper, canvas.MiterJoiner)
//c.SetDashes(0.0)
Expand Down
5 changes: 3 additions & 2 deletions font.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func (ff FontFace) Decorate(width float64) *Path {
p := &Path{}
if ff.decoration != nil {
for _, deco := range *ff.decoration {
p.Append(deco.Decorate(ff, width))
p = p.Append(deco.Decorate(ff, width))
}
}
return p
Expand Down Expand Up @@ -344,6 +344,7 @@ func (ff FontFace) Kerning(rPrev, rNext rune) float64 {

////////////////////////////////////////////////////////////////

// TODO: use similar system as stroke cappers and joiners
type FontDecoratorFunc func(FontFace, float64) *Path

func (f FontDecoratorFunc) Decorate(ff FontFace, w float64) *Path {
Expand Down Expand Up @@ -416,7 +417,7 @@ var DottedUnderline = FontDecoratorFunc(func(ff FontFace, w float64) *Path {

p := &Path{}
for i := 0; i < n; i++ {
p.Append(Circle(r).Translate(r+float64(i)*d, y))
p = p.Append(Circle(r).Translate(r+float64(i)*d, y))
}
return p
})
Expand Down
104 changes: 3 additions & 101 deletions latex.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func ParseLaTeX(s string) (*Path, error) {
}
if !p.Empty() {
_ = width
p = p.Transform(Identity.ReflectY().Translate(-x0, y0+height))
p = p.Transform(Identity.Translate(-x0, y0+height).ReflectY())
}
_ = ioutil.WriteFile(path.Join(tmpDir, hash), []byte(p.String()), 0644)
return p, nil
Expand Down Expand Up @@ -163,7 +163,7 @@ func ParseLaTeX(s string) (*Path, error) {
return nil, errors.New("unexpected SVG format: xlink:href does not point to existing path")
}

p.Append(svgPath.Translate(x, y))
p = p.Append(svgPath.Translate(x, y))
x0 = math.Min(x0, x)
y0 = math.Min(y0, y)
} else if tag == "rect" {
Expand All @@ -186,109 +186,11 @@ func ParseLaTeX(s string) (*Path, error) {
if n == 0 {
return nil, errors.New("unexpected SVG format: expected valid height attribute on rect tag")
}
p.Append(Rectangle(x, y, w, h))
p = p.Append(Rectangle(x, y, w, h))
x0 = math.Min(x0, x)
y0 = math.Min(y0, y)
}
}
}
return nil, nil
}

// func valueKind(vk pdf.ValueKind) string {
// switch vk {
// case pdf.Null:
// return "Null"
// case pdf.Bool:
// return "Bool"
// case pdf.Integer:
// return "Integer"
// case pdf.Real:
// return "Real"
// case pdf.String:
// return "String"
// case pdf.Name:
// return "Name"
// case pdf.Dict:
// return "Dict"
// case pdf.Array:
// return "Array"
// case pdf.Stream:
// return "Stream"
// }
// return "?"
// }
//
// func printValue(indent, key string, v pdf.Value) {
// fmt.Println(indent, key+": ", valueKind(v.Kind()), v)
// if v.Kind() == pdf.Dict || v.Kind() == pdf.Stream {
// for _, key := range v.Keys() {
// printValue(indent+" ", key, v.Key(key))
// }
// }
// if v.Kind() == pdf.Stream {
// s, _ := ioutil.ReadAll(v.Reader())
// fmt.Println(indent+" stream", len(s))
//
// }
// }
//
// func parseLaTeX2(s string) (*Path, error) {
// document := `\documentclass{article}
// \begin{document}
// \thispagestyle{empty}
// $` + s + `$
// \end{document}`
//
// tmpDir := "."
// //tmpDir, err := ioutil.TempDir("", "tdewolff-")
// //if err != nil {
// // return nil, err
// //}
// //defer os.RemoveAll(tmpDir)
//
// stdout := &bytes.Buffer{}
//
// cmd := exec.Command("pdflatex", "-jobname=canvas", "-halt-on-error")
// cmd.Dir = tmpDir
// cmd.Stdin = strings.NewReader(document)
// cmd.Stdout = stdout
// if err := cmd.Start(); err != nil {
// fmt.Println(stdout.String())
// return nil, err
// }
// if err := cmd.Wait(); err != nil {
// fmt.Println(stdout.String())
// return nil, err
// }
//
// r, err := pdf.Open(path.Join(tmpDir, "canvas.pdf"))
// if err != nil {
// return nil, err
// }
//
// if r.NumPage() != 1 {
// return nil, fmt.Errorf("bad PDF")
// }
//
// content := r.Page(1).Content()
// res := r.Page(1).Resources()
// if res.Kind() != pdf.Dict {
// return nil, fmt.Errorf("bad PDF")
// }
//
// fmt.Println("Content:", content.Text)
// fmt.Println(content.Text[0])
// printValue("", "", res)
//
// b, err := ioutil.ReadAll(res.Key("Font").Key("F11").Key("FontDescriptor").Key("FontFile").Reader())
// fmt.Println(err)
// font, err := conradSFNT.Parse(bytes.NewReader(b))
// fmt.Println(font, err)
//
// f, err := os.Open("cmmi10.pfb")
// fmt.Println(err)
// font, err = conradSFNT.Parse(f)
// fmt.Println(font, err)
// return nil, nil
// }
Loading

0 comments on commit b809d4a

Please sign in to comment.