reduce number of points with distance_threshold parameter deduce from flattening_threshold
This commit is contained in:
parent
65e5e944d4
commit
b88f2dc3a3
5 changed files with 1100 additions and 1081 deletions
|
@ -6,4 +6,6 @@ GOFILES=\
|
||||||
quad_float64.go\
|
quad_float64.go\
|
||||||
cubic_float64_others.go\
|
cubic_float64_others.go\
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
include $(GOROOT)/src/Make.pkg
|
include $(GOROOT)/src/Make.pkg
|
||||||
|
|
|
@ -1,66 +1,75 @@
|
||||||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
// created: 17/05/2011 by Laurent Le Goff
|
// created: 17/05/2011 by Laurent Le Goff
|
||||||
package curve
|
package curve
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CurveRecursionLimit = 32
|
CurveRecursionLimit = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
type CubicCurveFloat64 struct {
|
type CubicCurveFloat64 struct {
|
||||||
X1, Y1, X2, Y2, X3, Y3, X4, Y4 float64
|
X1, Y1, X2, Y2, X3, Y3, X4, Y4 float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type LineTracer interface {
|
type LineTracer interface {
|
||||||
LineTo(x, y float64)
|
LineTo(x, y float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CubicCurveFloat64) Subdivide(c1, c2 *CubicCurveFloat64) (x23, y23 float64) {
|
func (c *CubicCurveFloat64) Subdivide(c1, c2 *CubicCurveFloat64) (x23, y23 float64) {
|
||||||
// Calculate all the mid-points of the line segments
|
// Calculate all the mid-points of the line segments
|
||||||
//----------------------
|
//----------------------
|
||||||
c1.X1, c1.Y1 = c.X1, c.Y1
|
c1.X1, c1.Y1 = c.X1, c.Y1
|
||||||
c2.X4, c2.Y4 = c.X4, c.Y4
|
c2.X4, c2.Y4 = c.X4, c.Y4
|
||||||
c1.X2 = (c.X1 + c.X2) / 2
|
c1.X2 = (c.X1 + c.X2) / 2
|
||||||
c1.Y2 = (c.Y1 + c.Y2) / 2
|
c1.Y2 = (c.Y1 + c.Y2) / 2
|
||||||
x23 = (c.X2 + c.X3) / 2
|
x23 = (c.X2 + c.X3) / 2
|
||||||
y23 = (c.Y2 + c.Y3) / 2
|
y23 = (c.Y2 + c.Y3) / 2
|
||||||
c2.X3 = (c.X3 + c.X4) / 2
|
c2.X3 = (c.X3 + c.X4) / 2
|
||||||
c2.Y3 = (c.Y3 + c.Y4) / 2
|
c2.Y3 = (c.Y3 + c.Y4) / 2
|
||||||
c1.X3 = (c1.X2 + x23) / 2
|
c1.X3 = (c1.X2 + x23) / 2
|
||||||
c1.Y3 = (c1.Y2 + y23) / 2
|
c1.Y3 = (c1.Y2 + y23) / 2
|
||||||
c2.X2 = (x23 + c2.X3) / 2
|
c2.X2 = (x23 + c2.X3) / 2
|
||||||
c2.Y2 = (y23 + c2.Y3) / 2
|
c2.Y2 = (y23 + c2.Y3) / 2
|
||||||
c1.X4 = (c1.X3 + c2.X2) / 2
|
c1.X4 = (c1.X3 + c2.X2) / 2
|
||||||
c1.Y4 = (c1.Y3 + c2.Y2) / 2
|
c1.Y4 = (c1.Y3 + c2.Y2) / 2
|
||||||
c2.X1, c2.Y1 = c1.X4, c1.Y4
|
c2.X1, c2.Y1 = c1.X4, c1.Y4
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (curve *CubicCurveFloat64) Segment(t LineTracer, flattening_threshold float64) {
|
func (curve *CubicCurveFloat64) Segment(t LineTracer, flattening_threshold float64) {
|
||||||
var curves [CurveRecursionLimit]CubicCurveFloat64
|
var curves [CurveRecursionLimit]CubicCurveFloat64
|
||||||
curves[0] = *curve
|
curves[0] = *curve
|
||||||
i := 0
|
i := 0
|
||||||
// current curve
|
// current curve
|
||||||
var c *CubicCurveFloat64
|
var c *CubicCurveFloat64
|
||||||
var dx, dy, d2, d3 float64
|
|
||||||
for i >= 0 {
|
var dx, dy, d2, d3 float64
|
||||||
c = &curves[i]
|
var lx, ly float64
|
||||||
dx = c.X4 - c.X1
|
distance_threshold := flattening_threshold * 5
|
||||||
dy = c.Y4 - c.Y1
|
lx, ly = curve.X1, curve.Y1
|
||||||
|
|
||||||
d2 = math.Fabs(((c.X2-c.X4)*dy - (c.Y2-c.Y4)*dx))
|
for i >= 0 {
|
||||||
d3 = math.Fabs(((c.X3-c.X4)*dy - (c.Y3-c.Y4)*dx))
|
c = &curves[i]
|
||||||
|
dx = c.X4 - c.X1
|
||||||
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
dy = c.Y4 - c.Y1
|
||||||
t.LineTo(c.X4, c.Y4)
|
|
||||||
i--
|
d2 = math.Fabs(((c.X2-c.X4)*dy - (c.Y2-c.Y4)*dx))
|
||||||
} else {
|
d3 = math.Fabs(((c.X3-c.X4)*dy - (c.Y3-c.Y4)*dx))
|
||||||
// second half of bezier go lower onto the stack
|
|
||||||
c.Subdivide(&curves[i+1], &curves[i])
|
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
||||||
i++
|
if !(math.Fabs(lx - c.X4) < distance_threshold && math.Fabs(ly - c.Y4)< distance_threshold ) {
|
||||||
}
|
t.LineTo(c.X4, c.Y4)
|
||||||
}
|
lx, ly = c.X4, c.Y4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i--
|
||||||
|
} else {
|
||||||
|
// second half of bezier go lower onto the stack
|
||||||
|
c.Subdivide(&curves[i+1], &curves[i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,262 +1,262 @@
|
||||||
package curve
|
package curve
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"log"
|
"log"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"bufio"
|
"bufio"
|
||||||
"image"
|
"image"
|
||||||
"image/png"
|
"image/png"
|
||||||
"exp/draw"
|
"exp/draw"
|
||||||
"draw2d.googlecode.com/hg/draw2d/raster"
|
"draw2d.googlecode.com/hg/draw2d/raster"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flattening_threshold float64 = 0.25
|
flattening_threshold float64 = 0.25
|
||||||
testsCubicFloat64 = []CubicCurveFloat64{
|
testsCubicFloat64 = []CubicCurveFloat64{
|
||||||
CubicCurveFloat64{100, 100, 200, 100, 100, 200, 200, 200},
|
CubicCurveFloat64{100, 100, 200, 100, 100, 200, 200, 200},
|
||||||
CubicCurveFloat64{100, 100, 300, 200, 200, 200, 300, 100},
|
CubicCurveFloat64{100, 100, 300, 200, 200, 200, 300, 100},
|
||||||
CubicCurveFloat64{100, 100, 0, 300, 200, 0, 300, 300},
|
CubicCurveFloat64{100, 100, 0, 300, 200, 0, 300, 300},
|
||||||
CubicCurveFloat64{150, 290, 10, 10, 290, 10, 150, 290},
|
CubicCurveFloat64{150, 290, 10, 10, 290, 10, 150, 290},
|
||||||
CubicCurveFloat64{10, 290, 10, 10, 290, 10, 290, 290},
|
CubicCurveFloat64{10, 290, 10, 10, 290, 10, 290, 290},
|
||||||
CubicCurveFloat64{100, 290, 290, 10, 10, 10, 200, 290},
|
CubicCurveFloat64{100, 290, 290, 10, 10, 10, 200, 290},
|
||||||
}
|
}
|
||||||
testsQuadFloat64 = []QuadCurveFloat64{
|
testsQuadFloat64 = []QuadCurveFloat64{
|
||||||
QuadCurveFloat64{100, 100, 200, 100, 200, 200},
|
QuadCurveFloat64{100, 100, 200, 100, 200, 200},
|
||||||
QuadCurveFloat64{100, 100, 290, 200, 290, 100},
|
QuadCurveFloat64{100, 100, 290, 200, 290, 100},
|
||||||
QuadCurveFloat64{100, 100, 0, 290, 200, 290},
|
QuadCurveFloat64{100, 100, 0, 290, 200, 290},
|
||||||
QuadCurveFloat64{150, 290, 10, 10, 290, 290},
|
QuadCurveFloat64{150, 290, 10, 10, 290, 290},
|
||||||
QuadCurveFloat64{10, 290, 10, 10, 290, 290},
|
QuadCurveFloat64{10, 290, 10, 10, 290, 290},
|
||||||
QuadCurveFloat64{100, 290, 290, 10, 120, 290},
|
QuadCurveFloat64{100, 290, 290, 10, 120, 290},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type Path struct {
|
type Path struct {
|
||||||
points []float64
|
points []float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Path) LineTo(x, y float64) {
|
func (p *Path) LineTo(x, y float64) {
|
||||||
if len(p.points)+2 > cap(p.points) {
|
if len(p.points)+2 > cap(p.points) {
|
||||||
points := make([]float64, len(p.points)+2, len(p.points)+32)
|
points := make([]float64, len(p.points)+2, len(p.points)+32)
|
||||||
copy(points, p.points)
|
copy(points, p.points)
|
||||||
p.points = points
|
p.points = points
|
||||||
} else {
|
} else {
|
||||||
p.points = p.points[0 : len(p.points)+2]
|
p.points = p.points[0 : len(p.points)+2]
|
||||||
}
|
}
|
||||||
p.points[len(p.points)-2] = x
|
p.points[len(p.points)-2] = x
|
||||||
p.points[len(p.points)-1] = y
|
p.points[len(p.points)-1] = y
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
f, err := os.Create("_test.html")
|
f, err := os.Create("_test.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
log.Printf("Create html viewer")
|
log.Printf("Create html viewer")
|
||||||
f.Write([]byte("<html><body>"))
|
f.Write([]byte("<html><body>"))
|
||||||
for i := 0; i < len(testsCubicFloat64); i++ {
|
for i := 0; i < len(testsCubicFloat64); i++ {
|
||||||
f.Write([]byte(fmt.Sprintf("<div><img src='_testRec%d.png'/>\n<img src='_test%d.png'/>\n<img src='_testAdaptiveRec%d.png'/>\n<img src='_testAdaptive%d.png'/>\n<img src='_testParabolic%d.png'/>\n</div>\n", i, i, i, i, i)))
|
f.Write([]byte(fmt.Sprintf("<div><img src='_testRec%d.png'/>\n<img src='_test%d.png'/>\n<img src='_testAdaptiveRec%d.png'/>\n<img src='_testAdaptive%d.png'/>\n<img src='_testParabolic%d.png'/>\n</div>\n", i, i, i, i, i)))
|
||||||
}
|
}
|
||||||
for i := 0; i < len(testsQuadFloat64); 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(fmt.Sprintf("<div><img src='_testQuad%d.png'/>\n</div>\n", i)))
|
||||||
}
|
}
|
||||||
f.Write([]byte("</body></html>"))
|
f.Write([]byte("</body></html>"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func savepng(filePath string, m image.Image) {
|
func savepng(filePath string, m image.Image) {
|
||||||
f, err := os.Create(filePath)
|
f, err := os.Create(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
b := bufio.NewWriter(f)
|
b := bufio.NewWriter(f)
|
||||||
err = png.Encode(b, m)
|
err = png.Encode(b, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err = b.Flush()
|
err = b.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawPoints(img draw.Image, c image.Color, s ...float64) image.Image {
|
func drawPoints(img draw.Image, c image.Color, s ...float64) image.Image {
|
||||||
for i := 0; i < len(s); i += 2 {
|
/*for i := 0; i < len(s); i += 2 {
|
||||||
x, y := int(s[i]+0.5), int(s[i+1]+0.5)
|
x, y := int(s[i]+0.5), int(s[i+1]+0.5)
|
||||||
img.Set(x, y, c)
|
img.Set(x, y, c)
|
||||||
img.Set(x, y+1, c)
|
img.Set(x, y+1, c)
|
||||||
img.Set(x, y-1, c)
|
img.Set(x, y-1, c)
|
||||||
img.Set(x+1, y, 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-1, c)
|
img.Set(x+1, y-1, c)
|
||||||
img.Set(x-1, y, 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-1, c)
|
img.Set(x-1, y-1, c)
|
||||||
|
|
||||||
}
|
}*/
|
||||||
return img
|
return img
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCubicCurveRec(t *testing.T) {
|
func TestCubicCurveRec(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.SegmentRec(&p, flattening_threshold)
|
curve.SegmentRec(&p, flattening_threshold)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testRec%d.png", i), img)
|
savepng(fmt.Sprintf("_testRec%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCubicCurve(t *testing.T) {
|
func TestCubicCurve(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.Segment(&p, flattening_threshold)
|
curve.Segment(&p, flattening_threshold)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_test%d.png", i), img)
|
savepng(fmt.Sprintf("_test%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCubicCurveAdaptiveRec(t *testing.T) {
|
func TestCubicCurveAdaptiveRec(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
|
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testAdaptiveRec%d.png", i), img)
|
savepng(fmt.Sprintf("_testAdaptiveRec%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCubicCurveAdaptive(t *testing.T) {
|
func TestCubicCurveAdaptive(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.AdaptiveSegment(&p, 1, 0, 0)
|
curve.AdaptiveSegment(&p, 1, 0, 0)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testAdaptive%d.png", i), img)
|
savepng(fmt.Sprintf("_testAdaptive%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCubicCurveParabolic(t *testing.T) {
|
func TestCubicCurveParabolic(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.ParabolicSegment(&p, flattening_threshold)
|
curve.ParabolicSegment(&p, flattening_threshold)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testParabolic%d.png", i), img)
|
savepng(fmt.Sprintf("_testParabolic%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func TestQuadCurve(t *testing.T) {
|
func TestQuadCurve(t *testing.T) {
|
||||||
for i, curve := range testsQuadFloat64 {
|
for i, curve := range testsQuadFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.Segment(&p, flattening_threshold)
|
curve.Segment(&p, flattening_threshold)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testQuad%d.png", i), img)
|
savepng(fmt.Sprintf("_testQuad%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCubicCurveRec(b *testing.B) {
|
func BenchmarkCubicCurveRec(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.SegmentRec(&p, flattening_threshold)
|
curve.SegmentRec(&p, flattening_threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCubicCurve(b *testing.B) {
|
func BenchmarkCubicCurve(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.Segment(&p, flattening_threshold)
|
curve.Segment(&p, flattening_threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCubicCurveAdaptiveRec(b *testing.B) {
|
func BenchmarkCubicCurveAdaptiveRec(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
|
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCubicCurveAdaptive(b *testing.B) {
|
func BenchmarkCubicCurveAdaptive(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.AdaptiveSegment(&p, 1, 0, 0)
|
curve.AdaptiveSegment(&p, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCubicCurveParabolic(b *testing.B) {
|
func BenchmarkCubicCurveParabolic(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.ParabolicSegment(&p, flattening_threshold)
|
curve.ParabolicSegment(&p, flattening_threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkQuadCurve(b *testing.B) {
|
func BenchmarkQuadCurve(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsQuadFloat64 {
|
for _, curve := range testsQuadFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve.X1, curve.Y1)
|
||||||
curve.Segment(&p, flattening_threshold)
|
curve.Segment(&p, flattening_threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,61 @@
|
||||||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
// created: 17/05/2011 by Laurent Le Goff
|
// created: 17/05/2011 by Laurent Le Goff
|
||||||
package curve
|
package curve
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
type QuadCurveFloat64 struct {
|
type QuadCurveFloat64 struct {
|
||||||
X1, Y1, X2, Y2, X3, Y3 float64
|
X1, Y1, X2, Y2, X3, Y3 float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (c *QuadCurveFloat64) Subdivide(c1, c2 *QuadCurveFloat64) {
|
func (c *QuadCurveFloat64) Subdivide(c1, c2 *QuadCurveFloat64) {
|
||||||
// Calculate all the mid-points of the line segments
|
// Calculate all the mid-points of the line segments
|
||||||
//----------------------
|
//----------------------
|
||||||
c1.X1, c1.Y1 = c.X1, c.Y1
|
c1.X1, c1.Y1 = c.X1, c.Y1
|
||||||
c2.X3, c2.Y3 = c.X3, c.Y3
|
c2.X3, c2.Y3 = c.X3, c.Y3
|
||||||
c1.X2 = (c.X1 + c.X2) / 2
|
c1.X2 = (c.X1 + c.X2) / 2
|
||||||
c1.Y2 = (c.Y1 + c.Y2) / 2
|
c1.Y2 = (c.Y1 + c.Y2) / 2
|
||||||
c2.X2 = (c.X2 + c.X3) / 2
|
c2.X2 = (c.X2 + c.X3) / 2
|
||||||
c2.Y2 = (c.Y2 + c.Y3) / 2
|
c2.Y2 = (c.Y2 + c.Y3) / 2
|
||||||
c1.X3 = (c1.X2 + c2.X2) / 2
|
c1.X3 = (c1.X2 + c2.X2) / 2
|
||||||
c1.Y3 = (c1.Y2 + c2.Y2) / 2
|
c1.Y3 = (c1.Y2 + c2.Y2) / 2
|
||||||
c2.X1, c2.Y1 = c1.X3, c1.Y3
|
c2.X1, c2.Y1 = c1.X3, c1.Y3
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (curve *QuadCurveFloat64) Segment(t LineTracer, flattening_threshold float64) {
|
func (curve *QuadCurveFloat64) Segment(t LineTracer, flattening_threshold float64) {
|
||||||
var curves [CurveRecursionLimit]QuadCurveFloat64
|
var curves [CurveRecursionLimit]QuadCurveFloat64
|
||||||
curves[0] = *curve
|
curves[0] = *curve
|
||||||
i := 0
|
i := 0
|
||||||
// current curve
|
// current curve
|
||||||
var c *QuadCurveFloat64
|
var c *QuadCurveFloat64
|
||||||
var dx, dy, d float64
|
var dx, dy, d float64
|
||||||
for i >= 0 {
|
var lx, ly float64
|
||||||
c = &curves[i]
|
distance_threshold := flattening_threshold * 5
|
||||||
dx = c.X3 - c.X1
|
lx, ly = curve.X1, curve.Y1
|
||||||
dy = c.Y3 - c.Y1
|
|
||||||
|
for i >= 0 {
|
||||||
d = math.Fabs(((c.X2-c.X3)*dy - (c.Y2-c.Y3)*dx))
|
c = &curves[i]
|
||||||
|
dx = c.X3 - c.X1
|
||||||
if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
dy = c.Y3 - c.Y1
|
||||||
t.LineTo(c.X3, c.Y3)
|
|
||||||
i--
|
d = math.Fabs(((c.X2-c.X3)*dy - (c.Y2-c.Y3)*dx))
|
||||||
} else {
|
|
||||||
// second half of bezier go lower onto the stack
|
if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
||||||
c.Subdivide(&curves[i+1], &curves[i])
|
if !(math.Fabs(lx - c.X3) <= distance_threshold && math.Fabs(ly - c.Y3)<= distance_threshold ) {
|
||||||
i++
|
t.LineTo(c.X3, c.Y3)
|
||||||
}
|
lx, ly = c.X3, c.Y3
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
i--
|
||||||
|
} else {
|
||||||
|
// second half of bezier go lower onto the stack
|
||||||
|
c.Subdivide(&curves[i+1], &curves[i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue