From 97615b75957a73bcafcb33eeeb39beb0d22c4aa7 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Wed, 24 Jan 2018 16:59:07 +0100 Subject: [PATCH] added polygon filling --- canvas.go | 46 +++++++++++++++++++---- triangulation.go | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 triangulation.go diff --git a/canvas.go b/canvas.go index c5a37dc..c587883 100644 --- a/canvas.go +++ b/canvas.go @@ -155,13 +155,14 @@ func (cv *Canvas) Stroke() { } p1 := p.pos - v := p1.Sub(p0).Norm() - v = lm.Vec2{v[1], -v[0]}.MulF(cv.stroke.lineWidth * 0.5) + v1 := p1.Sub(p0).Norm() + v2 := lm.Vec2{v1[1], -v1[0]}.MulF(cv.stroke.lineWidth * 0.5) + v1 = v1.MulF(cv.stroke.lineWidth * 0.5) - x0f, y0f := cv.vecToGL(p0.Add(v)) - x1f, y1f := cv.vecToGL(p1.Add(v)) - x2f, y2f := cv.vecToGL(p1.Sub(v)) - x3f, y3f := cv.vecToGL(p0.Sub(v)) + x0f, y0f := cv.vecToGL(p0.Sub(v1).Add(v2)) + x1f, y1f := cv.vecToGL(p1.Add(v1).Add(v2)) + x2f, y2f := cv.vecToGL(p1.Add(v1).Sub(v2)) + x3f, y3f := cv.vecToGL(p0.Sub(v1).Sub(v2)) tris = append(tris, x0f, y0f, x1f, y1f, x2f, y2f, x0f, y0f, x2f, y2f, x3f, y3f) @@ -170,7 +171,7 @@ func (cv *Canvas) Stroke() { gli.BufferData(gl_ARRAY_BUFFER, len(tris)*4, unsafe.Pointer(&tris[0]), gl_STREAM_DRAW) gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, nil) - gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2)) + gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6)) gli.ColorMask(true, true, true, true) gli.StencilFunc(gl_EQUAL, 1, 0xFF) @@ -182,3 +183,34 @@ func (cv *Canvas) Stroke() { gli.Disable(gl_STENCIL_TEST) } + +func (cv *Canvas) Fill() { + if len(cv.path) < 3 { + return + } + + cv.activate() + + gli.UseProgram(sr.id) + gli.Uniform4f(sr.color, cv.fill.r, cv.fill.g, cv.fill.b, cv.fill.a) + gli.EnableVertexAttribArray(sr.vertex) + + gli.BindBuffer(gl_ARRAY_BUFFER, buf) + + var buf [1000]float32 + tris := buf[:0] + tris = append(tris, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1) + + tris = triangulatePath(cv.path, tris) + total := len(tris) + for i := 12; i < total; i += 2 { + x, y := tris[i], tris[i+1] + tris[i], tris[i+1] = cv.ptToGL(x, y) + } + + gli.BufferData(gl_ARRAY_BUFFER, len(tris)*4, unsafe.Pointer(&tris[0]), gl_STREAM_DRAW) + gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, nil) + gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6)) + + gli.DisableVertexAttribArray(sr.vertex) +} diff --git a/triangulation.go b/triangulation.go new file mode 100644 index 0000000..39b109c --- /dev/null +++ b/triangulation.go @@ -0,0 +1,97 @@ +package canvas + +import ( + "github.com/void6/lm" +) + +func pointIsRightOfLine(a, b, p lm.Vec2) bool { + if a[1] == b[1] { + return false + } + if a[1] > b[1] { + a, b = b, a + } + if p[1] < a[1] || p[1] > b[1] { + return false + } + v := b.Sub(a) + r := (p[1] - a[1]) / v[1] + x := a[0] + r*v[0] + return p[0] > x +} + +func triangleContainsPoint(a, b, c, p lm.Vec2) bool { + // if point is outside triangle bounds, return false + if p[0] < a[0] && p[0] < b[0] && p[0] < c[0] { + return false + } + if p[0] > a[0] && p[0] > b[0] && p[0] > c[0] { + return false + } + if p[1] < a[1] && p[1] < b[1] && p[1] < c[1] { + return false + } + if p[1] > a[1] && p[1] > b[1] && p[1] > c[1] { + return false + } + // check whether the point is to the right of each triangle line. + // if the total is 1, it is inside the triangle + count := 0 + if pointIsRightOfLine(a, b, p) { + count++ + } + if pointIsRightOfLine(b, c, p) { + count++ + } + if pointIsRightOfLine(c, a, p) { + count++ + } + return count == 1 +} + +func polygonContainsPoint(polygon []lm.Vec2, p lm.Vec2) bool { + a := polygon[len(polygon)-1] + count := 0 + for _, b := range polygon { + if pointIsRightOfLine(a, b, p) { + count++ + } + a = b + } + return count%2 == 1 +} + +func triangulatePath(path []pathPoint, target []float32) []float32 { + var buf [500]lm.Vec2 + polygon := buf[:0] + for _, p := range path { + polygon = append(polygon, p.pos) + } + + for len(polygon) > 2 { + var i int + triangles: + for i = range polygon { + a := polygon[i] + b := polygon[(i+1)%len(polygon)] + c := polygon[(i+2)%len(polygon)] + for i2, p := range polygon { + if i2 >= i && i2 <= i+2 { + continue + } + if triangleContainsPoint(a, b, c, p) { + continue triangles + } + center := a.Add(b).Add(c).DivF(3) + if !polygonContainsPoint(polygon, center) { + continue triangles + } + } + target = append(target, a[0], a[1], b[0], b[1], c[0], c[1]) + break + } + remove := (i + 1) % len(polygon) + polygon = append(polygon[:remove], polygon[remove+1:]...) + } + return target +}