moved math code to backendbase package so that backends can also use it
This commit is contained in:
parent
066f4f55bb
commit
cc9247c627
9 changed files with 364 additions and 357 deletions
140
backend/backendbase/math.go
Normal file
140
backend/backendbase/math.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package backendbase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type Vec [2]float64
|
||||
|
||||
func (v Vec) String() string {
|
||||
return fmt.Sprintf("[%f,%f]", v[0], v[1])
|
||||
}
|
||||
|
||||
func (v Vec) Add(v2 Vec) Vec {
|
||||
return Vec{v[0] + v2[0], v[1] + v2[1]}
|
||||
}
|
||||
|
||||
func (v Vec) Sub(v2 Vec) Vec {
|
||||
return Vec{v[0] - v2[0], v[1] - v2[1]}
|
||||
}
|
||||
|
||||
func (v Vec) Mul(v2 Vec) Vec {
|
||||
return Vec{v[0] * v2[0], v[1] * v2[1]}
|
||||
}
|
||||
|
||||
func (v Vec) Mulf(f float64) Vec {
|
||||
return Vec{v[0] * f, v[1] * f}
|
||||
}
|
||||
|
||||
func (v Vec) MulMat(m Mat) Vec {
|
||||
return Vec{
|
||||
m[0]*v[0] + m[2]*v[1] + m[4],
|
||||
m[1]*v[0] + m[3]*v[1] + m[5]}
|
||||
}
|
||||
|
||||
func (v Vec) MulMat2(m Mat2) Vec {
|
||||
return Vec{m[0]*v[0] + m[2]*v[1], m[1]*v[0] + m[3]*v[1]}
|
||||
}
|
||||
|
||||
func (v Vec) Div(v2 Vec) Vec {
|
||||
return Vec{v[0] / v2[0], v[1] / v2[1]}
|
||||
}
|
||||
|
||||
func (v Vec) Divf(f float64) Vec {
|
||||
return Vec{v[0] / f, v[1] / f}
|
||||
}
|
||||
|
||||
func (v Vec) Dot(v2 Vec) float64 {
|
||||
return v[0]*v2[0] + v[1]*v2[1]
|
||||
}
|
||||
|
||||
func (v Vec) Len() float64 {
|
||||
return math.Sqrt(v[0]*v[0] + v[1]*v[1])
|
||||
}
|
||||
|
||||
func (v Vec) LenSqr() float64 {
|
||||
return v[0]*v[0] + v[1]*v[1]
|
||||
}
|
||||
|
||||
func (v Vec) Norm() Vec {
|
||||
return v.Mulf(1.0 / v.Len())
|
||||
}
|
||||
|
||||
func (v Vec) Atan2() float64 {
|
||||
return math.Atan2(v[1], v[0])
|
||||
}
|
||||
|
||||
func (v Vec) Angle() float64 {
|
||||
return math.Pi*0.5 - math.Atan2(v[1], v[0])
|
||||
}
|
||||
|
||||
func (v Vec) AngleTo(v2 Vec) float64 {
|
||||
return math.Acos(v.Norm().Dot(v2.Norm()))
|
||||
}
|
||||
|
||||
type Mat [6]float64
|
||||
|
||||
func (m *Mat) String() string {
|
||||
return fmt.Sprintf("[%f,%f,0,\n %f,%f,0,\n %f,%f,1,]", m[0], m[2], m[4], m[1], m[3], m[5])
|
||||
}
|
||||
|
||||
var MatIdentity = Mat{
|
||||
1, 0,
|
||||
0, 1,
|
||||
0, 0}
|
||||
|
||||
func MatTranslate(v Vec) Mat {
|
||||
return Mat{
|
||||
1, 0,
|
||||
0, 1,
|
||||
v[0], v[1]}
|
||||
}
|
||||
|
||||
func MatScale(v Vec) Mat {
|
||||
return Mat{
|
||||
v[0], 0,
|
||||
0, v[1],
|
||||
0, 0}
|
||||
}
|
||||
|
||||
func MatRotate(radians float64) Mat {
|
||||
s, c := math.Sincos(radians)
|
||||
return Mat{
|
||||
c, s,
|
||||
-s, c,
|
||||
0, 0}
|
||||
}
|
||||
|
||||
func (m Mat) Mul(m2 Mat) Mat {
|
||||
return Mat{
|
||||
m[0]*m2[0] + m[1]*m2[2],
|
||||
m[0]*m2[1] + m[1]*m2[3],
|
||||
m[2]*m2[0] + m[3]*m2[2],
|
||||
m[2]*m2[1] + m[3]*m2[3],
|
||||
m[4]*m2[0] + m[5]*m2[2] + m2[4],
|
||||
m[4]*m2[1] + m[5]*m2[3] + m2[5]}
|
||||
}
|
||||
|
||||
func (m Mat) Invert() Mat {
|
||||
identity := 1.0 / (m[0]*m[3] - m[2]*m[1])
|
||||
|
||||
return Mat{
|
||||
m[3] * identity,
|
||||
-m[1] * identity,
|
||||
-m[2] * identity,
|
||||
m[0] * identity,
|
||||
(m[2]*m[5] - m[3]*m[4]) * identity,
|
||||
(m[1]*m[4] - m[0]*m[5]) * identity,
|
||||
}
|
||||
}
|
||||
|
||||
type Mat2 [4]float64
|
||||
|
||||
func (m Mat) Mat2() Mat2 {
|
||||
return Mat2{m[0], m[1], m[2], m[3]}
|
||||
}
|
||||
|
||||
func (m *Mat2) String() string {
|
||||
return fmt.Sprintf("[%f,%f,\n %f,%f]", m[0], m[2], m[1], m[3])
|
||||
}
|
30
canvas.go
30
canvas.go
|
@ -35,7 +35,7 @@ type Canvas struct {
|
|||
}
|
||||
|
||||
type drawState struct {
|
||||
transform mat
|
||||
transform backendbase.Mat
|
||||
fill drawStyle
|
||||
stroke drawStyle
|
||||
font *Font
|
||||
|
@ -150,7 +150,7 @@ func New(backend backendbase.Backend) *Canvas {
|
|||
cv.state.globalAlpha = 1
|
||||
cv.state.fill.color = color.RGBA{A: 255}
|
||||
cv.state.stroke.color = color.RGBA{A: 255}
|
||||
cv.state.transform = matIdentity
|
||||
cv.state.transform = backendbase.MatIdentity
|
||||
cv.path.cv = cv
|
||||
return cv
|
||||
}
|
||||
|
@ -170,8 +170,8 @@ func (cv *Canvas) Height() int {
|
|||
// Size returns the internal width and height of the canvas
|
||||
func (cv *Canvas) Size() (int, int) { return cv.b.Size() }
|
||||
|
||||
func (cv *Canvas) tf(v vec) vec {
|
||||
return v.mulMat(cv.state.transform)
|
||||
func (cv *Canvas) tf(v backendbase.Vec) backendbase.Vec {
|
||||
return v.MulMat(cv.state.transform)
|
||||
}
|
||||
|
||||
const alphaTexSize = 2048
|
||||
|
@ -380,7 +380,7 @@ func (cv *Canvas) Restore() {
|
|||
cv.b.ClearClip()
|
||||
for _, st := range cv.stateStack {
|
||||
if len(st.clip.p) > 0 {
|
||||
cv.clip(&st.clip, matIdentity)
|
||||
cv.clip(&st.clip, backendbase.MatIdentity)
|
||||
}
|
||||
}
|
||||
cv.state = cv.stateStack[l-1]
|
||||
|
@ -389,27 +389,27 @@ func (cv *Canvas) Restore() {
|
|||
|
||||
// Scale updates the current transformation with a scaling by the given values
|
||||
func (cv *Canvas) Scale(x, y float64) {
|
||||
cv.state.transform = matScale(vec{x, y}).mul(cv.state.transform)
|
||||
cv.state.transform = backendbase.MatScale(backendbase.Vec{x, y}).Mul(cv.state.transform)
|
||||
}
|
||||
|
||||
// Translate updates the current transformation with a translation by the given values
|
||||
func (cv *Canvas) Translate(x, y float64) {
|
||||
cv.state.transform = matTranslate(vec{x, y}).mul(cv.state.transform)
|
||||
cv.state.transform = backendbase.MatTranslate(backendbase.Vec{x, y}).Mul(cv.state.transform)
|
||||
}
|
||||
|
||||
// Rotate updates the current transformation with a rotation by the given angle
|
||||
func (cv *Canvas) Rotate(angle float64) {
|
||||
cv.state.transform = matRotate(angle).mul(cv.state.transform)
|
||||
cv.state.transform = backendbase.MatRotate(angle).Mul(cv.state.transform)
|
||||
}
|
||||
|
||||
// Transform updates the current transformation with the given matrix
|
||||
func (cv *Canvas) Transform(a, b, c, d, e, f float64) {
|
||||
cv.state.transform = mat{a, b, c, d, e, f}.mul(cv.state.transform)
|
||||
cv.state.transform = backendbase.Mat{a, b, c, d, e, f}.Mul(cv.state.transform)
|
||||
}
|
||||
|
||||
// SetTransform replaces the current transformation with the given matrix
|
||||
func (cv *Canvas) SetTransform(a, b, c, d, e, f float64) {
|
||||
cv.state.transform = mat{a, b, c, d, e, f}
|
||||
cv.state.transform = backendbase.Mat{a, b, c, d, e, f}
|
||||
}
|
||||
|
||||
// SetShadowColor sets the color of the shadow. If it is fully transparent (default)
|
||||
|
@ -456,14 +456,14 @@ func (cv *Canvas) IsPointInStroke(x, y float64) bool {
|
|||
}
|
||||
|
||||
var triBuf [500][2]float64
|
||||
tris := cv.strokeTris(&cv.path, cv.state.transform.invert(), true, triBuf[:0])
|
||||
tris := cv.strokeTris(&cv.path, cv.state.transform.Invert(), true, triBuf[:0])
|
||||
|
||||
pt := vec{x, y}
|
||||
pt := backendbase.Vec{x, y}
|
||||
|
||||
for i := 0; i < len(tris); i += 3 {
|
||||
a := vec{tris[i][0], tris[i][1]}
|
||||
b := vec{tris[i+1][0], tris[i+1][1]}
|
||||
c := vec{tris[i+2][0], tris[i+2][1]}
|
||||
a := backendbase.Vec{tris[i][0], tris[i][1]}
|
||||
b := backendbase.Vec{tris[i+1][0], tris[i+1][1]}
|
||||
c := backendbase.Vec{tris[i+2][0], tris[i+2][1]}
|
||||
if triangleContainsPoint(a, b, c, pt) {
|
||||
return true
|
||||
}
|
||||
|
|
12
gradients.go
12
gradients.go
|
@ -13,7 +13,7 @@ import (
|
|||
// will correspond to a straight line
|
||||
type LinearGradient struct {
|
||||
cv *Canvas
|
||||
from, to vec
|
||||
from, to backendbase.Vec
|
||||
created bool
|
||||
loaded bool
|
||||
opaque bool
|
||||
|
@ -27,7 +27,7 @@ type LinearGradient struct {
|
|||
// will correspond to a circle
|
||||
type RadialGradient struct {
|
||||
cv *Canvas
|
||||
from, to vec
|
||||
from, to backendbase.Vec
|
||||
radFrom float64
|
||||
radTo float64
|
||||
created bool
|
||||
|
@ -44,8 +44,8 @@ func (cv *Canvas) CreateLinearGradient(x0, y0, x1, y1 float64) *LinearGradient {
|
|||
lg := &LinearGradient{
|
||||
cv: cv,
|
||||
opaque: true,
|
||||
from: vec{x0, y0},
|
||||
to: vec{x1, y1},
|
||||
from: backendbase.Vec{x0, y0},
|
||||
to: backendbase.Vec{x1, y1},
|
||||
data: make(backendbase.Gradient, 0, 20),
|
||||
}
|
||||
runtime.SetFinalizer(lg, func(*LinearGradient) {
|
||||
|
@ -62,8 +62,8 @@ func (cv *Canvas) CreateRadialGradient(x0, y0, r0, x1, y1, r1 float64) *RadialGr
|
|||
rg := &RadialGradient{
|
||||
cv: cv,
|
||||
opaque: true,
|
||||
from: vec{x0, y0},
|
||||
to: vec{x1, y1},
|
||||
from: backendbase.Vec{x0, y0},
|
||||
to: backendbase.Vec{x1, y1},
|
||||
radFrom: r0,
|
||||
radTo: r1,
|
||||
data: make(backendbase.Gradient, 0, 20),
|
||||
|
|
18
images.go
18
images.go
|
@ -182,10 +182,10 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float64) {
|
|||
}
|
||||
|
||||
var data [4][2]float64
|
||||
data[0] = cv.tf(vec{dx, dy})
|
||||
data[1] = cv.tf(vec{dx, dy + dh})
|
||||
data[2] = cv.tf(vec{dx + dw, dy + dh})
|
||||
data[3] = cv.tf(vec{dx + dw, dy})
|
||||
data[0] = cv.tf(backendbase.Vec{dx, dy})
|
||||
data[1] = cv.tf(backendbase.Vec{dx, dy + dh})
|
||||
data[2] = cv.tf(backendbase.Vec{dx + dw, dy + dh})
|
||||
data[3] = cv.tf(backendbase.Vec{dx + dw, dy})
|
||||
|
||||
cv.drawShadow(data[:], nil, false)
|
||||
|
||||
|
@ -207,7 +207,7 @@ func (cv *Canvas) PutImageData(img *image.RGBA, x, y int) {
|
|||
type ImagePattern struct {
|
||||
cv *Canvas
|
||||
img *Image
|
||||
tf mat
|
||||
tf backendbase.Mat
|
||||
rep imagePatternRepeat
|
||||
ip backendbase.ImagePattern
|
||||
}
|
||||
|
@ -222,8 +222,8 @@ const (
|
|||
NoRepeat = imagePatternRepeat(backendbase.NoRepeat)
|
||||
)
|
||||
|
||||
func (ip *ImagePattern) data(tf mat) backendbase.ImagePatternData {
|
||||
m := tf.invert().mul(ip.tf.invert())
|
||||
func (ip *ImagePattern) data(tf backendbase.Mat) backendbase.ImagePatternData {
|
||||
m := tf.Invert().Mul(ip.tf.Invert())
|
||||
return backendbase.ImagePatternData{
|
||||
Image: ip.img.img,
|
||||
Transform: [9]float64{
|
||||
|
@ -239,7 +239,7 @@ func (ip *ImagePattern) data(tf mat) backendbase.ImagePatternData {
|
|||
// to the given matrix. The matrix is a 3x3 matrix, but three
|
||||
// of the values are always identity values
|
||||
func (ip *ImagePattern) SetTransform(tf [6]float64) {
|
||||
ip.tf = mat(tf)
|
||||
ip.tf = backendbase.Mat(tf)
|
||||
}
|
||||
|
||||
// CreatePattern creates a new image pattern with the specified
|
||||
|
@ -249,7 +249,7 @@ func (cv *Canvas) CreatePattern(src interface{}, repeat imagePatternRepeat) *Ima
|
|||
cv: cv,
|
||||
img: cv.getImage(src),
|
||||
rep: repeat,
|
||||
tf: mat{1, 0, 0, 1, 0, 0},
|
||||
tf: backendbase.Mat{1, 0, 0, 1, 0, 0},
|
||||
}
|
||||
if ip.img != nil {
|
||||
ip.ip = cv.b.LoadImagePattern(ip.data(cv.state.transform))
|
||||
|
|
140
math.go
140
math.go
|
@ -1,140 +0,0 @@
|
|||
package canvas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type vec [2]float64
|
||||
|
||||
func (v vec) String() string {
|
||||
return fmt.Sprintf("[%f,%f]", v[0], v[1])
|
||||
}
|
||||
|
||||
func (v vec) add(v2 vec) vec {
|
||||
return vec{v[0] + v2[0], v[1] + v2[1]}
|
||||
}
|
||||
|
||||
func (v vec) sub(v2 vec) vec {
|
||||
return vec{v[0] - v2[0], v[1] - v2[1]}
|
||||
}
|
||||
|
||||
func (v vec) mul(v2 vec) vec {
|
||||
return vec{v[0] * v2[0], v[1] * v2[1]}
|
||||
}
|
||||
|
||||
func (v vec) mulf(f float64) vec {
|
||||
return vec{v[0] * f, v[1] * f}
|
||||
}
|
||||
|
||||
func (v vec) mulMat(m mat) vec {
|
||||
return vec{
|
||||
m[0]*v[0] + m[2]*v[1] + m[4],
|
||||
m[1]*v[0] + m[3]*v[1] + m[5]}
|
||||
}
|
||||
|
||||
func (v vec) mulMat2(m mat2) vec {
|
||||
return vec{m[0]*v[0] + m[2]*v[1], m[1]*v[0] + m[3]*v[1]}
|
||||
}
|
||||
|
||||
func (v vec) div(v2 vec) vec {
|
||||
return vec{v[0] / v2[0], v[1] / v2[1]}
|
||||
}
|
||||
|
||||
func (v vec) divf(f float64) vec {
|
||||
return vec{v[0] / f, v[1] / f}
|
||||
}
|
||||
|
||||
func (v vec) dot(v2 vec) float64 {
|
||||
return v[0]*v2[0] + v[1]*v2[1]
|
||||
}
|
||||
|
||||
func (v vec) len() float64 {
|
||||
return math.Sqrt(v[0]*v[0] + v[1]*v[1])
|
||||
}
|
||||
|
||||
func (v vec) lenSqr() float64 {
|
||||
return v[0]*v[0] + v[1]*v[1]
|
||||
}
|
||||
|
||||
func (v vec) norm() vec {
|
||||
return v.mulf(1.0 / v.len())
|
||||
}
|
||||
|
||||
func (v vec) atan2() float64 {
|
||||
return math.Atan2(v[1], v[0])
|
||||
}
|
||||
|
||||
func (v vec) angle() float64 {
|
||||
return math.Pi*0.5 - math.Atan2(v[1], v[0])
|
||||
}
|
||||
|
||||
func (v vec) angleTo(v2 vec) float64 {
|
||||
return math.Acos(v.norm().dot(v2.norm()))
|
||||
}
|
||||
|
||||
type mat [6]float64
|
||||
|
||||
func (m *mat) String() string {
|
||||
return fmt.Sprintf("[%f,%f,0,\n %f,%f,0,\n %f,%f,1,]", m[0], m[2], m[4], m[1], m[3], m[5])
|
||||
}
|
||||
|
||||
var matIdentity = mat{
|
||||
1, 0,
|
||||
0, 1,
|
||||
0, 0}
|
||||
|
||||
func matTranslate(v vec) mat {
|
||||
return mat{
|
||||
1, 0,
|
||||
0, 1,
|
||||
v[0], v[1]}
|
||||
}
|
||||
|
||||
func matScale(v vec) mat {
|
||||
return mat{
|
||||
v[0], 0,
|
||||
0, v[1],
|
||||
0, 0}
|
||||
}
|
||||
|
||||
func matRotate(radians float64) mat {
|
||||
s, c := math.Sincos(radians)
|
||||
return mat{
|
||||
c, s,
|
||||
-s, c,
|
||||
0, 0}
|
||||
}
|
||||
|
||||
func (m mat) mul(m2 mat) mat {
|
||||
return mat{
|
||||
m[0]*m2[0] + m[1]*m2[2],
|
||||
m[0]*m2[1] + m[1]*m2[3],
|
||||
m[2]*m2[0] + m[3]*m2[2],
|
||||
m[2]*m2[1] + m[3]*m2[3],
|
||||
m[4]*m2[0] + m[5]*m2[2] + m2[4],
|
||||
m[4]*m2[1] + m[5]*m2[3] + m2[5]}
|
||||
}
|
||||
|
||||
func (m mat) invert() mat {
|
||||
identity := 1.0 / (m[0]*m[3] - m[2]*m[1])
|
||||
|
||||
return mat{
|
||||
m[3] * identity,
|
||||
-m[1] * identity,
|
||||
-m[2] * identity,
|
||||
m[0] * identity,
|
||||
(m[2]*m[5] - m[3]*m[4]) * identity,
|
||||
(m[1]*m[4] - m[0]*m[5]) * identity,
|
||||
}
|
||||
}
|
||||
|
||||
type mat2 [4]float64
|
||||
|
||||
func (m mat) mat2() mat2 {
|
||||
return mat2{m[0], m[1], m[2], m[3]}
|
||||
}
|
||||
|
||||
func (m *mat2) String() string {
|
||||
return fmt.Sprintf("[%f,%f,\n %f,%f]", m[0], m[2], m[1], m[3])
|
||||
}
|
132
path2d.go
132
path2d.go
|
@ -2,6 +2,8 @@ package canvas
|
|||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
)
|
||||
|
||||
// Path2D is a type that holds a predefined path which can be drawn
|
||||
|
@ -9,13 +11,13 @@ import (
|
|||
type Path2D struct {
|
||||
cv *Canvas
|
||||
p []pathPoint
|
||||
move vec
|
||||
move backendbase.Vec
|
||||
cwSum float64
|
||||
}
|
||||
|
||||
type pathPoint struct {
|
||||
pos vec
|
||||
next vec
|
||||
pos backendbase.Vec
|
||||
next backendbase.Vec
|
||||
flags pathPointFlag
|
||||
}
|
||||
|
||||
|
@ -40,12 +42,12 @@ func (cv *Canvas) NewPath2D() *Path2D {
|
|||
|
||||
// MoveTo (see equivalent function on canvas type)
|
||||
func (p *Path2D) MoveTo(x, y float64) {
|
||||
if len(p.p) > 0 && isSamePoint(p.p[len(p.p)-1].pos, vec{x, y}, 0.1) {
|
||||
if len(p.p) > 0 && isSamePoint(p.p[len(p.p)-1].pos, backendbase.Vec{x, y}, 0.1) {
|
||||
return
|
||||
}
|
||||
p.p = append(p.p, pathPoint{pos: vec{x, y}, flags: pathMove | pathIsConvex})
|
||||
p.p = append(p.p, pathPoint{pos: backendbase.Vec{x, y}, flags: pathMove | pathIsConvex})
|
||||
p.cwSum = 0
|
||||
p.move = vec{x, y}
|
||||
p.move = backendbase.Vec{x, y}
|
||||
}
|
||||
|
||||
// LineTo (see equivalent function on canvas type)
|
||||
|
@ -55,7 +57,7 @@ func (p *Path2D) LineTo(x, y float64) {
|
|||
|
||||
func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
||||
count := len(p.p)
|
||||
if count > 0 && isSamePoint(p.p[len(p.p)-1].pos, vec{x, y}, 0.1) {
|
||||
if count > 0 && isSamePoint(p.p[len(p.p)-1].pos, backendbase.Vec{x, y}, 0.1) {
|
||||
return
|
||||
}
|
||||
if count == 0 {
|
||||
|
@ -63,9 +65,9 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
|||
return
|
||||
}
|
||||
prev := &p.p[count-1]
|
||||
prev.next = vec{x, y}
|
||||
prev.next = backendbase.Vec{x, y}
|
||||
prev.flags |= pathAttach
|
||||
p.p = append(p.p, pathPoint{pos: vec{x, y}})
|
||||
p.p = append(p.p, pathPoint{pos: backendbase.Vec{x, y}})
|
||||
newp := &p.p[count]
|
||||
|
||||
if prev.flags&pathIsConvex > 0 {
|
||||
|
@ -84,9 +86,9 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
|||
prev2 := &p.p[count-2]
|
||||
cw := (prev.flags & pathIsClockwise) > 0
|
||||
|
||||
ln := prev.pos.sub(prev2.pos)
|
||||
lo := vec{ln[1], -ln[0]}
|
||||
dot := newp.pos.sub(prev2.pos).dot(lo)
|
||||
ln := prev.pos.Sub(prev2.pos)
|
||||
lo := backendbase.Vec{ln[1], -ln[0]}
|
||||
dot := newp.pos.Sub(prev2.pos).Dot(lo)
|
||||
|
||||
if (cw && dot <= 0) || (!cw && dot >= 0) {
|
||||
newp.flags |= pathIsConvex
|
||||
|
@ -97,8 +99,8 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
|||
newp.flags |= pathSelfIntersects
|
||||
} else if newp.flags&pathIsConvex == 0 && newp.flags&pathSelfIntersects == 0 && checkSelfIntersection && !Performance.IgnoreSelfIntersections {
|
||||
cuts := false
|
||||
var cutPoint vec
|
||||
b0, b1 := prev.pos, vec{x, y}
|
||||
var cutPoint backendbase.Vec
|
||||
b0, b1 := prev.pos, backendbase.Vec{x, y}
|
||||
for i := 1; i < count; i++ {
|
||||
a0, a1 := p.p[i-1].pos, p.p[i].pos
|
||||
var r1, r2 float64
|
||||
|
@ -108,7 +110,7 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
|||
break
|
||||
}
|
||||
}
|
||||
if cuts && !isSamePoint(cutPoint, vec{x, y}, samePointTolerance) {
|
||||
if cuts && !isSamePoint(cutPoint, backendbase.Vec{x, y}, samePointTolerance) {
|
||||
newp.flags |= pathSelfIntersects
|
||||
}
|
||||
}
|
||||
|
@ -116,19 +118,19 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
|||
|
||||
// Arc (see equivalent function on canvas type)
|
||||
func (p *Path2D) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise bool) {
|
||||
p.arc(x, y, radius, startAngle, endAngle, anticlockwise, matIdentity, true)
|
||||
p.arc(x, y, radius, startAngle, endAngle, anticlockwise, backendbase.MatIdentity, true)
|
||||
}
|
||||
|
||||
func (p *Path2D) arc(x, y, radius, startAngle, endAngle float64, anticlockwise bool, m mat, ident bool) {
|
||||
func (p *Path2D) arc(x, y, radius, startAngle, endAngle float64, anticlockwise bool, m backendbase.Mat, ident bool) {
|
||||
checkSelfIntersection := len(p.p) > 0
|
||||
|
||||
lastWasMove := len(p.p) == 0 || p.p[len(p.p)-1].flags&pathMove != 0
|
||||
|
||||
if endAngle == startAngle {
|
||||
s, c := math.Sincos(endAngle)
|
||||
pt := vec{x + radius*c, y + radius*s}
|
||||
pt := backendbase.Vec{x + radius*c, y + radius*s}
|
||||
if !ident {
|
||||
pt = pt.mulMat(m)
|
||||
pt = pt.MulMat(m)
|
||||
}
|
||||
p.lineTo(pt[0], pt[1], checkSelfIntersection)
|
||||
|
||||
|
@ -163,26 +165,26 @@ func (p *Path2D) arc(x, y, radius, startAngle, endAngle float64, anticlockwise b
|
|||
if !anticlockwise {
|
||||
for a := startAngle; a < endAngle; a += step {
|
||||
s, c := math.Sincos(a)
|
||||
pt := vec{x + radius*c, y + radius*s}
|
||||
pt := backendbase.Vec{x + radius*c, y + radius*s}
|
||||
if !ident {
|
||||
pt = pt.mulMat(m)
|
||||
pt = pt.MulMat(m)
|
||||
}
|
||||
p.lineTo(pt[0], pt[1], checkSelfIntersection)
|
||||
}
|
||||
} else {
|
||||
for a := startAngle; a > endAngle; a -= step {
|
||||
s, c := math.Sincos(a)
|
||||
pt := vec{x + radius*c, y + radius*s}
|
||||
pt := backendbase.Vec{x + radius*c, y + radius*s}
|
||||
if !ident {
|
||||
pt = pt.mulMat(m)
|
||||
pt = pt.MulMat(m)
|
||||
}
|
||||
p.lineTo(pt[0], pt[1], checkSelfIntersection)
|
||||
}
|
||||
}
|
||||
s, c := math.Sincos(endAngle)
|
||||
pt := vec{x + radius*c, y + radius*s}
|
||||
pt := backendbase.Vec{x + radius*c, y + radius*s}
|
||||
if !ident {
|
||||
pt = pt.mulMat(m)
|
||||
pt = pt.MulMat(m)
|
||||
}
|
||||
p.lineTo(pt[0], pt[1], checkSelfIntersection)
|
||||
|
||||
|
@ -193,34 +195,34 @@ func (p *Path2D) arc(x, y, radius, startAngle, endAngle float64, anticlockwise b
|
|||
|
||||
// ArcTo (see equivalent function on canvas type)
|
||||
func (p *Path2D) ArcTo(x1, y1, x2, y2, radius float64) {
|
||||
p.arcTo(x1, y1, x2, y2, radius, matIdentity, true)
|
||||
p.arcTo(x1, y1, x2, y2, radius, backendbase.MatIdentity, true)
|
||||
}
|
||||
|
||||
func (p *Path2D) arcTo(x1, y1, x2, y2, radius float64, m mat, ident bool) {
|
||||
func (p *Path2D) arcTo(x1, y1, x2, y2, radius float64, m backendbase.Mat, ident bool) {
|
||||
if len(p.p) == 0 {
|
||||
return
|
||||
}
|
||||
p0, p1, p2 := p.p[len(p.p)-1].pos, vec{x1, y1}, vec{x2, y2}
|
||||
p0, p1, p2 := p.p[len(p.p)-1].pos, backendbase.Vec{x1, y1}, backendbase.Vec{x2, y2}
|
||||
if !ident {
|
||||
p0 = p0.mulMat(m.invert())
|
||||
p0 = p0.MulMat(m.Invert())
|
||||
}
|
||||
v0, v1 := p0.sub(p1).norm(), p2.sub(p1).norm()
|
||||
angle := math.Acos(v0.dot(v1))
|
||||
v0, v1 := p0.Sub(p1).Norm(), p2.Sub(p1).Norm()
|
||||
angle := math.Acos(v0.Dot(v1))
|
||||
// should be in the range [0-pi]. if parallel, use a straight line
|
||||
if angle <= 0 || angle >= math.Pi {
|
||||
p.LineTo(x2, y2)
|
||||
return
|
||||
}
|
||||
// cv0 and cv1 are vectors that point to the center of the circle
|
||||
cv0 := vec{-v0[1], v0[0]}
|
||||
cv1 := vec{v1[1], -v1[0]}
|
||||
x := cv1.sub(cv0).div(v0.sub(v1))[0] * radius
|
||||
cv0 := backendbase.Vec{-v0[1], v0[0]}
|
||||
cv1 := backendbase.Vec{v1[1], -v1[0]}
|
||||
x := cv1.Sub(cv0).Div(v0.Sub(v1))[0] * radius
|
||||
if x < 0 {
|
||||
cv0 = cv0.mulf(-1)
|
||||
cv1 = cv1.mulf(-1)
|
||||
cv0 = cv0.Mulf(-1)
|
||||
cv1 = cv1.Mulf(-1)
|
||||
}
|
||||
center := p1.add(v0.mulf(math.Abs(x))).add(cv0.mulf(radius))
|
||||
a0, a1 := cv0.mulf(-1).atan2(), cv1.mulf(-1).atan2()
|
||||
center := p1.Add(v0.Mulf(math.Abs(x))).Add(cv0.Mulf(radius))
|
||||
a0, a1 := cv0.Mulf(-1).Atan2(), cv1.Mulf(-1).Atan2()
|
||||
if x > 0 {
|
||||
if a1-a0 > 0 {
|
||||
a0 += math.Pi * 2
|
||||
|
@ -239,17 +241,17 @@ func (p *Path2D) QuadraticCurveTo(x1, y1, x2, y2 float64) {
|
|||
return
|
||||
}
|
||||
p0 := p.p[len(p.p)-1].pos
|
||||
p1 := vec{x1, y1}
|
||||
p2 := vec{x2, y2}
|
||||
v0 := p1.sub(p0)
|
||||
v1 := p2.sub(p1)
|
||||
p1 := backendbase.Vec{x1, y1}
|
||||
p2 := backendbase.Vec{x2, y2}
|
||||
v0 := p1.Sub(p0)
|
||||
v1 := p2.Sub(p1)
|
||||
|
||||
const step = 0.01
|
||||
|
||||
for r := 0.0; r < 1; r += step {
|
||||
i0 := v0.mulf(r).add(p0)
|
||||
i1 := v1.mulf(r).add(p1)
|
||||
pt := i1.sub(i0).mulf(r).add(i0)
|
||||
i0 := v0.Mulf(r).Add(p0)
|
||||
i1 := v1.Mulf(r).Add(p1)
|
||||
pt := i1.Sub(i0).Mulf(r).Add(i0)
|
||||
p.LineTo(pt[0], pt[1])
|
||||
}
|
||||
p.LineTo(x2, y2)
|
||||
|
@ -261,24 +263,24 @@ func (p *Path2D) BezierCurveTo(x1, y1, x2, y2, x3, y3 float64) {
|
|||
return
|
||||
}
|
||||
p0 := p.p[len(p.p)-1].pos
|
||||
p1 := vec{x1, y1}
|
||||
p2 := vec{x2, y2}
|
||||
p3 := vec{x3, y3}
|
||||
v0 := p1.sub(p0)
|
||||
v1 := p2.sub(p1)
|
||||
v2 := p3.sub(p2)
|
||||
p1 := backendbase.Vec{x1, y1}
|
||||
p2 := backendbase.Vec{x2, y2}
|
||||
p3 := backendbase.Vec{x3, y3}
|
||||
v0 := p1.Sub(p0)
|
||||
v1 := p2.Sub(p1)
|
||||
v2 := p3.Sub(p2)
|
||||
|
||||
const step = 0.01
|
||||
|
||||
for r := 0.0; r < 1; r += step {
|
||||
i0 := v0.mulf(r).add(p0)
|
||||
i1 := v1.mulf(r).add(p1)
|
||||
i2 := v2.mulf(r).add(p2)
|
||||
iv0 := i1.sub(i0)
|
||||
iv1 := i2.sub(i1)
|
||||
j0 := iv0.mulf(r).add(i0)
|
||||
j1 := iv1.mulf(r).add(i1)
|
||||
pt := j1.sub(j0).mulf(r).add(j0)
|
||||
i0 := v0.Mulf(r).Add(p0)
|
||||
i1 := v1.Mulf(r).Add(p1)
|
||||
i2 := v2.Mulf(r).Add(p2)
|
||||
iv0 := i1.Sub(i0)
|
||||
iv1 := i2.Sub(i1)
|
||||
j0 := iv0.Mulf(r).Add(i0)
|
||||
j1 := iv1.Mulf(r).Add(i1)
|
||||
pt := j1.Sub(j0).Mulf(r).Add(j0)
|
||||
p.LineTo(pt[0], pt[1])
|
||||
}
|
||||
p.LineTo(x3, y3)
|
||||
|
@ -434,7 +436,7 @@ func (p *Path2D) IsPointInPath(x, y float64, rule pathRule) bool {
|
|||
num := 0
|
||||
prev := sp[len(sp)-1].pos
|
||||
for _, pt := range sp {
|
||||
r, dir := pointIsRightOfLine(prev, pt.pos, vec{x, y})
|
||||
r, dir := pointIsRightOfLine(prev, pt.pos, backendbase.Vec{x, y})
|
||||
prev = pt.pos
|
||||
if !r {
|
||||
continue
|
||||
|
@ -464,14 +466,14 @@ func (p *Path2D) IsPointInStroke(x, y float64) bool {
|
|||
}
|
||||
|
||||
var triBuf [500][2]float64
|
||||
tris := p.cv.strokeTris(p, mat{}, false, triBuf[:0])
|
||||
tris := p.cv.strokeTris(p, backendbase.Mat{}, false, triBuf[:0])
|
||||
|
||||
pt := vec{x, y}
|
||||
pt := backendbase.Vec{x, y}
|
||||
|
||||
for i := 0; i < len(tris); i += 3 {
|
||||
a := vec{tris[i][0], tris[i][1]}
|
||||
b := vec{tris[i+1][0], tris[i+1][1]}
|
||||
c := vec{tris[i+2][0], tris[i+2][1]}
|
||||
a := backendbase.Vec{tris[i][0], tris[i][1]}
|
||||
b := backendbase.Vec{tris[i+1][0], tris[i+1][1]}
|
||||
c := backendbase.Vec{tris[i+2][0], tris[i+2][1]}
|
||||
if triangleContainsPoint(a, b, c, pt) {
|
||||
return true
|
||||
}
|
||||
|
|
184
paths.go
184
paths.go
|
@ -2,6 +2,8 @@ package canvas
|
|||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
)
|
||||
|
||||
// BeginPath clears the current path and starts a new one
|
||||
|
@ -12,19 +14,19 @@ func (cv *Canvas) BeginPath() {
|
|||
cv.path.p = cv.path.p[:0]
|
||||
}
|
||||
|
||||
func isSamePoint(a, b vec, maxDist float64) bool {
|
||||
func isSamePoint(a, b backendbase.Vec, maxDist float64) bool {
|
||||
return math.Abs(b[0]-a[0]) <= maxDist && math.Abs(b[1]-a[1]) <= maxDist
|
||||
}
|
||||
|
||||
// MoveTo adds a gap and moves the end of the path to x/y
|
||||
func (cv *Canvas) MoveTo(x, y float64) {
|
||||
tf := cv.tf(vec{x, y})
|
||||
tf := cv.tf(backendbase.Vec{x, y})
|
||||
cv.path.MoveTo(tf[0], tf[1])
|
||||
}
|
||||
|
||||
// LineTo adds a line to the end of the path
|
||||
func (cv *Canvas) LineTo(x, y float64) {
|
||||
tf := cv.tf(vec{x, y})
|
||||
tf := cv.tf(backendbase.Vec{x, y})
|
||||
cv.path.LineTo(tf[0], tf[1])
|
||||
}
|
||||
|
||||
|
@ -33,7 +35,7 @@ func (cv *Canvas) LineTo(x, y float64) {
|
|||
// means that the line is added anticlockwise
|
||||
func (cv *Canvas) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise bool) {
|
||||
ax, ay := math.Sincos(startAngle)
|
||||
startAngle2 := vec{ay, ax}.mulMat2(cv.state.transform.mat2()).atan2()
|
||||
startAngle2 := backendbase.Vec{ay, ax}.MulMat2(cv.state.transform.Mat2()).Atan2()
|
||||
endAngle2 := startAngle2 + (endAngle - startAngle)
|
||||
cv.path.arc(x, y, radius, startAngle2, endAngle2, anticlockwise, cv.state.transform, false)
|
||||
}
|
||||
|
@ -49,17 +51,17 @@ func (cv *Canvas) ArcTo(x1, y1, x2, y2, radius float64) {
|
|||
// QuadraticCurveTo adds a quadratic curve to the path. It uses the current end
|
||||
// point of the path, x1/y1 defines the curve, and x2/y2 is the end point
|
||||
func (cv *Canvas) QuadraticCurveTo(x1, y1, x2, y2 float64) {
|
||||
tf1 := cv.tf(vec{x1, y1})
|
||||
tf2 := cv.tf(vec{x2, y2})
|
||||
tf1 := cv.tf(backendbase.Vec{x1, y1})
|
||||
tf2 := cv.tf(backendbase.Vec{x2, y2})
|
||||
cv.path.QuadraticCurveTo(tf1[0], tf1[1], tf2[0], tf2[1])
|
||||
}
|
||||
|
||||
// BezierCurveTo adds a bezier curve to the path. It uses the current end point
|
||||
// of the path, x1/y1 and x2/y2 define the curve, and x3/y3 is the end point
|
||||
func (cv *Canvas) BezierCurveTo(x1, y1, x2, y2, x3, y3 float64) {
|
||||
tf1 := cv.tf(vec{x1, y1})
|
||||
tf2 := cv.tf(vec{x2, y2})
|
||||
tf3 := cv.tf(vec{x3, y3})
|
||||
tf1 := cv.tf(backendbase.Vec{x1, y1})
|
||||
tf2 := cv.tf(backendbase.Vec{x2, y2})
|
||||
tf3 := cv.tf(backendbase.Vec{x3, y3})
|
||||
cv.path.BezierCurveTo(tf1[0], tf1[1], tf2[0], tf2[1], tf3[0], tf3[1])
|
||||
}
|
||||
|
||||
|
@ -69,9 +71,9 @@ func (cv *Canvas) BezierCurveTo(x1, y1, x2, y2, x3, y3 float64) {
|
|||
// are angles in radians, and anticlockwise means that the line is added
|
||||
// anticlockwise
|
||||
func (cv *Canvas) Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, anticlockwise bool) {
|
||||
tf := cv.tf(vec{x, y})
|
||||
tf := cv.tf(backendbase.Vec{x, y})
|
||||
ax, ay := math.Sincos(startAngle)
|
||||
startAngle2 := vec{ay, ax}.mulMat2(cv.state.transform.mat2()).atan2()
|
||||
startAngle2 := backendbase.Vec{ay, ax}.MulMat2(cv.state.transform.Mat2()).Atan2()
|
||||
endAngle2 := startAngle2 + (endAngle - startAngle)
|
||||
cv.path.Ellipse(tf[0], tf[1], radiusX, radiusY, rotation, startAngle2, endAngle2, anticlockwise)
|
||||
}
|
||||
|
@ -84,7 +86,7 @@ func (cv *Canvas) ClosePath() {
|
|||
|
||||
// Stroke uses the current StrokeStyle to draw the current path
|
||||
func (cv *Canvas) Stroke() {
|
||||
cv.strokePath(&cv.path, cv.state.transform.invert(), true)
|
||||
cv.strokePath(&cv.path, cv.state.transform.Invert(), true)
|
||||
}
|
||||
|
||||
// StrokePath uses the current StrokeStyle to draw the given path
|
||||
|
@ -94,10 +96,10 @@ func (cv *Canvas) StrokePath(path *Path2D) {
|
|||
p: make([]pathPoint, len(path.p)),
|
||||
}
|
||||
copy(path2.p, path.p)
|
||||
cv.strokePath(&path2, mat{}, false)
|
||||
cv.strokePath(&path2, backendbase.Mat{}, false)
|
||||
}
|
||||
|
||||
func (cv *Canvas) strokePath(path *Path2D, inv mat, doInv bool) {
|
||||
func (cv *Canvas) strokePath(path *Path2D, inv backendbase.Mat, doInv bool) {
|
||||
if len(path.p) == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -111,7 +113,7 @@ func (cv *Canvas) strokePath(path *Path2D, inv mat, doInv bool) {
|
|||
cv.b.Fill(&stl, tris, true)
|
||||
}
|
||||
|
||||
func (cv *Canvas) strokeTris(path *Path2D, inv mat, doInv bool, target [][2]float64) [][2]float64 {
|
||||
func (cv *Canvas) strokeTris(path *Path2D, inv backendbase.Mat, doInv bool, target [][2]float64) [][2]float64 {
|
||||
if len(path.p) == 0 {
|
||||
return target
|
||||
}
|
||||
|
@ -126,8 +128,8 @@ func (cv *Canvas) strokeTris(path *Path2D, inv mat, doInv bool, target [][2]floa
|
|||
}
|
||||
|
||||
for i, pt := range path.p {
|
||||
pt.pos = pt.pos.mulMat(inv)
|
||||
pt.next = pt.next.mulMat(inv)
|
||||
pt.pos = pt.pos.MulMat(inv)
|
||||
pt.next = pt.next.MulMat(inv)
|
||||
pcopy.p[i] = pt
|
||||
}
|
||||
path = &pcopy
|
||||
|
@ -136,7 +138,7 @@ func (cv *Canvas) strokeTris(path *Path2D, inv mat, doInv bool, target [][2]floa
|
|||
dashedPath := cv.applyLineDash(path.p)
|
||||
|
||||
start := true
|
||||
var p0 vec
|
||||
var p0 backendbase.Vec
|
||||
for _, p := range dashedPath {
|
||||
if p.flags&pathMove != 0 {
|
||||
p0 = p.pos
|
||||
|
@ -145,22 +147,22 @@ func (cv *Canvas) strokeTris(path *Path2D, inv mat, doInv bool, target [][2]floa
|
|||
}
|
||||
p1 := p.pos
|
||||
|
||||
v0 := p1.sub(p0).norm()
|
||||
v1 := vec{v0[1], -v0[0]}.mulf(cv.state.lineWidth * 0.5)
|
||||
v0 = v0.mulf(cv.state.lineWidth * 0.5)
|
||||
v0 := p1.Sub(p0).Norm()
|
||||
v1 := backendbase.Vec{v0[1], -v0[0]}.Mulf(cv.state.lineWidth * 0.5)
|
||||
v0 = v0.Mulf(cv.state.lineWidth * 0.5)
|
||||
|
||||
lp0 := p0.add(v1)
|
||||
lp1 := p1.add(v1)
|
||||
lp2 := p0.sub(v1)
|
||||
lp3 := p1.sub(v1)
|
||||
lp0 := p0.Add(v1)
|
||||
lp1 := p1.Add(v1)
|
||||
lp2 := p0.Sub(v1)
|
||||
lp3 := p1.Sub(v1)
|
||||
|
||||
if start {
|
||||
switch cv.state.lineCap {
|
||||
case Butt:
|
||||
// no need to do anything
|
||||
case Square:
|
||||
lp0 = lp0.sub(v0)
|
||||
lp2 = lp2.sub(v0)
|
||||
lp0 = lp0.Sub(v0)
|
||||
lp2 = lp2.Sub(v0)
|
||||
case Round:
|
||||
target = cv.addCircleTris(p0, cv.state.lineWidth*0.5, target)
|
||||
}
|
||||
|
@ -171,8 +173,8 @@ func (cv *Canvas) strokeTris(path *Path2D, inv mat, doInv bool, target [][2]floa
|
|||
case Butt:
|
||||
// no need to do anything
|
||||
case Square:
|
||||
lp1 = lp1.add(v0)
|
||||
lp3 = lp3.add(v0)
|
||||
lp1 = lp1.Add(v0)
|
||||
lp3 = lp3.Add(v0)
|
||||
case Round:
|
||||
target = cv.addCircleTris(p1, cv.state.lineWidth*0.5, target)
|
||||
}
|
||||
|
@ -209,8 +211,8 @@ func (cv *Canvas) applyLineDash(path []pathPoint) []pathPoint {
|
|||
continue
|
||||
}
|
||||
|
||||
v := pp.pos.sub(lp.pos)
|
||||
vl := v.len()
|
||||
v := pp.pos.Sub(lp.pos)
|
||||
vl := v.Len()
|
||||
prev := ldo
|
||||
for vl > 0 {
|
||||
draw := ldp%2 == 0
|
||||
|
@ -220,7 +222,7 @@ func (cv *Canvas) applyLineDash(path []pathPoint) []pathPoint {
|
|||
ldo = 0
|
||||
dl := cv.state.lineDash[ldp] - prev
|
||||
dist := dl / vl
|
||||
newp.pos = lp.pos.add(v.mulf(dist))
|
||||
newp.pos = lp.pos.Add(v.Mulf(dist))
|
||||
vl -= dl
|
||||
ldp++
|
||||
ldp %= len(cv.state.lineDash)
|
||||
|
@ -239,7 +241,7 @@ func (cv *Canvas) applyLineDash(path []pathPoint) []pathPoint {
|
|||
}
|
||||
|
||||
lp = newp
|
||||
v = pp.pos.sub(lp.pos)
|
||||
v = pp.pos.Sub(lp.pos)
|
||||
}
|
||||
lp = pp
|
||||
}
|
||||
|
@ -247,50 +249,50 @@ func (cv *Canvas) applyLineDash(path []pathPoint) []pathPoint {
|
|||
return path2
|
||||
}
|
||||
|
||||
func (cv *Canvas) lineJoint(p0, p1, p2, l0p0, l0p1, l0p2, l0p3 vec, tris [][2]float64) [][2]float64 {
|
||||
v2 := p1.sub(p2).norm()
|
||||
v3 := vec{v2[1], -v2[0]}.mulf(cv.state.lineWidth * 0.5)
|
||||
func (cv *Canvas) lineJoint(p0, p1, p2, l0p0, l0p1, l0p2, l0p3 backendbase.Vec, tris [][2]float64) [][2]float64 {
|
||||
v2 := p1.Sub(p2).Norm()
|
||||
v3 := backendbase.Vec{v2[1], -v2[0]}.Mulf(cv.state.lineWidth * 0.5)
|
||||
|
||||
switch cv.state.lineJoin {
|
||||
case Miter:
|
||||
l1p0 := p2.sub(v3)
|
||||
l1p1 := p1.sub(v3)
|
||||
l1p2 := p2.add(v3)
|
||||
l1p3 := p1.add(v3)
|
||||
l1p0 := p2.Sub(v3)
|
||||
l1p1 := p1.Sub(v3)
|
||||
l1p2 := p2.Add(v3)
|
||||
l1p3 := p1.Add(v3)
|
||||
|
||||
var ip0, ip1 vec
|
||||
if l0p1.sub(l1p1).lenSqr() < 0.000000001 {
|
||||
ip0 = l0p1.sub(l1p1).mulf(0.5).add(l1p1)
|
||||
var ip0, ip1 backendbase.Vec
|
||||
if l0p1.Sub(l1p1).LenSqr() < 0.000000001 {
|
||||
ip0 = l0p1.Sub(l1p1).Mulf(0.5).Add(l1p1)
|
||||
} else {
|
||||
var q float64
|
||||
ip0, _, q = lineIntersection(l0p0, l0p1, l1p1, l1p0)
|
||||
if q >= 1 {
|
||||
ip0 = l0p1.add(l1p1).mulf(0.5)
|
||||
ip0 = l0p1.Add(l1p1).Mulf(0.5)
|
||||
}
|
||||
}
|
||||
|
||||
if dist := ip0.sub(l0p1).lenSqr(); dist > cv.state.miterLimitSqr {
|
||||
l1p1 := p1.sub(v3)
|
||||
l1p3 := p1.add(v3)
|
||||
if dist := ip0.Sub(l0p1).LenSqr(); dist > cv.state.miterLimitSqr {
|
||||
l1p1 := p1.Sub(v3)
|
||||
l1p3 := p1.Add(v3)
|
||||
|
||||
tris = append(tris, cv.tf(p1), cv.tf(l0p1), cv.tf(l1p1),
|
||||
cv.tf(p1), cv.tf(l1p3), cv.tf(l0p3))
|
||||
return tris
|
||||
}
|
||||
|
||||
if l0p3.sub(l1p3).lenSqr() < 0.000000001 {
|
||||
ip1 = l0p3.sub(l1p3).mulf(0.5).add(l1p3)
|
||||
if l0p3.Sub(l1p3).LenSqr() < 0.000000001 {
|
||||
ip1 = l0p3.Sub(l1p3).Mulf(0.5).Add(l1p3)
|
||||
} else {
|
||||
var q float64
|
||||
ip1, _, q = lineIntersection(l0p2, l0p3, l1p3, l1p2)
|
||||
if q >= 1 {
|
||||
ip1 = l0p3.add(l1p3).mulf(0.5)
|
||||
ip1 = l0p3.Add(l1p3).Mulf(0.5)
|
||||
}
|
||||
}
|
||||
|
||||
if dist := ip1.sub(l1p1).lenSqr(); dist > cv.state.miterLimitSqr {
|
||||
l1p1 := p1.sub(v3)
|
||||
l1p3 := p1.add(v3)
|
||||
if dist := ip1.Sub(l1p1).LenSqr(); dist > cv.state.miterLimitSqr {
|
||||
l1p1 := p1.Sub(v3)
|
||||
l1p3 := p1.Add(v3)
|
||||
|
||||
tris = append(tris, cv.tf(p1), cv.tf(l0p1), cv.tf(l1p1),
|
||||
cv.tf(p1), cv.tf(l1p3), cv.tf(l0p3))
|
||||
|
@ -302,8 +304,8 @@ func (cv *Canvas) lineJoint(p0, p1, p2, l0p0, l0p1, l0p2, l0p3 vec, tris [][2]fl
|
|||
cv.tf(p1), cv.tf(l1p3), cv.tf(ip1),
|
||||
cv.tf(p1), cv.tf(ip1), cv.tf(l0p3))
|
||||
case Bevel:
|
||||
l1p1 := p1.sub(v3)
|
||||
l1p3 := p1.add(v3)
|
||||
l1p1 := p1.Sub(v3)
|
||||
l1p3 := p1.Add(v3)
|
||||
|
||||
tris = append(tris, cv.tf(p1), cv.tf(l0p1), cv.tf(l1p1),
|
||||
cv.tf(p1), cv.tf(l1p3), cv.tf(l0p3))
|
||||
|
@ -314,7 +316,7 @@ func (cv *Canvas) lineJoint(p0, p1, p2, l0p0, l0p1, l0p2, l0p3 vec, tris [][2]fl
|
|||
return tris
|
||||
}
|
||||
|
||||
func (cv *Canvas) addCircleTris(center vec, radius float64, tris [][2]float64) [][2]float64 {
|
||||
func (cv *Canvas) addCircleTris(center backendbase.Vec, radius float64, tris [][2]float64) [][2]float64 {
|
||||
step := 6 / radius
|
||||
if step > 0.8 {
|
||||
step = 0.8
|
||||
|
@ -322,26 +324,26 @@ func (cv *Canvas) addCircleTris(center vec, radius float64, tris [][2]float64) [
|
|||
step = 0.05
|
||||
}
|
||||
centertf := cv.tf(center)
|
||||
p0 := cv.tf(vec{center[0], center[1] + radius})
|
||||
p0 := cv.tf(backendbase.Vec{center[0], center[1] + radius})
|
||||
for angle := step; angle <= math.Pi*2+step; angle += step {
|
||||
s, c := math.Sincos(angle)
|
||||
p1 := cv.tf(vec{center[0] + s*radius, center[1] + c*radius})
|
||||
p1 := cv.tf(backendbase.Vec{center[0] + s*radius, center[1] + c*radius})
|
||||
tris = append(tris, centertf, p0, p1)
|
||||
p0 = p1
|
||||
}
|
||||
return tris
|
||||
}
|
||||
|
||||
func lineIntersection(a0, a1, b0, b1 vec) (vec, float64, float64) {
|
||||
va := a1.sub(a0)
|
||||
vb := b1.sub(b0)
|
||||
func lineIntersection(a0, a1, b0, b1 backendbase.Vec) (backendbase.Vec, float64, float64) {
|
||||
va := a1.Sub(a0)
|
||||
vb := b1.Sub(b0)
|
||||
|
||||
if (va[0] == 0 && vb[0] == 0) || (va[1] == 0 && vb[1] == 0) || (va[0] == 0 && va[1] == 0) || (vb[0] == 0 && vb[1] == 0) {
|
||||
return vec{}, float64(math.Inf(1)), float64(math.Inf(1))
|
||||
return backendbase.Vec{}, float64(math.Inf(1)), float64(math.Inf(1))
|
||||
}
|
||||
d := va[1]*vb[0] - va[0]*vb[1]
|
||||
if d == 0 {
|
||||
return vec{}, float64(math.Inf(1)), float64(math.Inf(1))
|
||||
return backendbase.Vec{}, float64(math.Inf(1)), float64(math.Inf(1))
|
||||
}
|
||||
p := (vb[1]*(a0[0]-b0[0]) - a0[1]*vb[0] + b0[1]*vb[0]) / d
|
||||
var q float64
|
||||
|
@ -351,21 +353,21 @@ func lineIntersection(a0, a1, b0, b1 vec) (vec, float64, float64) {
|
|||
q = (a0[0] + p*va[0] - b0[0]) / vb[0]
|
||||
}
|
||||
|
||||
return a0.add(va.mulf(p)), p, q
|
||||
return a0.Add(va.Mulf(p)), p, q
|
||||
}
|
||||
|
||||
func linePointDistSqr(a, b, p vec) float64 {
|
||||
v := b.sub(a)
|
||||
vl := v.len()
|
||||
vn := v.divf(vl)
|
||||
d := p.sub(a).dot(vn)
|
||||
c := a.add(vn.mulf(d))
|
||||
return p.sub(c).lenSqr()
|
||||
func linePointDistSqr(a, b, p backendbase.Vec) float64 {
|
||||
v := b.Sub(a)
|
||||
vl := v.Len()
|
||||
vn := v.Divf(vl)
|
||||
d := p.Sub(a).Dot(vn)
|
||||
c := a.Add(vn.Mulf(d))
|
||||
return p.Sub(c).LenSqr()
|
||||
}
|
||||
|
||||
// Fill fills the current path with the current FillStyle
|
||||
func (cv *Canvas) Fill() {
|
||||
cv.fillPath(&cv.path, matIdentity)
|
||||
cv.fillPath(&cv.path, backendbase.MatIdentity)
|
||||
}
|
||||
|
||||
// FillPath fills the given path with the current FillStyle
|
||||
|
@ -374,7 +376,7 @@ func (cv *Canvas) FillPath(path *Path2D) {
|
|||
}
|
||||
|
||||
// FillPath fills the given path with the current FillStyle
|
||||
func (cv *Canvas) fillPath(path *Path2D, tf mat) {
|
||||
func (cv *Canvas) fillPath(path *Path2D, tf backendbase.Mat) {
|
||||
if len(path.p) < 3 {
|
||||
return
|
||||
}
|
||||
|
@ -396,16 +398,16 @@ func (cv *Canvas) fillPath(path *Path2D, tf mat) {
|
|||
cv.b.Fill(&stl, tris, false)
|
||||
}
|
||||
|
||||
func appendSubPathTriangles(tris [][2]float64, mat mat, path []pathPoint) [][2]float64 {
|
||||
func appendSubPathTriangles(tris [][2]float64, mat backendbase.Mat, path []pathPoint) [][2]float64 {
|
||||
last := path[len(path)-1]
|
||||
if last.flags&pathIsConvex != 0 {
|
||||
p0, p1 := path[0].pos.mulMat(mat), path[1].pos.mulMat(mat)
|
||||
p0, p1 := path[0].pos.MulMat(mat), path[1].pos.MulMat(mat)
|
||||
lastIdx := len(path)
|
||||
if path[0].pos == path[lastIdx-1].pos {
|
||||
lastIdx--
|
||||
}
|
||||
for i := 2; i < lastIdx; i++ {
|
||||
p2 := path[i].pos.mulMat(mat)
|
||||
p2 := path[i].pos.MulMat(mat)
|
||||
tris = append(tris, p0, p1, p2)
|
||||
p1 = p2
|
||||
}
|
||||
|
@ -423,10 +425,10 @@ func appendSubPathTriangles(tris [][2]float64, mat mat, path []pathPoint) [][2]f
|
|||
// Clip uses the current path to clip any further drawing. Use Save/Restore to
|
||||
// remove the clipping again
|
||||
func (cv *Canvas) Clip() {
|
||||
cv.clip(&cv.path, matIdentity)
|
||||
cv.clip(&cv.path, backendbase.MatIdentity)
|
||||
}
|
||||
|
||||
func (cv *Canvas) clip(path *Path2D, tf mat) {
|
||||
func (cv *Canvas) clip(path *Path2D, tf backendbase.Mat) {
|
||||
if len(path.p) < 3 {
|
||||
return
|
||||
}
|
||||
|
@ -476,10 +478,10 @@ func (cv *Canvas) Rect(x, y, w, h float64) {
|
|||
|
||||
// StrokeRect draws a rectangle using the current stroke style
|
||||
func (cv *Canvas) StrokeRect(x, y, w, h float64) {
|
||||
v0 := vec{x, y}
|
||||
v1 := vec{x + w, y}
|
||||
v2 := vec{x + w, y + h}
|
||||
v3 := vec{x, y + h}
|
||||
v0 := backendbase.Vec{x, y}
|
||||
v1 := backendbase.Vec{x + w, y}
|
||||
v2 := backendbase.Vec{x + w, y + h}
|
||||
v3 := backendbase.Vec{x, y + h}
|
||||
var p [5]pathPoint
|
||||
p[0] = pathPoint{pos: v0, flags: pathMove | pathAttach, next: v1}
|
||||
p[1] = pathPoint{pos: v1, next: v2, flags: pathAttach}
|
||||
|
@ -487,15 +489,15 @@ func (cv *Canvas) StrokeRect(x, y, w, h float64) {
|
|||
p[3] = pathPoint{pos: v3, next: v0, flags: pathAttach}
|
||||
p[4] = pathPoint{pos: v0, next: v1, flags: pathAttach}
|
||||
path := Path2D{p: p[:]}
|
||||
cv.strokePath(&path, mat{}, false)
|
||||
cv.strokePath(&path, backendbase.Mat{}, false)
|
||||
}
|
||||
|
||||
// FillRect fills a rectangle with the active fill style
|
||||
func (cv *Canvas) FillRect(x, y, w, h float64) {
|
||||
p0 := cv.tf(vec{x, y})
|
||||
p1 := cv.tf(vec{x, y + h})
|
||||
p2 := cv.tf(vec{x + w, y + h})
|
||||
p3 := cv.tf(vec{x + w, y})
|
||||
p0 := cv.tf(backendbase.Vec{x, y})
|
||||
p1 := cv.tf(backendbase.Vec{x, y + h})
|
||||
p2 := cv.tf(backendbase.Vec{x + w, y + h})
|
||||
p3 := cv.tf(backendbase.Vec{x + w, y})
|
||||
|
||||
data := [4][2]float64{{p0[0], p0[1]}, {p1[0], p1[1]}, {p2[0], p2[1]}, {p3[0], p3[1]}}
|
||||
|
||||
|
@ -507,10 +509,10 @@ func (cv *Canvas) FillRect(x, y, w, h float64) {
|
|||
|
||||
// ClearRect sets the color of the rectangle to transparent black
|
||||
func (cv *Canvas) ClearRect(x, y, w, h float64) {
|
||||
p0 := cv.tf(vec{x, y})
|
||||
p1 := cv.tf(vec{x, y + h})
|
||||
p2 := cv.tf(vec{x + w, y + h})
|
||||
p3 := cv.tf(vec{x + w, y})
|
||||
p0 := cv.tf(backendbase.Vec{x, y})
|
||||
p1 := cv.tf(backendbase.Vec{x, y + h})
|
||||
p2 := cv.tf(backendbase.Vec{x + w, y + h})
|
||||
p3 := cv.tf(backendbase.Vec{x + w, y})
|
||||
data := [4][2]float64{{p0[0], p0[1]}, {p1[0], p1[1]}, {p2[0], p2[1]}, {p3[0], p3[1]}}
|
||||
|
||||
cv.b.Clear(data)
|
||||
|
|
25
text.go
25
text.go
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
@ -117,8 +118,8 @@ func (cv *Canvas) FillText(str string, x, y float64) {
|
|||
return
|
||||
}
|
||||
|
||||
scaleX := vec{cv.state.transform[0], cv.state.transform[1]}.len()
|
||||
scaleY := vec{cv.state.transform[2], cv.state.transform[3]}.len()
|
||||
scaleX := backendbase.Vec{cv.state.transform[0], cv.state.transform[1]}.Len()
|
||||
scaleY := backendbase.Vec{cv.state.transform[2], cv.state.transform[3]}.Len()
|
||||
scale := (scaleX + scaleY) * 0.5
|
||||
fontSize := fixed.Int26_6(math.Round(float64(cv.state.fontSize) * scale))
|
||||
|
||||
|
@ -213,10 +214,10 @@ func (cv *Canvas) FillText(str string, x, y float64) {
|
|||
w, h := cv.b.Size()
|
||||
fw, fh := float64(w), float64(h)
|
||||
|
||||
p0 := cv.tf(vec{float64(bounds.Min.X)/scale + curX, float64(bounds.Min.Y)/scale + y})
|
||||
p1 := cv.tf(vec{float64(bounds.Min.X)/scale + curX, float64(bounds.Max.Y)/scale + y})
|
||||
p2 := cv.tf(vec{float64(bounds.Max.X)/scale + curX, float64(bounds.Max.Y)/scale + y})
|
||||
p3 := cv.tf(vec{float64(bounds.Max.X)/scale + curX, float64(bounds.Min.Y)/scale + y})
|
||||
p0 := cv.tf(backendbase.Vec{float64(bounds.Min.X)/scale + curX, float64(bounds.Min.Y)/scale + y})
|
||||
p1 := cv.tf(backendbase.Vec{float64(bounds.Min.X)/scale + curX, float64(bounds.Max.Y)/scale + y})
|
||||
p2 := cv.tf(backendbase.Vec{float64(bounds.Max.X)/scale + curX, float64(bounds.Max.Y)/scale + y})
|
||||
p3 := cv.tf(backendbase.Vec{float64(bounds.Max.X)/scale + curX, float64(bounds.Min.Y)/scale + y})
|
||||
inside := (p0[0] >= 0 || p1[0] >= 0 || p2[0] >= 0 || p3[0] >= 0) &&
|
||||
(p0[1] >= 0 || p1[1] >= 0 || p2[1] >= 0 || p3[1] >= 0) &&
|
||||
(p0[0] < fw || p1[0] < fw || p2[0] < fw || p3[0] < fw) &&
|
||||
|
@ -299,10 +300,10 @@ func (cv *Canvas) FillText(str string, x, y float64) {
|
|||
|
||||
// render textImage to the screen
|
||||
var pts [4][2]float64
|
||||
pts[0] = cv.tf(vec{float64(textOffset.X)/scale + x, float64(textOffset.Y)/scale + y})
|
||||
pts[1] = cv.tf(vec{float64(textOffset.X)/scale + x, float64(textOffset.Y)/scale + fstrHeight + y})
|
||||
pts[2] = cv.tf(vec{float64(textOffset.X)/scale + fstrWidth + x, float64(textOffset.Y)/scale + fstrHeight + y})
|
||||
pts[3] = cv.tf(vec{float64(textOffset.X)/scale + fstrWidth + x, float64(textOffset.Y)/scale + y})
|
||||
pts[0] = cv.tf(backendbase.Vec{float64(textOffset.X)/scale + x, float64(textOffset.Y)/scale + y})
|
||||
pts[1] = cv.tf(backendbase.Vec{float64(textOffset.X)/scale + x, float64(textOffset.Y)/scale + fstrHeight + y})
|
||||
pts[2] = cv.tf(backendbase.Vec{float64(textOffset.X)/scale + fstrWidth + x, float64(textOffset.Y)/scale + fstrHeight + y})
|
||||
pts[3] = cv.tf(backendbase.Vec{float64(textOffset.X)/scale + fstrWidth + x, float64(textOffset.Y)/scale + y})
|
||||
|
||||
mask := textImage.SubImage(image.Rect(0, 0, strWidth, strHeight)).(*image.Alpha)
|
||||
|
||||
|
@ -345,7 +346,7 @@ func (cv *Canvas) StrokeText(str string, x, y float64) {
|
|||
continue
|
||||
}
|
||||
|
||||
cv.runePath(rn, vec{x, y})
|
||||
cv.runePath(rn, backendbase.Vec{x, y})
|
||||
|
||||
x += float64(advance) / 64
|
||||
}
|
||||
|
@ -354,7 +355,7 @@ func (cv *Canvas) StrokeText(str string, x, y float64) {
|
|||
cv.path = prevPath
|
||||
}
|
||||
|
||||
func (cv *Canvas) runePath(rn rune, pos vec) {
|
||||
func (cv *Canvas) runePath(rn rune, pos backendbase.Vec) {
|
||||
gb := &truetype.GlyphBuf{}
|
||||
gb.Load(cv.state.font.font, cv.state.fontSize, cv.state.font.font.Index(rn), font.HintingNone)
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@ package canvas
|
|||
import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
)
|
||||
|
||||
const samePointTolerance = 1e-20
|
||||
|
||||
func pointIsRightOfLine(a, b, p vec) (bool, bool) {
|
||||
func pointIsRightOfLine(a, b, p backendbase.Vec) (bool, bool) {
|
||||
if a[1] == b[1] {
|
||||
return false, false
|
||||
}
|
||||
|
@ -19,13 +21,13 @@ func pointIsRightOfLine(a, b, p vec) (bool, bool) {
|
|||
if p[1] < a[1] || p[1] >= b[1] {
|
||||
return false, false
|
||||
}
|
||||
v := b.sub(a)
|
||||
v := b.Sub(a)
|
||||
r := (p[1] - a[1]) / v[1]
|
||||
x := a[0] + r*v[0]
|
||||
return p[0] > x, dir
|
||||
}
|
||||
|
||||
func triangleContainsPoint(a, b, c, p vec) bool {
|
||||
func triangleContainsPoint(a, b, c, p backendbase.Vec) bool {
|
||||
// if point is outside triangle bounds, return false
|
||||
if p[0] < a[0] && p[0] < b[0] && p[0] < c[0] {
|
||||
return false
|
||||
|
@ -56,12 +58,12 @@ func triangleContainsPoint(a, b, c, p vec) bool {
|
|||
|
||||
const parallelTolerance = 1e-10
|
||||
|
||||
func parallel(a1, b1, a2, b2 vec) bool {
|
||||
ang := b1.sub(a1).angleTo(b2.sub(a2))
|
||||
func parallel(a1, b1, a2, b2 backendbase.Vec) bool {
|
||||
ang := b1.Sub(a1).AngleTo(b2.Sub(a2))
|
||||
return math.Abs(ang) < parallelTolerance || math.Abs(ang-math.Pi) < parallelTolerance
|
||||
}
|
||||
|
||||
func polygonContainsLine(polygon []vec, ia, ib int, a, b vec) bool {
|
||||
func polygonContainsLine(polygon []backendbase.Vec, ia, ib int, a, b backendbase.Vec) bool {
|
||||
for i := range polygon {
|
||||
if i == ia || i == ib {
|
||||
continue
|
||||
|
@ -80,7 +82,7 @@ func polygonContainsLine(polygon []vec, ia, ib int, a, b vec) bool {
|
|||
|
||||
const onLineToleranceSqr = 1e-20
|
||||
|
||||
func polygonContainsPoint(polygon []vec, p vec) bool {
|
||||
func polygonContainsPoint(polygon []backendbase.Vec, p backendbase.Vec) bool {
|
||||
a := polygon[len(polygon)-1]
|
||||
count := 0
|
||||
for _, b := range polygon {
|
||||
|
@ -95,15 +97,15 @@ func polygonContainsPoint(polygon []vec, p vec) bool {
|
|||
return count%2 == 1
|
||||
}
|
||||
|
||||
func triangulatePath(path []pathPoint, mat mat, target [][2]float64) [][2]float64 {
|
||||
func triangulatePath(path []pathPoint, mat backendbase.Mat, target [][2]float64) [][2]float64 {
|
||||
if path[0].pos == path[len(path)-1].pos {
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
|
||||
var buf [500]vec
|
||||
var buf [500]backendbase.Vec
|
||||
polygon := buf[:0]
|
||||
for _, p := range path {
|
||||
polygon = append(polygon, p.pos.mulMat(mat))
|
||||
polygon = append(polygon, p.pos.MulMat(mat))
|
||||
}
|
||||
|
||||
for len(polygon) > 3 {
|
||||
|
@ -117,7 +119,7 @@ func triangulatePath(path []pathPoint, mat mat, target [][2]float64) [][2]float6
|
|||
if isSamePoint(a, c, math.SmallestNonzeroFloat64) {
|
||||
break
|
||||
}
|
||||
if len(polygon) > 3 && !polygonContainsPoint(polygon, a.add(c).divf(2)) {
|
||||
if len(polygon) > 3 && !polygonContainsPoint(polygon, a.Add(c).Divf(2)) {
|
||||
continue
|
||||
}
|
||||
if !polygonContainsLine(polygon, i, ic, a, c) {
|
||||
|
@ -156,7 +158,7 @@ type tessNet struct {
|
|||
}
|
||||
|
||||
type tessVert struct {
|
||||
pos vec
|
||||
pos backendbase.Vec
|
||||
attached []int
|
||||
count int
|
||||
}
|
||||
|
@ -183,7 +185,7 @@ func cutIntersections(path []pathPoint) tessNet {
|
|||
type cut struct {
|
||||
from, to int
|
||||
ratio float64
|
||||
point vec
|
||||
point backendbase.Vec
|
||||
}
|
||||
|
||||
var cutBuf [50]cut
|
||||
|
@ -296,8 +298,8 @@ func cutIntersections(path []pathPoint) tessNet {
|
|||
func setPathLeftRightInside(net *tessNet) {
|
||||
for i, e1 := range net.edges {
|
||||
a1, b1 := net.verts[e1.a], net.verts[e1.b]
|
||||
diff := b1.pos.sub(a1.pos)
|
||||
mid := a1.pos.add(diff.mulf(0.5))
|
||||
diff := b1.pos.Sub(a1.pos)
|
||||
mid := a1.pos.Add(diff.Mulf(0.5))
|
||||
|
||||
left, right := 0, 0
|
||||
if math.Abs(diff[1]) > math.Abs(diff[0]) {
|
||||
|
@ -315,7 +317,7 @@ func setPathLeftRightInside(net *tessNet) {
|
|||
if mid[1] < a2[1] || mid[1] > b2[1] {
|
||||
continue
|
||||
}
|
||||
v := b2.sub(a2)
|
||||
v := b2.Sub(a2)
|
||||
r := (mid[1] - a2[1]) / v[1]
|
||||
x := a2[0] + r*v[0]
|
||||
if mid[0] > x {
|
||||
|
@ -342,7 +344,7 @@ func setPathLeftRightInside(net *tessNet) {
|
|||
if mid[0] < a2[0] || mid[0] > b2[0] {
|
||||
continue
|
||||
}
|
||||
v := b2.sub(a2)
|
||||
v := b2.Sub(a2)
|
||||
r := (mid[0] - a2[0]) / v[0]
|
||||
y := a2[1] + r*v[1]
|
||||
if mid[1] > y {
|
||||
|
@ -404,7 +406,7 @@ func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool)
|
|||
for limit := 0; limit < len(net.edges); limit++ {
|
||||
ecur := net.edges[from]
|
||||
acur, bcur := net.verts[ecur.a], net.verts[ecur.b]
|
||||
dir := bcur.pos.sub(acur.pos)
|
||||
dir := bcur.pos.Sub(acur.pos)
|
||||
dirAngle := math.Atan2(dir[1], dir[0])
|
||||
minAngleDiff := math.Pi * 2
|
||||
var next, nextEdge int
|
||||
|
@ -421,7 +423,7 @@ func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool)
|
|||
if e.b == cur {
|
||||
na, nb = nb, na
|
||||
}
|
||||
ndir := nb.pos.sub(na.pos)
|
||||
ndir := nb.pos.Sub(na.pos)
|
||||
nextAngle := math.Atan2(ndir[1], ndir[0]) + math.Pi
|
||||
if nextAngle < dirAngle {
|
||||
nextAngle += math.Pi * 2
|
||||
|
|
Loading…
Reference in a new issue