move flattening code in draw2dbase
This commit is contained in:
parent
82ef300f1d
commit
74e6b9b1ec
2 changed files with 0 additions and 297 deletions
161
curve.go
161
curve.go
|
@ -1,161 +0,0 @@
|
||||||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
|
||||||
// created: 17/05/2011 by Laurent Le Goff
|
|
||||||
|
|
||||||
package draw2d
|
|
||||||
|
|
||||||
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
curve_test.go
136
curve_test.go
|
@ -1,136 +0,0 @@
|
||||||
package draw2d
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
Loading…
Reference in a new issue