move flattening code in draw2dbase
This commit is contained in:
parent
ee83fedb10
commit
82ef300f1d
13 changed files with 472 additions and 179 deletions
161
draw2dbase/curve.go
Normal file
161
draw2dbase/curve.go
Normal file
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 17/05/2011 by Laurent Le Goff
|
||||
|
||||
package draw2dbase
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
CurveRecursionLimit = 32
|
||||
)
|
||||
|
||||
// Cubic
|
||||
// x1, y1, cpx1, cpy1, cpx2, cpy2, x2, y2 float64
|
||||
|
||||
// Subdivide a Bezier cubic curve in 2 equivalents Bezier cubic curves.
|
||||
// c1 and c2 parameters are the resulting curves
|
||||
func SubdivideCubic(c, c1, c2 []float64) {
|
||||
// First point of c is the first point of c1
|
||||
c1[0], c1[1] = c[0], c[1]
|
||||
// Last point of c is the last point of c2
|
||||
c2[6], c2[7] = c[6], c[7]
|
||||
|
||||
// Subdivide segment using midpoints
|
||||
c1[2] = (c[0] + c[2]) / 2
|
||||
c1[3] = (c[1] + c[3]) / 2
|
||||
|
||||
midX := (c[2] + c[4]) / 2
|
||||
midY := (c[3] + c[5]) / 2
|
||||
|
||||
c2[4] = (c[4] + c[6]) / 2
|
||||
c2[5] = (c[5] + c[7]) / 2
|
||||
|
||||
c1[4] = (c1[2] + midX) / 2
|
||||
c1[5] = (c1[3] + midY) / 2
|
||||
|
||||
c2[2] = (midX + c2[4]) / 2
|
||||
c2[3] = (midY + c2[5]) / 2
|
||||
|
||||
c1[6] = (c1[4] + c2[2]) / 2
|
||||
c1[7] = (c1[5] + c2[3]) / 2
|
||||
|
||||
// Last Point of c1 is equal to the first point of c2
|
||||
c2[0], c2[1] = c1[6], c1[7]
|
||||
}
|
||||
|
||||
// TraceCubic generate lines subdividing the cubic curve using a Flattener
|
||||
// flattening_threshold helps determines the flattening expectation of the curve
|
||||
func TraceCubic(t Flattener, cubic []float64, flattening_threshold float64) {
|
||||
// Allocation curves
|
||||
var curves [CurveRecursionLimit * 8]float64
|
||||
copy(curves[0:8], cubic[0:8])
|
||||
i := 0
|
||||
|
||||
// current curve
|
||||
var c []float64
|
||||
|
||||
var dx, dy, d2, d3 float64
|
||||
|
||||
for i >= 0 {
|
||||
c = curves[i*8:]
|
||||
dx = c[6] - c[0]
|
||||
dy = c[7] - c[1]
|
||||
|
||||
d2 = math.Abs((c[2]-c[6])*dy - (c[3]-c[7])*dx)
|
||||
d3 = math.Abs((c[4]-c[6])*dy - (c[5]-c[7])*dx)
|
||||
|
||||
// if it's flat then trace a line
|
||||
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
||||
t.LineTo(c[6], c[7])
|
||||
i--
|
||||
} else {
|
||||
// second half of bezier go lower onto the stack
|
||||
SubdivideCubic(c, curves[(i+1)*8:], curves[i*8:])
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Quad
|
||||
// x1, y1, cpx1, cpy2, x2, y2 float64
|
||||
|
||||
// Subdivide a Bezier quad curve in 2 equivalents Bezier quad curves.
|
||||
// c1 and c2 parameters are the resulting curves
|
||||
func SubdivideQuad(c, c1, c2 []float64) {
|
||||
// First point of c is the first point of c1
|
||||
c1[0], c1[1] = c[0], c[1]
|
||||
// Last point of c is the last point of c2
|
||||
c2[4], c2[5] = c[4], c[5]
|
||||
|
||||
// Subdivide segment using midpoints
|
||||
c1[2] = (c[0] + c[2]) / 2
|
||||
c1[3] = (c[1] + c[3]) / 2
|
||||
c2[2] = (c[2] + c[4]) / 2
|
||||
c2[3] = (c[3] + c[5]) / 2
|
||||
c1[4] = (c1[2] + c2[2]) / 2
|
||||
c1[5] = (c1[3] + c2[3]) / 2
|
||||
c2[0], c2[1] = c1[4], c1[5]
|
||||
return
|
||||
}
|
||||
|
||||
// Trace generate lines subdividing the curve using a Flattener
|
||||
// flattening_threshold helps determines the flattening expectation of the curve
|
||||
func TraceQuad(t Flattener, quad []float64, flattening_threshold float64) {
|
||||
// Allocates curves stack
|
||||
var curves [CurveRecursionLimit * 6]float64
|
||||
copy(curves[0:6], quad[0:6])
|
||||
i := 0
|
||||
// current curve
|
||||
var c []float64
|
||||
var dx, dy, d float64
|
||||
|
||||
for i >= 0 {
|
||||
c = curves[i*6:]
|
||||
dx = c[4] - c[0]
|
||||
dy = c[5] - c[1]
|
||||
|
||||
d = math.Abs(((c[2]-c[4])*dy - (c[3]-c[5])*dx))
|
||||
|
||||
// if it's flat then trace a line
|
||||
if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
||||
t.LineTo(c[4], c[5])
|
||||
i--
|
||||
} else {
|
||||
// second half of bezier go lower onto the stack
|
||||
SubdivideQuad(c, curves[(i+1)*6:], curves[i*6:])
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TraceArc trace an arc using a Flattener
|
||||
func TraceArc(t Flattener, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) {
|
||||
end := start + angle
|
||||
clockWise := true
|
||||
if angle < 0 {
|
||||
clockWise = false
|
||||
}
|
||||
ra := (math.Abs(rx) + math.Abs(ry)) / 2
|
||||
da := math.Acos(ra/(ra+0.125/scale)) * 2
|
||||
//normalize
|
||||
if !clockWise {
|
||||
da = -da
|
||||
}
|
||||
angle = start + da
|
||||
var curX, curY float64
|
||||
for {
|
||||
if (angle < end-da/4) != clockWise {
|
||||
curX = x + math.Cos(end)*rx
|
||||
curY = y + math.Sin(end)*ry
|
||||
return curX, curY
|
||||
}
|
||||
curX = x + math.Cos(angle)*rx
|
||||
curY = y + math.Sin(angle)*ry
|
||||
|
||||
angle += da
|
||||
t.LineTo(curX, curY)
|
||||
}
|
||||
return curX, curY
|
||||
}
|
136
draw2dbase/curve_test.go
Normal file
136
draw2dbase/curve_test.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
package draw2dbase
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/llgcode/draw2d/raster"
|
||||
)
|
||||
|
||||
var (
|
||||
flattening_threshold float64 = 0.5
|
||||
testsCubicFloat64 = []float64{
|
||||
100, 100, 200, 100, 100, 200, 200, 200,
|
||||
100, 100, 300, 200, 200, 200, 300, 100,
|
||||
100, 100, 0, 300, 200, 0, 300, 300,
|
||||
150, 290, 10, 10, 290, 10, 150, 290,
|
||||
10, 290, 10, 10, 290, 10, 290, 290,
|
||||
100, 290, 290, 10, 10, 10, 200, 290,
|
||||
}
|
||||
testsQuadFloat64 = []float64{
|
||||
100, 100, 200, 100, 200, 200,
|
||||
100, 100, 290, 200, 290, 100,
|
||||
100, 100, 0, 290, 200, 290,
|
||||
150, 290, 10, 10, 290, 290,
|
||||
10, 290, 10, 10, 290, 290,
|
||||
100, 290, 290, 10, 120, 290,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.Mkdir("test_results", 0666)
|
||||
f, err := os.Create("test_results/_test.html")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
log.Printf("Create html viewer")
|
||||
f.Write([]byte("<html><body>"))
|
||||
for i := 0; i < len(testsCubicFloat64)/8; i++ {
|
||||
f.Write([]byte(fmt.Sprintf("<div><img src='_test%d.png'/></div>\n", i)))
|
||||
}
|
||||
for i := 0; i < len(testsQuadFloat64); i++ {
|
||||
f.Write([]byte(fmt.Sprintf("<div><img src='_testQuad%d.png'/>\n</div>\n", i)))
|
||||
}
|
||||
f.Write([]byte("</body></html>"))
|
||||
|
||||
}
|
||||
|
||||
func drawPoints(img draw.Image, c color.Color, s ...float64) image.Image {
|
||||
for i := 0; i < len(s); i += 2 {
|
||||
x, y := int(s[i]+0.5), int(s[i+1]+0.5)
|
||||
img.Set(x, y, c)
|
||||
img.Set(x, y+1, c)
|
||||
img.Set(x, y-1, c)
|
||||
img.Set(x+1, y, c)
|
||||
img.Set(x+1, y+1, c)
|
||||
img.Set(x+1, y-1, c)
|
||||
img.Set(x-1, y, c)
|
||||
img.Set(x-1, y+1, c)
|
||||
img.Set(x-1, y-1, c)
|
||||
|
||||
}
|
||||
return img
|
||||
}
|
||||
|
||||
func TestCubicCurve(t *testing.T) {
|
||||
for i := 0; i < len(testsCubicFloat64); i += 8 {
|
||||
var p SegmentedPath
|
||||
p.MoveTo(testsCubicFloat64[i], testsCubicFloat64[i+1])
|
||||
TraceCubic(&p, testsCubicFloat64[i:], flattening_threshold)
|
||||
img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
|
||||
raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, testsCubicFloat64[i:i+8]...)
|
||||
raster.PolylineBresenham(img, image.Black, p.Points...)
|
||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.Points...)
|
||||
SaveToPngFile(fmt.Sprintf("test_results/_test%d.png", i/8), img)
|
||||
log.Printf("Num of points: %d\n", len(p.Points))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func TestQuadCurve(t *testing.T) {
|
||||
for i := 0; i < len(testsQuadFloat64); i += 6 {
|
||||
var p SegmentedPath
|
||||
p.MoveTo(testsQuadFloat64[i], testsQuadFloat64[i+1])
|
||||
TraceQuad(&p, testsQuadFloat64[i:], flattening_threshold)
|
||||
img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
|
||||
raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, testsQuadFloat64[i:i+6]...)
|
||||
raster.PolylineBresenham(img, image.Black, p.Points...)
|
||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.Points...)
|
||||
SaveToPngFile(fmt.Sprintf("test_results/_testQuad%d.png", i), img)
|
||||
log.Printf("Num of points: %d\n", len(p.Points))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func BenchmarkCubicCurve(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := 0; i < len(testsCubicFloat64); i += 8 {
|
||||
var p SegmentedPath
|
||||
p.MoveTo(testsCubicFloat64[i], testsCubicFloat64[i+1])
|
||||
TraceCubic(&p, testsCubicFloat64[i:], flattening_threshold)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SaveToPngFile create and save an image to a file using PNG format
|
||||
func SaveToPngFile(filePath string, m image.Image) error {
|
||||
// Create the file
|
||||
f, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// Create Writer from file
|
||||
b := bufio.NewWriter(f)
|
||||
// Write the image into the buffer
|
||||
err = png.Encode(b, m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = b.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -3,24 +3,20 @@
|
|||
|
||||
package draw2dbase
|
||||
|
||||
import (
|
||||
"github.com/llgcode/draw2d"
|
||||
)
|
||||
|
||||
type DashVertexConverter struct {
|
||||
next draw2d.Flattener
|
||||
next Flattener
|
||||
x, y, distance float64
|
||||
dash []float64
|
||||
currentDash int
|
||||
dashOffset float64
|
||||
}
|
||||
|
||||
func NewDashConverter(dash []float64, dashOffset float64, converter draw2d.Flattener) *DashVertexConverter {
|
||||
func NewDashConverter(dash []float64, dashOffset float64, flattener Flattener) *DashVertexConverter {
|
||||
var dasher DashVertexConverter
|
||||
dasher.dash = dash
|
||||
dasher.currentDash = 0
|
||||
dasher.dashOffset = dashOffset
|
||||
dasher.next = converter
|
||||
dasher.next = flattener
|
||||
return &dasher
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
package draw2dbase
|
||||
|
||||
import (
|
||||
"github.com/llgcode/draw2d"
|
||||
)
|
||||
|
||||
type DemuxFlattener struct {
|
||||
Flatteners []draw2d.Flattener
|
||||
Flatteners []Flattener
|
||||
}
|
||||
|
||||
func (dc DemuxFlattener) MoveTo(x, y float64) {
|
||||
|
|
121
draw2dbase/flattener.go
Normal file
121
draw2dbase/flattener.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 06/12/2010 by Laurent Le Goff
|
||||
|
||||
package draw2dbase
|
||||
|
||||
import (
|
||||
"github.com/llgcode/draw2d"
|
||||
)
|
||||
|
||||
// Flattener receive segment definition
|
||||
type Flattener interface {
|
||||
// MoveTo Start a New line from the point (x, y)
|
||||
MoveTo(x, y float64)
|
||||
// LineTo Draw a line from the current position to the point (x, y)
|
||||
LineTo(x, y float64)
|
||||
// LineJoin add the most recent starting point to close the path to create a polygon
|
||||
LineJoin()
|
||||
// Close add the most recent starting point to close the path to create a polygon
|
||||
Close()
|
||||
// End mark the current line as finished so we can draw caps
|
||||
End()
|
||||
}
|
||||
|
||||
// Flatten convert curves into straight segments keeping join segments info
|
||||
func Flatten(path *draw2d.Path, flattener Flattener, scale float64) {
|
||||
// First Point
|
||||
var startX, startY float64 = 0, 0
|
||||
// Current Point
|
||||
var x, y float64 = 0, 0
|
||||
i := 0
|
||||
for _, cmp := range path.Components {
|
||||
switch cmp {
|
||||
case draw2d.MoveToCmp:
|
||||
x, y = path.Points[i], path.Points[i+1]
|
||||
startX, startY = x, y
|
||||
if i != 0 {
|
||||
flattener.End()
|
||||
}
|
||||
flattener.MoveTo(x, y)
|
||||
i += 2
|
||||
case draw2d.LineToCmp:
|
||||
x, y = path.Points[i], path.Points[i+1]
|
||||
flattener.LineTo(x, y)
|
||||
flattener.LineJoin()
|
||||
i += 2
|
||||
case draw2d.QuadCurveToCmp:
|
||||
TraceQuad(flattener, path.Points[i-2:], 0.5)
|
||||
x, y = path.Points[i+2], path.Points[i+3]
|
||||
flattener.LineTo(x, y)
|
||||
i += 4
|
||||
case draw2d.CubicCurveToCmp:
|
||||
TraceCubic(flattener, path.Points[i-2:], 0.5)
|
||||
x, y = path.Points[i+4], path.Points[i+5]
|
||||
flattener.LineTo(x, y)
|
||||
i += 6
|
||||
case draw2d.ArcToCmp:
|
||||
x, y = TraceArc(flattener, path.Points[i], path.Points[i+1], path.Points[i+2], path.Points[i+3], path.Points[i+4], path.Points[i+5], scale)
|
||||
flattener.LineTo(x, y)
|
||||
i += 6
|
||||
case draw2d.CloseCmp:
|
||||
flattener.LineTo(startX, startY)
|
||||
flattener.Close()
|
||||
}
|
||||
}
|
||||
flattener.End()
|
||||
}
|
||||
|
||||
// Transformer apply the Matrix transformation tr
|
||||
type Transformer struct {
|
||||
Tr draw2d.MatrixTransform
|
||||
Flattener Flattener
|
||||
}
|
||||
|
||||
func (t Transformer) MoveTo(x, y float64) {
|
||||
u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4]
|
||||
v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5]
|
||||
t.Flattener.MoveTo(u, v)
|
||||
}
|
||||
|
||||
func (t Transformer) LineTo(x, y float64) {
|
||||
u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4]
|
||||
v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5]
|
||||
t.Flattener.LineTo(u, v)
|
||||
}
|
||||
|
||||
func (t Transformer) LineJoin() {
|
||||
t.Flattener.LineJoin()
|
||||
}
|
||||
|
||||
func (t Transformer) Close() {
|
||||
t.Flattener.Close()
|
||||
}
|
||||
|
||||
func (t Transformer) End() {
|
||||
t.Flattener.End()
|
||||
}
|
||||
|
||||
type SegmentedPath struct {
|
||||
Points []float64
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) MoveTo(x, y float64) {
|
||||
p.Points = append(p.Points, x, y)
|
||||
// TODO need to mark this point as moveto
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) LineTo(x, y float64) {
|
||||
p.Points = append(p.Points, x, y)
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) LineJoin() {
|
||||
// TODO need to mark the current point as linejoin
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) Close() {
|
||||
// TODO Close
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) End() {
|
||||
// Nothing to do
|
||||
}
|
|
@ -32,7 +32,7 @@ func toFtJoin(j draw2d.LineJoin) raster.Joiner {
|
|||
}
|
||||
|
||||
type LineStroker struct {
|
||||
Next draw2d.Flattener
|
||||
Flattener Flattener
|
||||
HalfLineWidth float64
|
||||
Cap draw2d.LineCap
|
||||
Join draw2d.LineJoin
|
||||
|
@ -41,9 +41,9 @@ type LineStroker struct {
|
|||
x, y, nx, ny float64
|
||||
}
|
||||
|
||||
func NewLineStroker(c draw2d.LineCap, j draw2d.LineJoin, flattener draw2d.Flattener) *LineStroker {
|
||||
func NewLineStroker(c draw2d.LineCap, j draw2d.LineJoin, flattener Flattener) *LineStroker {
|
||||
l := new(LineStroker)
|
||||
l.Next = flattener
|
||||
l.Flattener = flattener
|
||||
l.HalfLineWidth = 0.5
|
||||
l.Cap = c
|
||||
l.Join = j
|
||||
|
@ -82,18 +82,18 @@ func (l *LineStroker) Close() {
|
|||
|
||||
func (l *LineStroker) End() {
|
||||
if len(l.vertices) > 1 {
|
||||
l.Next.MoveTo(l.vertices[0], l.vertices[1])
|
||||
l.Flattener.MoveTo(l.vertices[0], l.vertices[1])
|
||||
for i, j := 2, 3; j < len(l.vertices); i, j = i+2, j+2 {
|
||||
l.Next.LineTo(l.vertices[i], l.vertices[j])
|
||||
l.Flattener.LineTo(l.vertices[i], l.vertices[j])
|
||||
}
|
||||
}
|
||||
for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 {
|
||||
l.Next.LineTo(l.rewind[i], l.rewind[j])
|
||||
l.Flattener.LineTo(l.rewind[i], l.rewind[j])
|
||||
}
|
||||
if len(l.vertices) > 1 {
|
||||
l.Next.LineTo(l.vertices[0], l.vertices[1])
|
||||
l.Flattener.LineTo(l.vertices[0], l.vertices[1])
|
||||
}
|
||||
l.Next.End()
|
||||
l.Flattener.End()
|
||||
// reinit vertices
|
||||
l.vertices = l.vertices[0:0]
|
||||
l.rewind = l.rewind[0:0]
|
||||
|
|
|
@ -188,17 +188,17 @@ func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
|
|||
paths = append(paths, gc.Current.Path)
|
||||
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||
|
||||
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
|
||||
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
|
||||
stroker.HalfLineWidth = gc.Current.LineWidth / 2
|
||||
|
||||
var liner draw2d.Flattener
|
||||
var liner draw2dbase.Flattener
|
||||
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
|
||||
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
|
||||
} else {
|
||||
liner = stroker
|
||||
}
|
||||
for _, p := range paths {
|
||||
p.Flatten(liner, gc.Current.Tr.GetScale())
|
||||
draw2dbase.Flatten(p, liner, gc.Current.Tr.GetScale())
|
||||
}
|
||||
|
||||
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
|
||||
|
@ -209,9 +209,9 @@ func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
|
|||
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
|
||||
|
||||
/**** first method ****/
|
||||
flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
|
||||
flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
|
||||
for _, p := range paths {
|
||||
p.Flatten(flattener, gc.Current.Tr.GetScale())
|
||||
draw2dbase.Flatten(p, flattener, gc.Current.Tr.GetScale())
|
||||
}
|
||||
|
||||
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
|
||||
|
@ -222,21 +222,21 @@ func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
|
|||
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
|
||||
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||
|
||||
flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
|
||||
flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
|
||||
|
||||
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
|
||||
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
|
||||
stroker.HalfLineWidth = gc.Current.LineWidth / 2
|
||||
|
||||
var liner draw2d.Flattener
|
||||
var liner draw2dbase.Flattener
|
||||
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
|
||||
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
|
||||
} else {
|
||||
liner = stroker
|
||||
}
|
||||
|
||||
demux := draw2dbase.DemuxFlattener{[]draw2d.Flattener{flattener, liner}}
|
||||
demux := draw2dbase.DemuxFlattener{[]draw2dbase.Flattener{flattener, liner}}
|
||||
for _, p := range paths {
|
||||
p.Flatten(demux, gc.Current.Tr.GetScale())
|
||||
draw2dbase.Flatten(p, demux, gc.Current.Tr.GetScale())
|
||||
}
|
||||
|
||||
// Fill
|
||||
|
|
|
@ -276,17 +276,17 @@ func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
|
|||
paths = append(paths, gc.Current.Path)
|
||||
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||
|
||||
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
|
||||
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
|
||||
stroker.HalfLineWidth = gc.Current.LineWidth / 2
|
||||
|
||||
var liner draw2d.Flattener
|
||||
var liner draw2dbase.Flattener
|
||||
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
|
||||
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
|
||||
} else {
|
||||
liner = stroker
|
||||
}
|
||||
for _, p := range paths {
|
||||
p.Flatten(liner, gc.Current.Tr.GetScale())
|
||||
draw2dbase.Flatten(p, liner, gc.Current.Tr.GetScale())
|
||||
}
|
||||
|
||||
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
|
||||
|
@ -297,9 +297,9 @@ func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
|
|||
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
|
||||
|
||||
/**** first method ****/
|
||||
flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
|
||||
flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
|
||||
for _, p := range paths {
|
||||
p.Flatten(flattener, gc.Current.Tr.GetScale())
|
||||
draw2dbase.Flatten(p, flattener, gc.Current.Tr.GetScale())
|
||||
}
|
||||
|
||||
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
|
||||
|
@ -310,21 +310,21 @@ func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
|
|||
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
|
||||
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||
|
||||
flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
|
||||
flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
|
||||
|
||||
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
|
||||
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
|
||||
stroker.HalfLineWidth = gc.Current.LineWidth / 2
|
||||
|
||||
var liner draw2d.Flattener
|
||||
var liner draw2dbase.Flattener
|
||||
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
|
||||
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
|
||||
} else {
|
||||
liner = stroker
|
||||
}
|
||||
|
||||
demux := draw2dbase.DemuxFlattener{[]draw2d.Flattener{flattener, liner}}
|
||||
demux := draw2dbase.DemuxFlattener{[]draw2dbase.Flattener{flattener, liner}}
|
||||
for _, p := range paths {
|
||||
p.Flatten(demux, gc.Current.Tr.GetScale())
|
||||
draw2dbase.Flatten(p, demux, gc.Current.Tr.GetScale())
|
||||
}
|
||||
|
||||
// Fill
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"math"
|
||||
)
|
||||
|
||||
// Rectangle draw a rectangle using a PathBuilder
|
||||
// Rectangle draws a rectangle using a PathBuilder
|
||||
func Rectangle(path PathBuilder, x1, y1, x2, y2 float64) {
|
||||
path.MoveTo(x1, y1)
|
||||
path.LineTo(x2, y1)
|
||||
|
@ -16,7 +16,7 @@ func Rectangle(path PathBuilder, x1, y1, x2, y2 float64) {
|
|||
path.Close()
|
||||
}
|
||||
|
||||
// RoundedRectangle draw a rounded rectangle using a PathBuilder
|
||||
// RoundedRectangle draws a rounded rectangle using a PathBuilder
|
||||
func RoundedRectangle(path PathBuilder, x1, y1, x2, y2, arcWidth, arcHeight float64) {
|
||||
arcWidth = arcWidth / 2
|
||||
arcHeight = arcHeight / 2
|
||||
|
@ -31,13 +31,13 @@ func RoundedRectangle(path PathBuilder, x1, y1, x2, y2, arcWidth, arcHeight floa
|
|||
path.Close()
|
||||
}
|
||||
|
||||
// Ellipse draw an ellipse using a PathBuilder
|
||||
// Ellipse draws an ellipse using a PathBuilder
|
||||
func Ellipse(path PathBuilder, cx, cy, rx, ry float64) {
|
||||
path.ArcTo(cx, cy, rx, ry, 0, -math.Pi*2)
|
||||
path.Close()
|
||||
}
|
||||
|
||||
// Circle draw an circle using a PathBuilder
|
||||
// Circle draws a circle using a PathBuilder
|
||||
func Circle(path PathBuilder, cx, cy, radius float64) {
|
||||
path.ArcTo(cx, cy, radius, radius, 0, -math.Pi*2)
|
||||
path.Close()
|
||||
|
|
59
flattener.go
59
flattener.go
|
@ -1,59 +0,0 @@
|
|||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||
// created: 06/12/2010 by Laurent Le Goff
|
||||
|
||||
package draw2d
|
||||
|
||||
// Flattener receive segment definition
|
||||
type Flattener interface {
|
||||
// MoveTo Start a New line from the point (x, y)
|
||||
MoveTo(x, y float64)
|
||||
// LineTo Draw a line from the current position to the point (x, y)
|
||||
LineTo(x, y float64)
|
||||
// LineJoin add the most recent starting point to close the path to create a polygon
|
||||
LineJoin()
|
||||
// Close add the most recent starting point to close the path to create a polygon
|
||||
Close()
|
||||
// End mark the current line as finished so we can draw caps
|
||||
End()
|
||||
}
|
||||
|
||||
type SegmentedPath struct {
|
||||
Points []float64
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) MoveTo(x, y float64) {
|
||||
p.Points = append(p.Points, x, y)
|
||||
// TODO need to mark this point as moveto
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) LineTo(x, y float64) {
|
||||
p.Points = append(p.Points, x, y)
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) LineJoin() {
|
||||
// TODO need to mark the current point as linejoin
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) Close() {
|
||||
// TODO Close
|
||||
}
|
||||
|
||||
func (p *SegmentedPath) End() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
type LineCap int
|
||||
|
||||
const (
|
||||
RoundCap LineCap = iota
|
||||
ButtCap
|
||||
SquareCap
|
||||
)
|
||||
|
||||
type LineJoin int
|
||||
|
||||
const (
|
||||
BevelJoin LineJoin = iota
|
||||
RoundJoin
|
||||
MiterJoin
|
||||
)
|
16
gc.go
16
gc.go
|
@ -15,6 +15,22 @@ const (
|
|||
FillRuleWinding
|
||||
)
|
||||
|
||||
type LineCap int
|
||||
|
||||
const (
|
||||
RoundCap LineCap = iota
|
||||
ButtCap
|
||||
SquareCap
|
||||
)
|
||||
|
||||
type LineJoin int
|
||||
|
||||
const (
|
||||
BevelJoin LineJoin = iota
|
||||
RoundJoin
|
||||
MiterJoin
|
||||
)
|
||||
|
||||
type GraphicContext interface {
|
||||
PathBuilder
|
||||
// Create a new path
|
||||
|
|
48
path.go
48
path.go
|
@ -9,7 +9,7 @@ import (
|
|||
"math"
|
||||
)
|
||||
|
||||
// PathBuilder define method that create path
|
||||
// PathBuilder defines methods that creates path
|
||||
type PathBuilder interface {
|
||||
// LastPoint returns the current point of the current path
|
||||
LastPoint() (x, y float64)
|
||||
|
@ -33,7 +33,7 @@ type PathBuilder interface {
|
|||
Close()
|
||||
}
|
||||
|
||||
// PathCmp represent components of a path
|
||||
// PathCmp represents component of a path
|
||||
type PathCmp int
|
||||
|
||||
const (
|
||||
|
@ -174,47 +174,3 @@ func (p *Path) String() string {
|
|||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Flatten convert curves into straight segments keeping join segments info
|
||||
func (path *Path) Flatten(flattener Flattener, scale float64) {
|
||||
// First Point
|
||||
var startX, startY float64 = 0, 0
|
||||
// Current Point
|
||||
var x, y float64 = 0, 0
|
||||
i := 0
|
||||
for _, cmd := range path.Components {
|
||||
switch cmd {
|
||||
case MoveToCmp:
|
||||
x, y = path.Points[i], path.Points[i+1]
|
||||
startX, startY = x, y
|
||||
if i != 0 {
|
||||
flattener.End()
|
||||
}
|
||||
flattener.MoveTo(x, y)
|
||||
i += 2
|
||||
case LineToCmp:
|
||||
x, y = path.Points[i], path.Points[i+1]
|
||||
flattener.LineTo(x, y)
|
||||
flattener.LineJoin()
|
||||
i += 2
|
||||
case QuadCurveToCmp:
|
||||
TraceQuad(flattener, path.Points[i-2:], 0.5)
|
||||
x, y = path.Points[i+2], path.Points[i+3]
|
||||
flattener.LineTo(x, y)
|
||||
i += 4
|
||||
case CubicCurveToCmp:
|
||||
TraceCubic(flattener, path.Points[i-2:], 0.5)
|
||||
x, y = path.Points[i+4], path.Points[i+5]
|
||||
flattener.LineTo(x, y)
|
||||
i += 6
|
||||
case ArcToCmp:
|
||||
x, y = TraceArc(flattener, path.Points[i], path.Points[i+1], path.Points[i+2], path.Points[i+3], path.Points[i+4], path.Points[i+5], scale)
|
||||
flattener.LineTo(x, y)
|
||||
i += 6
|
||||
case CloseCmp:
|
||||
flattener.LineTo(startX, startY)
|
||||
flattener.Close()
|
||||
}
|
||||
}
|
||||
flattener.End()
|
||||
}
|
||||
|
|
30
transform.go
30
transform.go
|
@ -246,33 +246,3 @@ func (tr MatrixTransform) IsTranslation() bool {
|
|||
func fequals(float1, float2 float64) bool {
|
||||
return math.Abs(float1-float2) <= epsilon
|
||||
}
|
||||
|
||||
// Transformer apply the Matrix transformation tr
|
||||
type Transformer struct {
|
||||
Tr MatrixTransform
|
||||
Flattener Flattener
|
||||
}
|
||||
|
||||
func (t Transformer) MoveTo(x, y float64) {
|
||||
u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4]
|
||||
v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5]
|
||||
t.Flattener.MoveTo(u, v)
|
||||
}
|
||||
|
||||
func (t Transformer) LineTo(x, y float64) {
|
||||
u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4]
|
||||
v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5]
|
||||
t.Flattener.LineTo(u, v)
|
||||
}
|
||||
|
||||
func (t Transformer) LineJoin() {
|
||||
t.Flattener.LineJoin()
|
||||
}
|
||||
|
||||
func (t Transformer) Close() {
|
||||
t.Flattener.Close()
|
||||
}
|
||||
|
||||
func (t Transformer) End() {
|
||||
t.Flattener.End()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue