use vertex converter as antigrain project
add a Stroke2 and Fill2 method to GraphicContext (comparison purpose: different algorithm)
This commit is contained in:
parent
184115e3e1
commit
0bff8ca6ee
13 changed files with 566 additions and 207 deletions
|
@ -1,6 +1,6 @@
|
||||||
package draw2d
|
package draw2d
|
||||||
|
|
||||||
import(
|
import (
|
||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,25 +15,25 @@ func Rect(path Path, x1, y1, x2, y2 float) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RoundRect(path Path, x1, y1, x2, y2, arcWidth, arcHeight float) {
|
func RoundRect(path Path, x1, y1, x2, y2, arcWidth, arcHeight float) {
|
||||||
arcWidth = arcWidth/2;
|
arcWidth = arcWidth / 2
|
||||||
arcHeight = arcHeight/2;
|
arcHeight = arcHeight / 2
|
||||||
path.MoveTo(x1, y1+ arcHeight);
|
path.MoveTo(x1, y1+arcHeight)
|
||||||
path.QuadCurveTo(x1, y1, x1 + arcWidth, y1);
|
path.QuadCurveTo(x1, y1, x1+arcWidth, y1)
|
||||||
path.LineTo(x2-arcWidth, y1);
|
path.LineTo(x2-arcWidth, y1)
|
||||||
path.QuadCurveTo(x2, y1, x2, y1 + arcHeight);
|
path.QuadCurveTo(x2, y1, x2, y1+arcHeight)
|
||||||
path.LineTo(x2, y2-arcHeight);
|
path.LineTo(x2, y2-arcHeight)
|
||||||
path.QuadCurveTo(x2, y2, x2 - arcWidth, y2);
|
path.QuadCurveTo(x2, y2, x2-arcWidth, y2)
|
||||||
path.LineTo(x1 + arcWidth, y2);
|
path.LineTo(x1+arcWidth, y2)
|
||||||
path.QuadCurveTo(x1, y2, x1, y2 - arcHeight);
|
path.QuadCurveTo(x1, y2, x1, y2-arcHeight)
|
||||||
path.Close()
|
path.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Ellipse(path Path, cx, cy, rx, ry float) {
|
func Ellipse(path Path, cx, cy, rx, ry float) {
|
||||||
path.ArcTo(cx, cy, rx, ry, 0, -math.Pi * 2)
|
path.ArcTo(cx, cy, rx, ry, 0, -math.Pi*2)
|
||||||
path.Close()
|
path.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Circle(path Path, cx, cy, radius float) {
|
func Circle(path Path, cx, cy, radius float) {
|
||||||
path.ArcTo(cx, cy, radius, radius, 0, -math.Pi * 2)
|
path.ArcTo(cx, cy, radius, radius, 0, -math.Pi*2)
|
||||||
path.Close()
|
path.Close()
|
||||||
}
|
}
|
|
@ -1,10 +1,8 @@
|
||||||
// Copyright 2010 The draw2d Authors. All rights reserved.
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
// created: 21/11/2010 by Laurent Le Goff
|
// created: 21/11/2010 by Laurent Le Goff
|
||||||
|
|
||||||
package draw2d
|
package draw2d
|
||||||
|
|
||||||
|
func arc(t VertexConverter, x, y, rx, ry, start, angle, scale float) (lastX, lastY float) {
|
||||||
func arc(t LineTracer, x, y, rx, ry, start, angle, scale float) {
|
|
||||||
end := start + angle
|
end := start + angle
|
||||||
clockWise := true
|
clockWise := true
|
||||||
if angle < 0 {
|
if angle < 0 {
|
||||||
|
@ -22,13 +20,13 @@ func arc(t LineTracer, x, y, rx, ry, start, angle, scale float) {
|
||||||
if (angle < end-da/4) != clockWise {
|
if (angle < end-da/4) != clockWise {
|
||||||
curX = x + cos(end)*rx
|
curX = x + cos(end)*rx
|
||||||
curY = y + sin(end)*ry
|
curY = y + sin(end)*ry
|
||||||
t.LineTo(curX, curY)
|
return curX, curY
|
||||||
break
|
|
||||||
}
|
}
|
||||||
curX = x + cos(angle)*rx
|
curX = x + cos(angle)*rx
|
||||||
curY = y + sin(angle)*ry
|
curY = y + sin(angle)*ry
|
||||||
|
|
||||||
angle += da
|
angle += da
|
||||||
t.LineTo(curX, curY)
|
t.Vertex(curX, curY)
|
||||||
}
|
}
|
||||||
|
return curX, curY
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,25 +33,22 @@ var (
|
||||||
The more this value is the less sharp turns will be cut.
|
The more this value is the less sharp turns will be cut.
|
||||||
Typically it should not exceed 10-15 degrees.
|
Typically it should not exceed 10-15 degrees.
|
||||||
*/
|
*/
|
||||||
|
func cubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float) {
|
||||||
func cubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float) {
|
|
||||||
cuspLimit = computeCuspLimit(cuspLimit)
|
cuspLimit = computeCuspLimit(cuspLimit)
|
||||||
distanceToleranceSquare := 0.5 / approximationScale
|
distanceToleranceSquare := 0.5 / approximationScale
|
||||||
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
|
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
|
||||||
recursiveCubicBezier(v, x1, y1, x2, y2, x3, y3, x4, y4, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
|
recursiveCubicBezier(v, x1, y1, x2, y2, x3, y3, x4, y4, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
|
||||||
v.LineTo(x4, y4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* see cubicBezier comments for approximationScale and angleTolerance definition
|
* see cubicBezier comments for approximationScale and angleTolerance definition
|
||||||
*/
|
*/
|
||||||
func quadraticBezier(v LineTracer, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float) {
|
func quadraticBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float) {
|
||||||
distanceToleranceSquare := 0.5 / approximationScale
|
distanceToleranceSquare := 0.5 / approximationScale
|
||||||
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
|
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
|
||||||
|
|
||||||
recursiveQuadraticBezierBezier(v, x1, y1, x2, y2, x3, y3, 0, distanceToleranceSquare, angleTolerance)
|
recursiveQuadraticBezierBezier(v, x1, y1, x2, y2, x3, y3, 0, distanceToleranceSquare, angleTolerance)
|
||||||
v.LineTo(x3, y3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,7 +65,7 @@ func computeCuspLimit(v float) (r float) {
|
||||||
/**
|
/**
|
||||||
* http://www.antigrain.com/research/adaptive_bezier/index.html
|
* http://www.antigrain.com/research/adaptive_bezier/index.html
|
||||||
*/
|
*/
|
||||||
func recursiveQuadraticBezierBezier(v LineTracer, x1, y1, x2, y2, x3, y3 float, level int, distanceToleranceSquare, angleTolerance float) {
|
func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 float, level int, distanceToleranceSquare, angleTolerance float) {
|
||||||
if level > CurveRecursionLimit {
|
if level > CurveRecursionLimit {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -94,7 +91,7 @@ func recursiveQuadraticBezierBezier(v LineTracer, x1, y1, x2, y2, x3, y3 float,
|
||||||
// we tend to finish subdivisions.
|
// we tend to finish subdivisions.
|
||||||
//----------------------
|
//----------------------
|
||||||
if angleTolerance < CurveAngleToleranceEpsilon {
|
if angleTolerance < CurveAngleToleranceEpsilon {
|
||||||
v.LineTo(x123, y123)
|
v.Vertex(x123, y123)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +105,7 @@ func recursiveQuadraticBezierBezier(v LineTracer, x1, y1, x2, y2, x3, y3 float,
|
||||||
if da < angleTolerance {
|
if da < angleTolerance {
|
||||||
// Finally we can stop the recursion
|
// Finally we can stop the recursion
|
||||||
//----------------------
|
//----------------------
|
||||||
v.LineTo(x123, y123)
|
v.Vertex(x123, y123)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +131,7 @@ func recursiveQuadraticBezierBezier(v LineTracer, x1, y1, x2, y2, x3, y3 float,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if d < distanceToleranceSquare {
|
if d < distanceToleranceSquare {
|
||||||
v.LineTo(x2, y2)
|
v.Vertex(x2, y2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,7 +145,7 @@ func recursiveQuadraticBezierBezier(v LineTracer, x1, y1, x2, y2, x3, y3 float,
|
||||||
/**
|
/**
|
||||||
* http://www.antigrain.com/research/adaptive_bezier/index.html
|
* http://www.antigrain.com/research/adaptive_bezier/index.html
|
||||||
*/
|
*/
|
||||||
func recursiveCubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4 float, level int, distanceToleranceSquare, angleTolerance, cuspLimit float) {
|
func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 float, level int, distanceToleranceSquare, angleTolerance, cuspLimit float) {
|
||||||
if level > CurveRecursionLimit {
|
if level > CurveRecursionLimit {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -215,12 +212,12 @@ func recursiveCubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4 float, le
|
||||||
}
|
}
|
||||||
if d2 > d3 {
|
if d2 > d3 {
|
||||||
if d2 < distanceToleranceSquare {
|
if d2 < distanceToleranceSquare {
|
||||||
v.LineTo(x2, y2)
|
v.Vertex(x2, y2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if d3 < distanceToleranceSquare {
|
if d3 < distanceToleranceSquare {
|
||||||
v.LineTo(x3, y3)
|
v.Vertex(x3, y3)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,7 +228,7 @@ func recursiveCubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4 float, le
|
||||||
//----------------------
|
//----------------------
|
||||||
if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
|
if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
|
||||||
if angleTolerance < CurveAngleToleranceEpsilon {
|
if angleTolerance < CurveAngleToleranceEpsilon {
|
||||||
v.LineTo(x23, y23)
|
v.Vertex(x23, y23)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,14 +240,14 @@ func recursiveCubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4 float, le
|
||||||
}
|
}
|
||||||
|
|
||||||
if da1 < angleTolerance {
|
if da1 < angleTolerance {
|
||||||
v.LineTo(x2, y2)
|
v.Vertex(x2, y2)
|
||||||
v.LineTo(x3, y3)
|
v.Vertex(x3, y3)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
v.LineTo(x3, y3)
|
v.Vertex(x3, y3)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,7 +259,7 @@ func recursiveCubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4 float, le
|
||||||
//----------------------
|
//----------------------
|
||||||
if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
|
if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
|
||||||
if angleTolerance < CurveAngleToleranceEpsilon {
|
if angleTolerance < CurveAngleToleranceEpsilon {
|
||||||
v.LineTo(x23, y23)
|
v.Vertex(x23, y23)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,14 +271,14 @@ func recursiveCubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4 float, le
|
||||||
}
|
}
|
||||||
|
|
||||||
if da1 < angleTolerance {
|
if da1 < angleTolerance {
|
||||||
v.LineTo(x2, y2)
|
v.Vertex(x2, y2)
|
||||||
v.LineTo(x3, y3)
|
v.Vertex(x3, y3)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
v.LineTo(x2, y2)
|
v.Vertex(x2, y2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,7 +293,7 @@ func recursiveCubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4 float, le
|
||||||
// we tend to finish subdivisions.
|
// we tend to finish subdivisions.
|
||||||
//----------------------
|
//----------------------
|
||||||
if angleTolerance < CurveAngleToleranceEpsilon {
|
if angleTolerance < CurveAngleToleranceEpsilon {
|
||||||
v.LineTo(x23, y23)
|
v.Vertex(x23, y23)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,18 +312,18 @@ func recursiveCubicBezier(v LineTracer, x1, y1, x2, y2, x3, y3, x4, y4 float, le
|
||||||
if da1+da2 < angleTolerance {
|
if da1+da2 < angleTolerance {
|
||||||
// Finally we can stop the recursion
|
// Finally we can stop the recursion
|
||||||
//----------------------
|
//----------------------
|
||||||
v.LineTo(x23, y23)
|
v.Vertex(x23, y23)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
v.LineTo(x2, y2)
|
v.Vertex(x2, y2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if da2 > cuspLimit {
|
if da2 > cuspLimit {
|
||||||
v.LineTo(x3, y3)
|
v.Vertex(x3, y3)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,49 @@
|
||||||
package draw2d
|
package draw2d
|
||||||
|
|
||||||
import(
|
type DashVertexConverter struct {
|
||||||
"freetype-go.googlecode.com/hg/freetype/raster"
|
command VertexCommand
|
||||||
)
|
next VertexConverter
|
||||||
|
|
||||||
type DashAdder struct {
|
|
||||||
adder raster.Adder
|
|
||||||
x, y, distance float
|
x, y, distance float
|
||||||
dash []float
|
dash []float
|
||||||
currentDash int
|
currentDash int
|
||||||
dashOffset float
|
dashOffset float
|
||||||
}
|
}
|
||||||
|
|
||||||
func traceDashPath(dash []float, dashOffset float, approximationScale float, adder raster.Adder, paths ...*PathStorage) {
|
func NewDashConverter(dash []float, dashOffset float, converter VertexConverter) *DashVertexConverter {
|
||||||
var dasher DashAdder
|
var dasher DashVertexConverter
|
||||||
if dash != nil && len(dash) > 0 {
|
|
||||||
dasher.dash = dash
|
dasher.dash = dash
|
||||||
} else {
|
|
||||||
dasher.dash = nil
|
|
||||||
}
|
|
||||||
dasher.currentDash = 0
|
dasher.currentDash = 0
|
||||||
dasher.dashOffset = dashOffset
|
dasher.dashOffset = dashOffset
|
||||||
dasher.adder = adder
|
dasher.next = converter
|
||||||
for _, path := range paths {
|
return &dasher
|
||||||
path.TraceLine(&dasher, approximationScale)
|
}
|
||||||
|
|
||||||
|
func (dasher *DashVertexConverter) NextCommand(cmd VertexCommand) {
|
||||||
|
dasher.command = cmd
|
||||||
|
if(dasher.command == VertexStopCommand) {
|
||||||
|
dasher.next.NextCommand(VertexStopCommand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dasher *DashAdder) MoveTo(x, y float) {
|
func (dasher *DashVertexConverter) Vertex(x, y float) {
|
||||||
dasher.adder.Start(floatToPoint(x, y))
|
switch dasher.command {
|
||||||
|
case VertexStartCommand:
|
||||||
|
dasher.start(x, y)
|
||||||
|
default:
|
||||||
|
dasher.lineTo(x, y)
|
||||||
|
}
|
||||||
|
dasher.command = VertexNoCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dasher *DashVertexConverter) start(x, y float) {
|
||||||
|
dasher.next.NextCommand(VertexStartCommand)
|
||||||
|
dasher.next.Vertex(x, y)
|
||||||
dasher.x, dasher.y = x, y
|
dasher.x, dasher.y = x, y
|
||||||
dasher.distance = dasher.dashOffset
|
dasher.distance = dasher.dashOffset
|
||||||
dasher.currentDash = 0
|
dasher.currentDash = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dasher *DashAdder) LineTo(x, y float) {
|
func (dasher *DashVertexConverter) lineTo(x, y float) {
|
||||||
rest := dasher.dash[dasher.currentDash] - dasher.distance
|
rest := dasher.dash[dasher.currentDash] - dasher.distance
|
||||||
for rest < 0 {
|
for rest < 0 {
|
||||||
dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
|
dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
|
||||||
|
@ -48,10 +57,12 @@ func (dasher *DashAdder) LineTo(x, y float) {
|
||||||
ly := dasher.y + k*(y-dasher.y)
|
ly := dasher.y + k*(y-dasher.y)
|
||||||
if dasher.currentDash%2 == 0 {
|
if dasher.currentDash%2 == 0 {
|
||||||
// line
|
// line
|
||||||
dasher.adder.Add1(floatToPoint(lx, ly))
|
dasher.next.Vertex(lx, ly)
|
||||||
} else {
|
} else {
|
||||||
// gap
|
// gap
|
||||||
dasher.adder.Start(floatToPoint(lx, ly))
|
dasher.next.NextCommand(VertexStopCommand)
|
||||||
|
dasher.next.NextCommand(VertexStartCommand)
|
||||||
|
dasher.next.Vertex(lx, ly)
|
||||||
}
|
}
|
||||||
d = d - rest
|
d = d - rest
|
||||||
dasher.x, dasher.y = lx, ly
|
dasher.x, dasher.y = lx, ly
|
||||||
|
@ -60,9 +71,13 @@ func (dasher *DashAdder) LineTo(x, y float) {
|
||||||
}
|
}
|
||||||
dasher.distance = d
|
dasher.distance = d
|
||||||
if dasher.currentDash%2 == 0 {
|
if dasher.currentDash%2 == 0 {
|
||||||
dasher.adder.Add1(floatToPoint(x, y))
|
// line
|
||||||
|
dasher.next.Vertex(x, y)
|
||||||
} else {
|
} else {
|
||||||
dasher.adder.Start(floatToPoint(x, y))
|
// gap
|
||||||
|
dasher.next.NextCommand(VertexStopCommand)
|
||||||
|
dasher.next.NextCommand(VertexStartCommand)
|
||||||
|
dasher.next.Vertex(x, y)
|
||||||
}
|
}
|
||||||
if dasher.distance >= dasher.dash[dasher.currentDash] {
|
if dasher.distance >= dasher.dash[dasher.currentDash] {
|
||||||
dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
|
dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
|
||||||
|
|
20
draw2d/src/pkg/draw2d/demux_converter.go
Normal file
20
draw2d/src/pkg/draw2d/demux_converter.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package draw2d
|
||||||
|
|
||||||
|
type DemuxConverter struct {
|
||||||
|
converters []VertexConverter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDemuxConverter(converters ...VertexConverter) *DemuxConverter {
|
||||||
|
return &DemuxConverter{converters}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *DemuxConverter) NextCommand(cmd VertexCommand) {
|
||||||
|
for _, converter := range dc.converters {
|
||||||
|
converter.NextCommand(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (dc *DemuxConverter) Vertex(x, y float) {
|
||||||
|
for _, converter := range dc.converters {
|
||||||
|
converter.Vertex(x, y)
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,25 +16,10 @@ const (
|
||||||
FillRuleWinding
|
FillRuleWinding
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cap int
|
|
||||||
|
|
||||||
const (
|
|
||||||
RoundCap Cap = iota
|
|
||||||
ButtCap
|
|
||||||
SquareCap
|
|
||||||
)
|
|
||||||
|
|
||||||
type Join int
|
|
||||||
|
|
||||||
const (
|
|
||||||
BevelJoin Join = iota
|
|
||||||
RoundJoin
|
|
||||||
MiterJoin
|
|
||||||
)
|
|
||||||
|
|
||||||
type GraphicContext struct {
|
type GraphicContext struct {
|
||||||
PaintedImage *image.RGBA
|
PaintedImage *image.RGBA
|
||||||
rasterizer *raster.Rasterizer
|
fillRasterizer *raster.Rasterizer
|
||||||
|
strokeRasterizer *raster.Rasterizer
|
||||||
current *contextStack
|
current *contextStack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +44,8 @@ func NewGraphicContext(pi *image.RGBA) *GraphicContext {
|
||||||
gc := new(GraphicContext)
|
gc := new(GraphicContext)
|
||||||
gc.PaintedImage = pi
|
gc.PaintedImage = pi
|
||||||
width, height := gc.PaintedImage.Bounds().Dx(), gc.PaintedImage.Bounds().Dy()
|
width, height := gc.PaintedImage.Bounds().Dx(), gc.PaintedImage.Bounds().Dy()
|
||||||
gc.rasterizer = raster.NewRasterizer(width, height)
|
gc.fillRasterizer = raster.NewRasterizer(width, height)
|
||||||
|
gc.strokeRasterizer = raster.NewRasterizer(width, height)
|
||||||
|
|
||||||
gc.current = new(contextStack)
|
gc.current = new(contextStack)
|
||||||
|
|
||||||
|
@ -205,55 +191,124 @@ func (gc *GraphicContext) Close() {
|
||||||
gc.current.path.Close()
|
gc.current.path.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gc *GraphicContext) paint(color image.Color) {
|
func (gc *GraphicContext) paint(rasterizer *raster.Rasterizer, color image.Color) {
|
||||||
painter := raster.NewRGBAPainter(gc.PaintedImage)
|
painter := raster.NewRGBAPainter(gc.PaintedImage)
|
||||||
painter.SetColor(color)
|
painter.SetColor(color)
|
||||||
gc.rasterizer.Rasterize(painter)
|
rasterizer.Rasterize(painter)
|
||||||
gc.rasterizer.Clear()
|
rasterizer.Clear()
|
||||||
gc.current.path = new(PathStorage)
|
gc.current.path = new(PathStorage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** first method ****/
|
||||||
func (gc *GraphicContext) Stroke(paths ...*PathStorage) {
|
func (gc *GraphicContext) Stroke(paths ...*PathStorage) {
|
||||||
paths = append(paths, gc.current.path)
|
paths = append(paths, gc.current.path)
|
||||||
gc.rasterizer.UseNonZeroWinding = true
|
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||||
|
|
||||||
rasterPath := new(raster.Path)
|
rasterPath := new(raster.Path)
|
||||||
if(gc.current.dash == nil) {
|
|
||||||
tracePath(gc.current.tr.GetMaxAbsScaling(), rasterPath, paths...)
|
var pathConverter *PathConverter
|
||||||
|
if gc.current.dash != nil && len(gc.current.dash) > 0 {
|
||||||
|
dasher := NewDashConverter(gc.current.dash, gc.current.dashOffset, NewVertexAdder(rasterPath))
|
||||||
|
pathConverter = NewPathConverter(dasher)
|
||||||
} else {
|
} else {
|
||||||
traceDashPath(gc.current.dash, gc.current.dashOffset, gc.current.tr.GetMaxAbsScaling(), rasterPath, paths...)
|
pathConverter = NewPathConverter(NewVertexAdder(rasterPath))
|
||||||
}
|
}
|
||||||
mta := NewMatrixTransformAdder(gc.current.tr, gc.rasterizer)
|
|
||||||
|
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||||
|
pathConverter.Convert(paths...)
|
||||||
|
|
||||||
|
mta := NewMatrixTransformAdder(gc.current.tr, gc.strokeRasterizer)
|
||||||
raster.Stroke(mta, *rasterPath, raster.Fix32(gc.current.lineWidth*256), gc.current.cap.capper(), gc.current.join.joiner())
|
raster.Stroke(mta, *rasterPath, raster.Fix32(gc.current.lineWidth*256), gc.current.cap.capper(), gc.current.join.joiner())
|
||||||
gc.paint(gc.current.strokeColor)
|
|
||||||
|
gc.paint(gc.strokeRasterizer, gc.current.strokeColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** second method ****/
|
||||||
|
func (gc *GraphicContext) Stroke2(paths ...*PathStorage) {
|
||||||
|
paths = append(paths, gc.current.path)
|
||||||
|
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||||
|
|
||||||
|
stroker := NewLineStroker(NewVertexMatrixTransform(gc.current.tr, NewVertexAdder(gc.strokeRasterizer)))
|
||||||
|
stroker.HalfLineWidth = gc.current.lineWidth / 2
|
||||||
|
var pathConverter *PathConverter
|
||||||
|
if gc.current.dash != nil && len(gc.current.dash) > 0 {
|
||||||
|
dasher := NewDashConverter(gc.current.dash, gc.current.dashOffset, stroker)
|
||||||
|
pathConverter = NewPathConverter(dasher)
|
||||||
|
} else {
|
||||||
|
pathConverter = NewPathConverter(stroker)
|
||||||
|
}
|
||||||
|
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||||
|
pathConverter.Convert(paths...)
|
||||||
|
|
||||||
|
gc.paint(gc.strokeRasterizer, gc.current.strokeColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**** first method ****/
|
||||||
func (gc *GraphicContext) Fill(paths ...*PathStorage) {
|
func (gc *GraphicContext) Fill(paths ...*PathStorage) {
|
||||||
paths = append(paths, gc.current.path)
|
paths = append(paths, gc.current.path)
|
||||||
gc.rasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
|
gc.fillRasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
|
||||||
mta := NewMatrixTransformAdder(gc.current.tr, gc.rasterizer)
|
|
||||||
tracePath(gc.current.tr.GetMaxAbsScaling(), mta, paths...)
|
pathConverter := NewPathConverter(NewVertexAdder(NewMatrixTransformAdder(gc.current.tr, gc.fillRasterizer)))
|
||||||
gc.paint(gc.current.fillColor)
|
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||||
|
pathConverter.Convert(paths...)
|
||||||
|
gc.paint(gc.fillRasterizer, gc.current.fillColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**** second method ****/
|
||||||
|
func (gc *GraphicContext) Fill2(paths ...*PathStorage) {
|
||||||
|
paths = append(paths, gc.current.path)
|
||||||
|
gc.fillRasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
|
||||||
|
|
||||||
|
/**** first method ****/
|
||||||
|
pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.current.tr, NewVertexAdder(gc.fillRasterizer)))
|
||||||
|
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||||
|
pathConverter.Convert(paths...)
|
||||||
|
gc.paint(gc.fillRasterizer, gc.current.fillColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gc *GraphicContext) FillStroke(paths ...*PathStorage) {
|
func (gc *GraphicContext) FillStroke(paths ...*PathStorage) {
|
||||||
paths = append(paths, gc.current.path)
|
paths = append(paths, gc.current.path)
|
||||||
mta := NewMatrixTransformAdder(gc.current.tr, gc.rasterizer)
|
gc.fillRasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
|
||||||
tracePath(gc.current.tr.GetMaxAbsScaling(), mta, paths...)
|
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||||
|
|
||||||
gc.rasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
|
filler := NewVertexMatrixTransform(gc.current.tr, NewVertexAdder(gc.fillRasterizer))
|
||||||
gc.paint(gc.current.fillColor)
|
|
||||||
|
|
||||||
gc.rasterizer.UseNonZeroWinding = true
|
|
||||||
rasterPath := new(raster.Path)
|
rasterPath := new(raster.Path)
|
||||||
if(gc.current.dash == nil) {
|
stroker := NewVertexAdder(rasterPath)
|
||||||
tracePath(gc.current.tr.GetMaxAbsScaling(), rasterPath, paths...)
|
|
||||||
} else {
|
demux := NewDemuxConverter(filler, stroker)
|
||||||
traceDashPath(gc.current.dash, gc.current.dashOffset, gc.current.tr.GetMaxAbsScaling(), rasterPath, paths...)
|
|
||||||
}
|
pathConverter := NewPathConverter(demux)
|
||||||
|
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||||
|
pathConverter.Convert(paths...)
|
||||||
|
|
||||||
|
mta := NewMatrixTransformAdder(gc.current.tr, gc.strokeRasterizer)
|
||||||
raster.Stroke(mta, *rasterPath, raster.Fix32(gc.current.lineWidth*256), gc.current.cap.capper(), gc.current.join.joiner())
|
raster.Stroke(mta, *rasterPath, raster.Fix32(gc.current.lineWidth*256), gc.current.cap.capper(), gc.current.join.joiner())
|
||||||
gc.paint(gc.current.strokeColor)
|
|
||||||
|
gc.paint(gc.fillRasterizer, gc.current.fillColor)
|
||||||
|
gc.paint(gc.strokeRasterizer, gc.current.strokeColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* second method */
|
||||||
|
func (gc *GraphicContext) FillStroke2(paths ...*PathStorage) {
|
||||||
|
gc.fillRasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
|
||||||
|
gc.strokeRasterizer.UseNonZeroWinding = true
|
||||||
|
|
||||||
|
filler := NewVertexMatrixTransform(gc.current.tr, NewVertexAdder(gc.fillRasterizer))
|
||||||
|
|
||||||
|
stroker := NewLineStroker(NewVertexMatrixTransform(gc.current.tr, NewVertexAdder(gc.strokeRasterizer)))
|
||||||
|
stroker.HalfLineWidth = gc.current.lineWidth / 2
|
||||||
|
|
||||||
|
demux := NewDemuxConverter(filler, stroker)
|
||||||
|
paths = append(paths, gc.current.path)
|
||||||
|
pathConverter := NewPathConverter(demux)
|
||||||
|
pathConverter.ApproximationScale = gc.current.tr.GetMaxAbsScaling()
|
||||||
|
pathConverter.Convert(paths...)
|
||||||
|
|
||||||
|
gc.paint(gc.fillRasterizer, gc.current.fillColor)
|
||||||
|
gc.paint(gc.strokeRasterizer, gc.current.strokeColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (f FillRule) fillRule() bool {
|
func (f FillRule) fillRule() bool {
|
||||||
switch f {
|
switch f {
|
||||||
case FillRuleEvenOdd:
|
case FillRuleEvenOdd:
|
||||||
|
@ -285,4 +340,3 @@ func (j Join) joiner() raster.Joiner {
|
||||||
}
|
}
|
||||||
return raster.RoundJoiner
|
return raster.RoundJoiner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,10 @@ func distance(x1, y1, x2, y2 float) float {
|
||||||
return float(math.Sqrt(float64(dx*dx + dy*dy)))
|
return float(math.Sqrt(float64(dx*dx + dy*dy)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vectorDistance(dx, dy float) float {
|
||||||
|
return float(math.Sqrt(float64(dx*dx + dy*dy)))
|
||||||
|
}
|
||||||
|
|
||||||
func squareDistance(x1, y1, x2, y2 float) float {
|
func squareDistance(x1, y1, x2, y2 float) float {
|
||||||
dx := x2 - x1
|
dx := x2 - x1
|
||||||
dy := y2 - y1
|
dy := y2 - y1
|
||||||
|
|
|
@ -17,8 +17,18 @@ type Path interface {
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
type LineTracer interface {
|
|
||||||
MoveTo(x, y float)
|
|
||||||
LineTo(x, y float)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
type VertexCommand byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
VertexNoCommand VertexCommand = iota
|
||||||
|
VertexStartCommand
|
||||||
|
VertexJoinCommand
|
||||||
|
VertexCloseCommand
|
||||||
|
VertexStopCommand
|
||||||
|
)
|
||||||
|
|
||||||
|
type VertexConverter interface {
|
||||||
|
NextCommand(cmd VertexCommand)
|
||||||
|
Vertex(x, y float)
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package draw2d
|
package draw2d
|
||||||
|
|
||||||
|
|
||||||
import(
|
import (
|
||||||
"freetype-go.googlecode.com/hg/freetype/raster"
|
"freetype-go.googlecode.com/hg/freetype/raster"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type PathAdder struct {
|
type VertexAdder struct {
|
||||||
|
command VertexCommand
|
||||||
adder raster.Adder
|
adder raster.Adder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,17 +16,21 @@ func floatToPoint(x, y float) raster.Point {
|
||||||
return raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}
|
return raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tracePath(approximationScale float, adder raster.Adder, paths ...*PathStorage) {
|
|
||||||
pathAdder := &PathAdder{adder}
|
func NewVertexAdder(adder raster.Adder) *VertexAdder {
|
||||||
for _, path := range paths {
|
return &VertexAdder{VertexNoCommand, adder}
|
||||||
path.TraceLine(pathAdder, approximationScale)
|
}
|
||||||
|
|
||||||
|
func (vertexAdder *VertexAdder) NextCommand(cmd VertexCommand) {
|
||||||
|
vertexAdder.command = cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vertexAdder *VertexAdder) Vertex(x, y float) {
|
||||||
|
switch vertexAdder.command {
|
||||||
|
case VertexStartCommand:
|
||||||
|
vertexAdder.adder.Start(floatToPoint(x, y))
|
||||||
|
default:
|
||||||
|
vertexAdder.adder.Add1(floatToPoint(x, y))
|
||||||
}
|
}
|
||||||
}
|
vertexAdder.command = VertexNoCommand
|
||||||
|
|
||||||
func (pathAdder *PathAdder) MoveTo(x, y float) {
|
|
||||||
pathAdder.adder.Start(floatToPoint(x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pathAdder *PathAdder) LineTo(x, y float) {
|
|
||||||
pathAdder.adder.Add1(floatToPoint(x, y))
|
|
||||||
}
|
}
|
||||||
|
|
153
draw2d/src/pkg/draw2d/path_converter.go
Normal file
153
draw2d/src/pkg/draw2d/path_converter.go
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 06/12/2010 by Laurent Le Goff
|
||||||
|
package draw2d
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PathConverter struct {
|
||||||
|
converter VertexConverter
|
||||||
|
ApproximationScale, AngleTolerance, CuspLimit float
|
||||||
|
startX, startY, x, y float
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPathConverter(converter VertexConverter) *PathConverter {
|
||||||
|
return &PathConverter{converter, 1, 0, 0, 0, 0, 0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) Convert(paths ...*PathStorage) {
|
||||||
|
for _, path := range paths {
|
||||||
|
j := 0
|
||||||
|
for _, cmd := range path.commands {
|
||||||
|
j = j + c.ConvertCommand(cmd, path.vertices[j:]...)
|
||||||
|
}
|
||||||
|
c.converter.NextCommand(VertexStopCommand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (c *PathConverter) ConvertCommand(cmd PathCmd, vertices ...float) int {
|
||||||
|
switch cmd {
|
||||||
|
case MoveTo:
|
||||||
|
c.MoveTo(vertices[0], vertices[1])
|
||||||
|
return 2
|
||||||
|
case LineTo:
|
||||||
|
c.LineTo(vertices[0], vertices[1])
|
||||||
|
return 2
|
||||||
|
case QuadCurveTo:
|
||||||
|
c.QuadCurveTo(vertices[0], vertices[1], vertices[2], vertices[3])
|
||||||
|
return 4
|
||||||
|
case CubicCurveTo:
|
||||||
|
c.CubicCurveTo(vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5])
|
||||||
|
return 6
|
||||||
|
case ArcTo:
|
||||||
|
c.x, c.y = arc(c.converter, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale)
|
||||||
|
if c.startX == c.x && c.startY == c.y {
|
||||||
|
c.converter.NextCommand(VertexCloseCommand)
|
||||||
|
}
|
||||||
|
c.converter.Vertex(c.x, c.y)
|
||||||
|
return 6
|
||||||
|
case Close:
|
||||||
|
c.Close()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) MoveTo(x, y float) *PathConverter {
|
||||||
|
c.x, c.y = x, y
|
||||||
|
c.startX, c.startY = c.x, c.y
|
||||||
|
c.converter.NextCommand(VertexStopCommand)
|
||||||
|
c.converter.NextCommand(VertexStartCommand)
|
||||||
|
c.converter.Vertex(c.x, c.y)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) RMoveTo(dx, dy float) *PathConverter {
|
||||||
|
c.MoveTo(c.x+dx, c.y+dy)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) LineTo(x, y float) *PathConverter {
|
||||||
|
c.x, c.y = x, y
|
||||||
|
if c.startX == c.x && c.startY == c.y {
|
||||||
|
c.converter.NextCommand(VertexCloseCommand)
|
||||||
|
}
|
||||||
|
c.converter.Vertex(c.x, c.y)
|
||||||
|
c.converter.NextCommand(VertexJoinCommand)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) RLineTo(dx, dy float) *PathConverter {
|
||||||
|
c.LineTo(c.x+dx, c.y+dy)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) QuadCurveTo(cx, cy, x, y float) *PathConverter {
|
||||||
|
quadraticBezier(c.converter, c.x, c.y, cx, cy, x, y, c.ApproximationScale, c.AngleTolerance)
|
||||||
|
c.x, c.y = x, y
|
||||||
|
if c.startX == c.x && c.startY == c.y {
|
||||||
|
c.converter.NextCommand(VertexCloseCommand)
|
||||||
|
}
|
||||||
|
c.converter.Vertex(c.x, c.y)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) RQuadCurveTo(dcx, dcy, dx, dy float) *PathConverter {
|
||||||
|
c.QuadCurveTo(c.x+dcx, c.y+dcy, c.x+dx, c.y+dy)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float) *PathConverter {
|
||||||
|
cubicBezier(c.converter, c.x, c.y, cx1, cy1, cx2, cy2, x, y, c.ApproximationScale, c.AngleTolerance, c.CuspLimit)
|
||||||
|
c.x, c.y = x, y
|
||||||
|
if c.startX == c.x && c.startY == c.y {
|
||||||
|
c.converter.NextCommand(VertexCloseCommand)
|
||||||
|
}
|
||||||
|
c.converter.Vertex(c.x, c.y)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float) *PathConverter {
|
||||||
|
c.CubicCurveTo(c.x+dcx1, c.y+dcy1, c.x+dcx2, c.y+dcy2, c.x+dx, c.y+dy)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) ArcTo(cx, cy, rx, ry, startAngle, angle float) *PathConverter {
|
||||||
|
endAngle := startAngle + angle
|
||||||
|
clockWise := true
|
||||||
|
if angle < 0 {
|
||||||
|
clockWise = false
|
||||||
|
}
|
||||||
|
// normalize
|
||||||
|
if clockWise {
|
||||||
|
for endAngle < startAngle {
|
||||||
|
endAngle += math.Pi * 2.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for startAngle < endAngle {
|
||||||
|
startAngle += math.Pi * 2.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startX := cx + cos(startAngle)*rx
|
||||||
|
startY := cy + sin(startAngle)*ry
|
||||||
|
c.MoveTo(startX, startY)
|
||||||
|
c.x, c.y = arc(c.converter, cx, cy, rx, ry, startAngle, angle, c.ApproximationScale)
|
||||||
|
if c.startX == c.x && c.startY == c.y {
|
||||||
|
c.converter.NextCommand(VertexCloseCommand)
|
||||||
|
}
|
||||||
|
c.converter.Vertex(c.x, c.y)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) *PathConverter {
|
||||||
|
c.ArcTo(c.x+dcx, c.y+dcy, rx, ry, startAngle, angle)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PathConverter) Close() *PathConverter {
|
||||||
|
c.converter.NextCommand(VertexCloseCommand)
|
||||||
|
c.converter.Vertex(c.startX, c.startY)
|
||||||
|
return c
|
||||||
|
}
|
|
@ -137,44 +137,6 @@ func (p *PathStorage) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) *PathSto
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PathStorage) TraceLine(tracer LineTracer, approximationScale float) {
|
|
||||||
j := 0
|
|
||||||
x, y := 0.0, 0.0
|
|
||||||
firstX, firstY := x, y
|
|
||||||
if len(p.commands) > 0 {
|
|
||||||
if p.commands[0] == MoveTo {
|
|
||||||
firstX, firstY = p.vertices[0], p.vertices[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, cmd := range p.commands {
|
|
||||||
switch cmd {
|
|
||||||
case MoveTo:
|
|
||||||
tracer.MoveTo(p.vertices[j], p.vertices[j+1])
|
|
||||||
x, y = p.vertices[j], p.vertices[j+1]
|
|
||||||
firstX, firstY = x, y
|
|
||||||
j = j + 2
|
|
||||||
case LineTo:
|
|
||||||
tracer.LineTo(p.vertices[j], p.vertices[j+1])
|
|
||||||
x, y = p.vertices[j], p.vertices[j+1]
|
|
||||||
j = j + 2
|
|
||||||
case QuadCurveTo:
|
|
||||||
quadraticBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], approximationScale, 0)
|
|
||||||
x, y = p.vertices[j+2], p.vertices[j+3]
|
|
||||||
j = j + 4
|
|
||||||
case CubicCurveTo:
|
|
||||||
cubicBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], approximationScale, 0, 0)
|
|
||||||
x, y = p.vertices[j+4], p.vertices[j+5]
|
|
||||||
j = j + 6
|
|
||||||
case ArcTo:
|
|
||||||
arc(tracer, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], approximationScale)
|
|
||||||
j = j + 6
|
|
||||||
case Close:
|
|
||||||
tracer.LineTo(firstX, firstY)
|
|
||||||
x, y = firstX, firstY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PathStorage) String() string {
|
func (p *PathStorage) String() string {
|
||||||
s := ""
|
s := ""
|
||||||
j := 0
|
j := 0
|
||||||
|
|
120
draw2d/src/pkg/draw2d/stroker.go
Normal file
120
draw2d/src/pkg/draw2d/stroker.go
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package draw2d
|
||||||
|
|
||||||
|
type Cap int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoundCap Cap = iota
|
||||||
|
ButtCap
|
||||||
|
SquareCap
|
||||||
|
)
|
||||||
|
|
||||||
|
type Join int
|
||||||
|
|
||||||
|
const (
|
||||||
|
BevelJoin Join = iota
|
||||||
|
RoundJoin
|
||||||
|
MiterJoin
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type LineStroker struct {
|
||||||
|
Next VertexConverter
|
||||||
|
HalfLineWidth float
|
||||||
|
Cap Cap
|
||||||
|
Join Join
|
||||||
|
vertices []float
|
||||||
|
rewind []float
|
||||||
|
x, y, nx, ny float
|
||||||
|
command VertexCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLineStroker(converter VertexConverter) *LineStroker {
|
||||||
|
l := new(LineStroker)
|
||||||
|
l.Next = converter
|
||||||
|
l.HalfLineWidth = 0.5
|
||||||
|
l.vertices = make([]float, 0)
|
||||||
|
l.rewind = make([]float, 0)
|
||||||
|
l.Cap = ButtCap
|
||||||
|
l.Join = MiterJoin
|
||||||
|
l.command = VertexNoCommand
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (l *LineStroker) NextCommand(command VertexCommand) {
|
||||||
|
l.command = command
|
||||||
|
if command == VertexStopCommand {
|
||||||
|
l.Next.NextCommand(VertexStartCommand)
|
||||||
|
for i, j := 0, 1; j < len(l.vertices); i, j = i+2, j+2 {
|
||||||
|
l.Next.Vertex(l.vertices[i], l.vertices[j])
|
||||||
|
l.Next.NextCommand(VertexNoCommand)
|
||||||
|
}
|
||||||
|
for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 {
|
||||||
|
l.Next.NextCommand(VertexNoCommand)
|
||||||
|
l.Next.Vertex(l.rewind[i], l.rewind[j])
|
||||||
|
}
|
||||||
|
if len(l.vertices) > 1 {
|
||||||
|
l.Next.NextCommand(VertexNoCommand)
|
||||||
|
l.Next.Vertex(l.vertices[0], l.vertices[1])
|
||||||
|
}
|
||||||
|
l.Next.NextCommand(VertexStopCommand)
|
||||||
|
// reinit vertices
|
||||||
|
l.vertices = make([]float, 0)
|
||||||
|
l.rewind = make([]float, 0)
|
||||||
|
l.x, l.y, l.nx, l.ny = 0, 0, 0, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LineStroker) Vertex(x, y float) {
|
||||||
|
switch l.command {
|
||||||
|
case VertexNoCommand:
|
||||||
|
l.line(l.x, l.y, x, y)
|
||||||
|
case VertexStartCommand:
|
||||||
|
l.x, l.y = x, y
|
||||||
|
case VertexJoinCommand:
|
||||||
|
l.joinLine(l.x, l.y, l.nx, l.ny, x, y)
|
||||||
|
case VertexCloseCommand:
|
||||||
|
l.line(l.x, l.y, x, y)
|
||||||
|
l.joinLine(l.x, l.y, l.nx, l.ny, x, y)
|
||||||
|
l.closePolygon()
|
||||||
|
}
|
||||||
|
l.command = VertexNoCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LineStroker) closePolygon() {
|
||||||
|
if len(l.vertices) > 1 {
|
||||||
|
l.vertices = append(l.vertices, l.vertices[0], l.vertices[1])
|
||||||
|
l.rewind = append(l.rewind, l.rewind[0], l.rewind[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (l *LineStroker) line(x1, y1, x2, y2 float) {
|
||||||
|
dx := (x2 - x1)
|
||||||
|
dy := (y2 - y1)
|
||||||
|
d := vectorDistance(dx, dy)
|
||||||
|
if d != 0 {
|
||||||
|
nx := dy * l.HalfLineWidth / d
|
||||||
|
ny := -(dx * l.HalfLineWidth / d)
|
||||||
|
l.vertices = append(l.vertices, x1+nx, y1+ny, x2+nx, y2+ny)
|
||||||
|
l.rewind = append(l.rewind, x1-nx, y1-ny, x2-nx, y2-ny)
|
||||||
|
l.x, l.y, l.nx, l.ny = x2, y2, nx, ny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LineStroker) joinLine(x1, y1, nx1, ny1, x2, y2 float) {
|
||||||
|
dx := (x2 - x1)
|
||||||
|
dy := (y2 - y1)
|
||||||
|
d := vectorDistance(dx, dy)
|
||||||
|
|
||||||
|
if d != 0 {
|
||||||
|
nx := dy * l.HalfLineWidth / d
|
||||||
|
ny := -(dx * l.HalfLineWidth / d)
|
||||||
|
/* l.join(x1, y1, x1 + nx, y1 - ny, nx, ny, x1 + ny2, y1 + nx2, nx2, ny2)
|
||||||
|
l.join(x1, y1, x1 - ny1, y1 - nx1, nx1, ny1, x1 - ny2, y1 - nx2, nx2, ny2)*/
|
||||||
|
|
||||||
|
l.vertices = append(l.vertices, x1+nx, y1+ny, x2+nx, y2+ny)
|
||||||
|
l.rewind = append(l.rewind, x1-nx, y1-ny, x2-nx, y2-ny)
|
||||||
|
l.x, l.y, l.nx, l.ny = x2, y2, nx, ny
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
// created: 21/11/2010 by Laurent Le Goff
|
// created: 21/11/2010 by Laurent Le Goff
|
||||||
|
|
||||||
package draw2d
|
package draw2d
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"freetype-go.googlecode.com/hg/freetype/raster"
|
"freetype-go.googlecode.com/hg/freetype/raster"
|
||||||
)
|
)
|
||||||
|
@ -125,35 +126,35 @@ func (tr1 MatrixTransform) Multiply(tr2 MatrixTransform) MatrixTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (tr *MatrixTransform) Scale(sx, sy float) (*MatrixTransform){
|
func (tr *MatrixTransform) Scale(sx, sy float) *MatrixTransform {
|
||||||
tr[0] = tr[0]*sx;
|
tr[0] = tr[0] * sx
|
||||||
tr[1] = tr[1]*sx;
|
tr[1] = tr[1] * sx
|
||||||
tr[4] = tr[4]*sx;
|
tr[4] = tr[4] * sx
|
||||||
tr[2] = tr[2]*sy;
|
tr[2] = tr[2] * sy
|
||||||
tr[3] = tr[3]*sy;
|
tr[3] = tr[3] * sy
|
||||||
tr[5] = tr[5]*sy;
|
tr[5] = tr[5] * sy
|
||||||
return tr;
|
return tr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *MatrixTransform) Translate(tx, ty float) (*MatrixTransform){
|
func (tr *MatrixTransform) Translate(tx, ty float) *MatrixTransform {
|
||||||
tr[4] = tr[4] + tx
|
tr[4] = tr[4] + tx
|
||||||
tr[5] = tr[5] + ty
|
tr[5] = tr[5] + ty
|
||||||
return tr;
|
return tr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *MatrixTransform) Rotate(angle float) (*MatrixTransform){
|
func (tr *MatrixTransform) Rotate(angle float) *MatrixTransform {
|
||||||
ca := cos(angle);
|
ca := cos(angle)
|
||||||
sa := sin(angle);
|
sa := sin(angle)
|
||||||
t0 := tr[0] * ca - tr[1] * sa;
|
t0 := tr[0]*ca - tr[1]*sa
|
||||||
t2 := tr[1] * ca - tr[3] * sa;
|
t2 := tr[1]*ca - tr[3]*sa
|
||||||
t4 := tr[4] * ca - tr[5] * sa;
|
t4 := tr[4]*ca - tr[5]*sa
|
||||||
tr[1] = tr[0] * sa + tr[1] * ca;
|
tr[1] = tr[0]*sa + tr[1]*ca
|
||||||
tr[3] = tr[2] * sa + tr[3] * ca;
|
tr[3] = tr[2]*sa + tr[3]*ca
|
||||||
tr[5] = tr[4] * sa + tr[5] * ca;
|
tr[5] = tr[4]*sa + tr[5]*ca
|
||||||
tr[0] = t0;
|
tr[0] = t0
|
||||||
tr[2] = t2;
|
tr[2] = t2
|
||||||
tr[4] = t4;
|
tr[4] = t4
|
||||||
return tr;
|
return tr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr MatrixTransform) GetTranslation() (x, y float) {
|
func (tr MatrixTransform) GetTranslation() (x, y float) {
|
||||||
|
@ -167,7 +168,7 @@ func (tr MatrixTransform) GetScaling() (x, y float) {
|
||||||
func (tr MatrixTransform) GetMaxAbsScaling() (s float) {
|
func (tr MatrixTransform) GetMaxAbsScaling() (s float) {
|
||||||
sx := fabs(tr[0])
|
sx := fabs(tr[0])
|
||||||
sy := fabs(tr[3])
|
sy := fabs(tr[3])
|
||||||
if(sx > sy) {
|
if sx > sy {
|
||||||
return sx
|
return sx
|
||||||
}
|
}
|
||||||
return sy
|
return sy
|
||||||
|
@ -176,7 +177,7 @@ func (tr MatrixTransform) GetMaxAbsScaling() (s float) {
|
||||||
func (tr MatrixTransform) GetMinAbsScaling() (s float) {
|
func (tr MatrixTransform) GetMinAbsScaling() (s float) {
|
||||||
sx := fabs(tr[0])
|
sx := fabs(tr[0])
|
||||||
sy := fabs(tr[3])
|
sy := fabs(tr[3])
|
||||||
if(sx > sy) {
|
if sx > sy {
|
||||||
return sy
|
return sy
|
||||||
}
|
}
|
||||||
return sx
|
return sx
|
||||||
|
@ -221,13 +222,34 @@ func fequals(float1, float2 float) bool {
|
||||||
return fabs(float1-float2) <= epsilon
|
return fabs(float1-float2) <= epsilon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this VertexConverter apply the Matrix transformation tr
|
||||||
|
type VertexMatrixTransform struct {
|
||||||
|
tr MatrixTransform
|
||||||
|
Next VertexConverter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVertexMatrixTransform(tr MatrixTransform, converter VertexConverter) *VertexMatrixTransform {
|
||||||
|
return &VertexMatrixTransform{tr, converter}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertex Matrix Transform
|
||||||
|
func (vmt *VertexMatrixTransform) NextCommand(command VertexCommand) {
|
||||||
|
vmt.Next.NextCommand(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vmt *VertexMatrixTransform) Vertex(x, y float) {
|
||||||
|
vmt.tr.Transform(&x, &y)
|
||||||
|
vmt.Next.Vertex(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// this adder apply a Matrix transformation to points
|
// this adder apply a Matrix transformation to points
|
||||||
type MatrixTransformAdder struct {
|
type MatrixTransformAdder struct {
|
||||||
tr MatrixTransform
|
tr MatrixTransform
|
||||||
next raster.Adder
|
next raster.Adder
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMatrixTransformAdder(tr MatrixTransform, adder raster.Adder) (*MatrixTransformAdder) {
|
func NewMatrixTransformAdder(tr MatrixTransform, adder raster.Adder) *MatrixTransformAdder {
|
||||||
return &MatrixTransformAdder{tr, adder}
|
return &MatrixTransformAdder{tr, adder}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,4 +277,3 @@ func (mta MatrixTransformAdder) Add3(b, c, d raster.Point) {
|
||||||
mta.tr.TransformRasterPoint(&b, &c, &d)
|
mta.tr.TransformRasterPoint(&b, &c, &d)
|
||||||
mta.next.Add3(b, c, d)
|
mta.next.Add3(b, c, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue