From 26d11865b53b816e82ddb60291359601e555a0f2 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Wed, 21 Mar 2018 12:45:32 +0100 Subject: [PATCH] switched from float32 to float64 for better compatibility with the default go math package; moved vector and matrix code into package --- canvas.go | 165 ++++++++++++++++--------------- color.go | 58 +++++------ gradients.go | 32 +++--- imagedata.go | 2 +- images.go | 30 +++--- math.go | 141 +++++++++++++++++++++++++++ paths.go | 247 +++++++++++++++++++++++------------------------ text.go | 21 ++-- triangulation.go | 22 ++--- 9 files changed, 424 insertions(+), 294 deletions(-) create mode 100644 math.go diff --git a/canvas.go b/canvas.go index a1bc4b7..87209cc 100644 --- a/canvas.go +++ b/canvas.go @@ -5,7 +5,6 @@ import ( "unsafe" "github.com/golang/freetype/truetype" - "github.com/tfriedel6/lm" ) //go:generate go run make_shaders.go @@ -15,7 +14,7 @@ import ( // using a set of functions very similar to the HTML5 canvas type Canvas struct { x, y, w, h int - fx, fy, fw, fh float32 + fx, fy, fw, fh float64 polyPath []pathPoint linePath []pathPoint @@ -25,26 +24,26 @@ type Canvas struct { } type pathPoint struct { - pos lm.Vec2 - tf lm.Vec2 + pos vec + tf vec move bool - next lm.Vec2 + next vec attach bool } type drawState struct { - transform lm.Mat3x3 + transform mat fill drawStyle stroke drawStyle font *Font - fontSize float32 - lineWidth float32 + fontSize float64 + lineWidth float64 lineJoin lineJoin lineEnd lineEnd - lineDash []float32 + lineDash []float64 lineDashPoint int - lineDashOffset float32 + lineDashOffset float64 clip []pathPoint /* @@ -83,18 +82,18 @@ const ( func New(x, y, w, h int) *Canvas { cv := &Canvas{ x: x, y: y, w: w, h: h, - fx: float32(x), fy: float32(y), - fw: float32(w), fh: float32(h), + fx: float64(x), fy: float64(y), + fw: float64(w), fh: float64(h), stateStack: make([]drawState, 0, 20), } cv.state.lineWidth = 1 - cv.state.transform = lm.Mat3x3Identity() + cv.state.transform = matIdentity() return cv } func (cv *Canvas) SetSize(w, h int) { cv.w, cv.h = w, h - cv.fw, cv.fh = float32(w), float32(h) + cv.fw, cv.fh = float64(w), float64(h) activeCanvas = nil } @@ -102,8 +101,8 @@ func (cv *Canvas) W() int { return cv.w } func (cv *Canvas) H() int { return cv.h } func (cv *Canvas) Size() (int, int) { return cv.w, cv.h } -func (cv *Canvas) tf(v lm.Vec2) lm.Vec2 { - v, _ = v.MulMat3x3(cv.state.transform) +func (cv *Canvas) tf(v vec) vec { + v, _ = v.mulMat(cv.state.transform) return v } @@ -310,15 +309,15 @@ func (cv *Canvas) useShader(style *drawStyle) (vertexLoc uint32) { gli.UseProgram(lgr.id) from := cv.tf(lg.from) to := cv.tf(lg.to) - dir := to.Sub(from) - length := dir.Len() - dir = dir.DivF(length) - gli.Uniform2f(lgr.canvasSize, cv.fw, cv.fh) - inv := cv.state.transform.Invert() + dir := to.sub(from) + length := dir.len() + dir = dir.divf(length) + gli.Uniform2f(lgr.canvasSize, float32(cv.fw), float32(cv.fh)) + inv := cv.state.transform.invert().f32() gli.UniformMatrix3fv(lgr.invmat, 1, false, &inv[0]) - gli.Uniform2f(lgr.from, from[0], from[1]) - gli.Uniform2f(lgr.dir, dir[0], dir[1]) - gli.Uniform1f(lgr.len, length) + gli.Uniform2f(lgr.from, float32(from[0]), float32(from[1])) + gli.Uniform2f(lgr.dir, float32(dir[0]), float32(dir[1])) + gli.Uniform1f(lgr.len, float32(length)) gli.Uniform1i(lgr.gradient, 0) return lgr.vertex } @@ -329,18 +328,18 @@ func (cv *Canvas) useShader(style *drawStyle) (vertexLoc uint32) { gli.UseProgram(rgr.id) from := cv.tf(rg.from) to := cv.tf(rg.to) - dir := to.Sub(from) - length := dir.Len() - dir = dir.DivF(length) - gli.Uniform2f(rgr.canvasSize, cv.fw, cv.fh) - inv := cv.state.transform.Invert() + dir := to.sub(from) + length := dir.len() + dir = dir.divf(length) + gli.Uniform2f(rgr.canvasSize, float32(cv.fw), float32(cv.fh)) + inv := cv.state.transform.invert().f32() gli.UniformMatrix3fv(rgr.invmat, 1, false, &inv[0]) - gli.Uniform2f(rgr.from, from[0], from[1]) - gli.Uniform2f(rgr.to, to[0], to[1]) - gli.Uniform2f(rgr.dir, dir[0], dir[1]) - gli.Uniform1f(rgr.radFrom, rg.radFrom) - gli.Uniform1f(rgr.radTo, rg.radTo) - gli.Uniform1f(rgr.len, length) + gli.Uniform2f(rgr.from, float32(from[0]), float32(from[1])) + gli.Uniform2f(rgr.to, float32(to[0]), float32(to[1])) + gli.Uniform2f(rgr.dir, float32(dir[0]), float32(dir[1])) + gli.Uniform1f(rgr.radFrom, float32(rg.radFrom)) + gli.Uniform1f(rgr.radTo, float32(rg.radTo)) + gli.Uniform1f(rgr.len, float32(length)) gli.Uniform1i(rgr.gradient, 0) return rgr.vertex } @@ -348,8 +347,8 @@ func (cv *Canvas) useShader(style *drawStyle) (vertexLoc uint32) { gli.UseProgram(ipr.id) gli.ActiveTexture(gl_TEXTURE0) gli.BindTexture(gl_TEXTURE_2D, img.tex) - gli.Uniform2f(ipr.canvasSize, cv.fw, cv.fh) - inv := cv.state.transform.Invert() + gli.Uniform2f(ipr.canvasSize, float32(cv.fw), float32(cv.fh)) + inv := cv.state.transform.invert().f32() gli.UniformMatrix3fv(ipr.invmat, 1, false, &inv[0]) gli.Uniform2f(ipr.imageSize, float32(img.w), float32(img.h)) gli.Uniform1i(ipr.image, 0) @@ -357,9 +356,9 @@ func (cv *Canvas) useShader(style *drawStyle) (vertexLoc uint32) { } gli.UseProgram(sr.id) - gli.Uniform2f(sr.canvasSize, cv.fw, cv.fh) + gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh)) c := style.color - gli.Uniform4f(sr.color, c.r, c.g, c.b, c.a) + gli.Uniform4f(sr.color, float32(c.r), float32(c.g), float32(c.b), float32(c.a)) return sr.vertex } @@ -371,15 +370,15 @@ func (cv *Canvas) useAlphaShader(style *drawStyle, alphaTexSlot int32) (vertexLo gli.UseProgram(lgar.id) from := cv.tf(lg.from) to := cv.tf(lg.to) - dir := to.Sub(from) - length := dir.Len() - dir = dir.DivF(length) - gli.Uniform2f(lgar.canvasSize, cv.fw, cv.fh) - inv := cv.state.transform.Invert() + dir := to.sub(from) + length := dir.len() + dir = dir.divf(length) + gli.Uniform2f(lgar.canvasSize, float32(cv.fw), float32(cv.fh)) + inv := cv.state.transform.invert().f32() gli.UniformMatrix3fv(lgar.invmat, 1, false, &inv[0]) - gli.Uniform2f(lgar.from, from[0], from[1]) - gli.Uniform2f(lgar.dir, dir[0], dir[1]) - gli.Uniform1f(lgar.len, length) + gli.Uniform2f(lgar.from, float32(from[0]), float32(from[1])) + gli.Uniform2f(lgar.dir, float32(dir[0]), float32(dir[1])) + gli.Uniform1f(lgar.len, float32(length)) gli.Uniform1i(lgar.gradient, 0) gli.Uniform1i(lgar.alphaTex, alphaTexSlot) return lgar.vertex, lgar.alphaTexCoord @@ -391,18 +390,18 @@ func (cv *Canvas) useAlphaShader(style *drawStyle, alphaTexSlot int32) (vertexLo gli.UseProgram(rgar.id) from := cv.tf(rg.from) to := cv.tf(rg.to) - dir := to.Sub(from) - length := dir.Len() - dir = dir.DivF(length) - gli.Uniform2f(rgar.canvasSize, cv.fw, cv.fh) - inv := cv.state.transform.Invert() + dir := to.sub(from) + length := dir.len() + dir = dir.divf(length) + gli.Uniform2f(rgar.canvasSize, float32(cv.fw), float32(cv.fh)) + inv := cv.state.transform.invert().f32() gli.UniformMatrix3fv(rgar.invmat, 1, false, &inv[0]) - gli.Uniform2f(rgar.from, from[0], from[1]) - gli.Uniform2f(rgar.to, to[0], to[1]) - gli.Uniform2f(rgar.dir, dir[0], dir[1]) - gli.Uniform1f(rgar.radFrom, rg.radFrom) - gli.Uniform1f(rgar.radTo, rg.radTo) - gli.Uniform1f(rgar.len, length) + gli.Uniform2f(rgar.from, float32(from[0]), float32(from[1])) + gli.Uniform2f(rgar.to, float32(to[0]), float32(to[1])) + gli.Uniform2f(rgar.dir, float32(dir[0]), float32(dir[1])) + gli.Uniform1f(rgar.radFrom, float32(rg.radFrom)) + gli.Uniform1f(rgar.radTo, float32(rg.radTo)) + gli.Uniform1f(rgar.len, float32(length)) gli.Uniform1i(rgar.gradient, 0) gli.Uniform1i(rgar.alphaTex, alphaTexSlot) return rgar.vertex, rgar.alphaTexCoord @@ -411,8 +410,8 @@ func (cv *Canvas) useAlphaShader(style *drawStyle, alphaTexSlot int32) (vertexLo gli.UseProgram(ipar.id) gli.ActiveTexture(gl_TEXTURE0) gli.BindTexture(gl_TEXTURE_2D, img.tex) - gli.Uniform2f(ipar.canvasSize, cv.fw, cv.fh) - inv := cv.state.transform.Invert() + gli.Uniform2f(ipar.canvasSize, float32(cv.fw), float32(cv.fh)) + inv := cv.state.transform.invert().f32() gli.UniformMatrix3fv(ipar.invmat, 1, false, &inv[0]) gli.Uniform2f(ipar.imageSize, float32(img.w), float32(img.h)) gli.Uniform1i(ipar.image, 0) @@ -421,20 +420,20 @@ func (cv *Canvas) useAlphaShader(style *drawStyle, alphaTexSlot int32) (vertexLo } gli.UseProgram(sar.id) - gli.Uniform2f(sar.canvasSize, cv.fw, cv.fh) + gli.Uniform2f(sar.canvasSize, float32(cv.fw), float32(cv.fh)) c := style.color - gli.Uniform4f(sar.color, c.r, c.g, c.b, c.a) + gli.Uniform4f(sar.color, float32(c.r), float32(c.g), float32(c.b), float32(c.a)) gli.Uniform1i(sar.alphaTex, alphaTexSlot) return sar.vertex, sar.alphaTexCoord } // SetLineWidth sets the line width for any line drawing calls -func (cv *Canvas) SetLineWidth(width float32) { +func (cv *Canvas) SetLineWidth(width float64) { cv.state.lineWidth = width } // SetFont sets the font and font size -func (cv *Canvas) SetFont(font interface{}, size float32) { +func (cv *Canvas) SetFont(font interface{}, size float64) { switch v := font.(type) { case *Font: cv.state.font = v @@ -464,14 +463,14 @@ func (cv *Canvas) SetLineEnd(end lineEnd) { } // SetLineDash sets the line dash style -func (cv *Canvas) SetLineDash(dash []float32) { +func (cv *Canvas) SetLineDash(dash []float64) { l := len(dash) if l%2 == 0 { - d2 := make([]float32, l) + d2 := make([]float64, l) copy(d2, dash) cv.state.lineDash = d2 } else { - d2 := make([]float32, l*2) + d2 := make([]float64, l*2) copy(d2[:l], dash) copy(d2[l:], dash) cv.state.lineDash = d2 @@ -503,37 +502,37 @@ func (cv *Canvas) Restore() { } } -func (cv *Canvas) Scale(x, y float32) { - cv.state.transform = lm.Mat3x3Scale(lm.Vec2{x, y}).Mul(cv.state.transform) +func (cv *Canvas) Scale(x, y float64) { + cv.state.transform = matScale(vec{x, y}).mul(cv.state.transform) } -func (cv *Canvas) Translate(x, y float32) { - cv.state.transform = lm.Mat3x3Translate(lm.Vec2{x, y}).Mul(cv.state.transform) +func (cv *Canvas) Translate(x, y float64) { + cv.state.transform = matTranslate(vec{x, y}).mul(cv.state.transform) } -func (cv *Canvas) Rotate(angle float32) { - cv.state.transform = lm.Mat3x3Rotate(angle).Mul(cv.state.transform) +func (cv *Canvas) Rotate(angle float64) { + cv.state.transform = matRotate(angle).mul(cv.state.transform) } -func (cv *Canvas) Transform(a, b, c, d, e, f float32) { - cv.state.transform = lm.Mat3x3{a, b, 0, c, d, 0, e, f, 1}.Mul(cv.state.transform) +func (cv *Canvas) Transform(a, b, c, d, e, f float64) { + cv.state.transform = mat{a, b, 0, c, d, 0, e, f, 1}.mul(cv.state.transform) } -func (cv *Canvas) SetTransform(a, b, c, d, e, f float32) { - cv.state.transform = lm.Mat3x3{a, b, 0, c, d, 0, e, f, 1} +func (cv *Canvas) SetTransform(a, b, c, d, e, f float64) { + cv.state.transform = mat{a, b, 0, c, d, 0, e, f, 1} } // FillRect fills a rectangle with the active fill style -func (cv *Canvas) FillRect(x, y, w, h float32) { +func (cv *Canvas) FillRect(x, y, w, h float64) { cv.activate() - p0 := cv.tf(lm.Vec2{x, y}) - p1 := cv.tf(lm.Vec2{x, y + h}) - p2 := cv.tf(lm.Vec2{x + w, y + h}) - p3 := cv.tf(lm.Vec2{x + w, y}) + 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}) gli.BindBuffer(gl_ARRAY_BUFFER, buf) - data := [8]float32{p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]} + data := [8]float64{p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]} gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW) vertex := cv.useShader(&cv.state.fill) diff --git a/color.go b/color.go index 57ae697..da2537d 100644 --- a/color.go +++ b/color.go @@ -8,16 +8,16 @@ import ( ) type glColor struct { - r, g, b, a float32 + r, g, b, a float64 } func colorGoToGL(color color.Color) glColor { ir, ig, ib, ia := color.RGBA() var c glColor - c.r = float32(ir) / 65535 - c.g = float32(ig) / 65535 - c.b = float32(ib) / 65535 - c.a = float32(ia) / 65535 + c.r = float64(ir) / 65535 + c.g = float64(ig) / 65535 + c.b = float64(ib) / 65535 + c.a = float64(ia) / 65535 return c } @@ -74,18 +74,18 @@ func parseHexRunePair(rn1, rn2 rune) (int, bool) { return i1*16 + i2, true } -func parseColorComponent(value interface{}) (float32, bool) { +func parseColorComponent(value interface{}) (float64, bool) { switch v := value.(type) { case float32: - return v, true + return float64(v), true case float64: - return float32(v), true + return v, true case int: - return float32(v) / 255, true + return float64(v) / 255, true case uint: - return float32(v) / 255, true + return float64(v) / 255, true case uint8: - return float32(v) / 255, true + return float64(v) / 255, true case string: if len(v) == 0 { return 0, false @@ -99,7 +99,7 @@ func parseColorComponent(value interface{}) (float32, bool) { if err != nil { return 0, false } - return float32(conv) / 255, true + return float64(conv) / 255, true } else if strings.ContainsRune(v, '.') { conv, err := strconv.ParseFloat(v, 32) if err != nil { @@ -110,13 +110,13 @@ func parseColorComponent(value interface{}) (float32, bool) { } else if conv > 1 { conv = 1 } - return float32(conv), true + return float64(conv), true } else { conv, err := strconv.ParseUint(v, 10, 8) if err != nil { return 0, false } - return float32(conv) / 255, true + return float64(conv) / 255, true } } return 0, false @@ -130,25 +130,25 @@ func parseColor(value ...interface{}) (c glColor, ok bool) { ok = true return case [3]float32: - return glColor{r: v[0], g: v[1], b: v[2], a: 1}, true + return glColor{r: float64(v[0]), g: float64(v[1]), b: float64(v[2]), a: 1}, true case [4]float32: - return glColor{r: v[0], g: v[1], b: v[2], a: v[3]}, true + return glColor{r: float64(v[0]), g: float64(v[1]), b: float64(v[2]), a: float64(v[3])}, true case [3]float64: - return glColor{r: float32(v[0]), g: float32(v[1]), b: float32(v[2]), a: 1}, true + return glColor{r: v[0], g: v[1], b: v[2], a: 1}, true case [4]float64: - return glColor{r: float32(v[0]), g: float32(v[1]), b: float32(v[2]), a: float32(v[3])}, true + return glColor{r: v[0], g: v[1], b: v[2], a: v[3]}, true case [3]int: - return glColor{r: float32(v[0]) / 255, g: float32(v[1]) / 255, b: float32(v[2]) / 255, a: 1}, true + return glColor{r: float64(v[0]) / 255, g: float64(v[1]) / 255, b: float64(v[2]) / 255, a: 1}, true case [4]int: - return glColor{r: float32(v[0]) / 255, g: float32(v[1]) / 255, b: float32(v[2]) / 255, a: float32(v[3]) / 255}, true + return glColor{r: float64(v[0]) / 255, g: float64(v[1]) / 255, b: float64(v[2]) / 255, a: float64(v[3]) / 255}, true case [3]uint: - return glColor{r: float32(v[0]) / 255, g: float32(v[1]) / 255, b: float32(v[2]) / 255, a: 1}, true + return glColor{r: float64(v[0]) / 255, g: float64(v[1]) / 255, b: float64(v[2]) / 255, a: 1}, true case [4]uint: - return glColor{r: float32(v[0]) / 255, g: float32(v[1]) / 255, b: float32(v[2]) / 255, a: float32(v[3]) / 255}, true + return glColor{r: float64(v[0]) / 255, g: float64(v[1]) / 255, b: float64(v[2]) / 255, a: float64(v[3]) / 255}, true case [3]uint8: - return glColor{r: float32(v[0]) / 255, g: float32(v[1]) / 255, b: float32(v[2]) / 255, a: 1}, true + return glColor{r: float64(v[0]) / 255, g: float64(v[1]) / 255, b: float64(v[2]) / 255, a: 1}, true case [4]uint8: - return glColor{r: float32(v[0]) / 255, g: float32(v[1]) / 255, b: float32(v[2]) / 255, a: float32(v[3]) / 255}, true + return glColor{r: float64(v[0]) / 255, g: float64(v[1]) / 255, b: float64(v[2]) / 255, a: float64(v[3]) / 255}, true case string: if len(v) == 0 { return @@ -180,7 +180,7 @@ func parseColor(value ...interface{}) (c glColor, ok bool) { } ia = ia*16 + ia } - return glColor{r: float32(ir) / 255, g: float32(ig) / 255, b: float32(ib) / 255, a: float32(ia) / 255}, true + return glColor{r: float64(ir) / 255, g: float64(ig) / 255, b: float64(ib) / 255, a: float64(ia) / 255}, true } else if len(str) == 6 || len(str) == 8 { var ir, ig, ib int ia := 255 @@ -202,7 +202,7 @@ func parseColor(value ...interface{}) (c glColor, ok bool) { return } } - return glColor{r: float32(ir) / 255, g: float32(ig) / 255, b: float32(ib) / 255, a: float32(ia) / 255}, true + return glColor{r: float64(ir) / 255, g: float64(ig) / 255, b: float64(ib) / 255, a: float64(ia) / 255}, true } else { return } @@ -211,16 +211,16 @@ func parseColor(value ...interface{}) (c glColor, ok bool) { var ir, ig, ib, ia int n, err := fmt.Sscanf(v, "rgb(%d,%d,%d)", &ir, &ig, &ib) if err == nil && n == 3 { - return glColor{r: float32(ir) / 255, g: float32(ig) / 255, b: float32(ib) / 255, a: 1}, true + return glColor{r: float64(ir) / 255, g: float64(ig) / 255, b: float64(ib) / 255, a: 1}, true } n, err = fmt.Sscanf(v, "rgba(%d,%d,%d,%d)", &ir, &ig, &ib, &ia) if err == nil && n == 4 { - return glColor{r: float32(ir) / 255, g: float32(ig) / 255, b: float32(ib) / 255, a: float32(ia) / 255}, true + return glColor{r: float64(ir) / 255, g: float64(ig) / 255, b: float64(ib) / 255, a: float64(ia) / 255}, true } } } } else if len(value) == 3 || len(value) == 4 { - var pr, pg, pb, pa float32 + var pr, pg, pb, pa float64 pr, ok = parseColorComponent(value[0]) if !ok { return diff --git a/gradients.go b/gradients.go index e818571..641e00c 100644 --- a/gradients.go +++ b/gradients.go @@ -1,10 +1,8 @@ package canvas import ( + "math" "runtime" - - "github.com/barnex/fmath" - "github.com/tfriedel6/lm" ) type LinearGradient struct { @@ -13,11 +11,11 @@ type LinearGradient struct { type RadialGradient struct { gradient - radFrom, radTo float32 + radFrom, radTo float64 } type gradient struct { - from, to lm.Vec2 + from, to vec stops []gradientStop tex uint32 loaded bool @@ -25,12 +23,12 @@ type gradient struct { } type gradientStop struct { - pos float32 + pos float64 color glColor } -func NewLinearGradient(x0, y0, x1, y1 float32) *LinearGradient { - lg := &LinearGradient{gradient: gradient{from: lm.Vec2{x0, y0}, to: lm.Vec2{x1, y1}}} +func NewLinearGradient(x0, y0, x1, y1 float64) *LinearGradient { + lg := &LinearGradient{gradient: gradient{from: vec{x0, y0}, to: vec{x1, y1}}} gli.GenTextures(1, &lg.tex) gli.ActiveTexture(gl_TEXTURE0) gli.BindTexture(gl_TEXTURE_1D, lg.tex) @@ -45,8 +43,8 @@ func NewLinearGradient(x0, y0, x1, y1 float32) *LinearGradient { return lg } -func NewRadialGradient(x0, y0, r0, x1, y1, r1 float32) *RadialGradient { - rg := &RadialGradient{gradient: gradient{from: lm.Vec2{x0, y0}, to: lm.Vec2{x1, y1}}, radFrom: r0, radTo: r1} +func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) *RadialGradient { + rg := &RadialGradient{gradient: gradient{from: vec{x0, y0}, to: vec{x1, y1}}, radFrom: r0, radTo: r1} gli.GenTextures(1, &rg.tex) gli.ActiveTexture(gl_TEXTURE0) gli.BindTexture(gl_TEXTURE_1D, rg.tex) @@ -75,18 +73,18 @@ func (g *gradient) load() { var pixels [2048 * 4]byte pp := 0 for i := 0; i < 2048; i++ { - c := g.colorAt(float32(i) / 2047) - pixels[pp] = byte(fmath.Floor(c.r*255 + 0.5)) - pixels[pp+1] = byte(fmath.Floor(c.g*255 + 0.5)) - pixels[pp+2] = byte(fmath.Floor(c.b*255 + 0.5)) - pixels[pp+3] = byte(fmath.Floor(c.a*255 + 0.5)) + c := g.colorAt(float64(i) / 2047) + pixels[pp] = byte(math.Floor(c.r*255 + 0.5)) + pixels[pp+1] = byte(math.Floor(c.g*255 + 0.5)) + pixels[pp+2] = byte(math.Floor(c.b*255 + 0.5)) + pixels[pp+3] = byte(math.Floor(c.a*255 + 0.5)) pp += 4 } gli.TexImage1D(gl_TEXTURE_1D, 0, gl_RGBA, 2048, 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&pixels[0])) g.loaded = true } -func (g *gradient) colorAt(pos float32) glColor { +func (g *gradient) colorAt(pos float64) glColor { if len(g.stops) == 0 { return glColor{} } else if len(g.stops) == 1 { @@ -115,7 +113,7 @@ func (g *gradient) colorAt(pos float32) glColor { return c } -func (g *gradient) AddColorStop(pos float32, color ...interface{}) { +func (g *gradient) AddColorStop(pos float64, color ...interface{}) { c, _ := parseColor(color...) insert := len(g.stops) for i, stop := range g.stops { diff --git a/imagedata.go b/imagedata.go index 6a890af..8c3863a 100644 --- a/imagedata.go +++ b/imagedata.go @@ -81,7 +81,7 @@ func (cv *Canvas) PutImageData(img *image.RGBA, x, y int) { gli.UseProgram(ir.id) gli.Uniform1i(ir.image, 0) - gli.Uniform2f(ir.canvasSize, cv.fw, cv.fh) + gli.Uniform2f(ir.canvasSize, float32(cv.fw), float32(cv.fh)) gli.VertexAttribPointer(ir.vertex, 2, gl_FLOAT, false, 0, nil) gli.VertexAttribPointer(ir.texCoord, 2, gl_FLOAT, false, 0, gli.PtrOffset(8*4)) gli.EnableVertexAttribArray(ir.vertex) diff --git a/images.go b/images.go index 27d5965..9d9f82d 100644 --- a/images.go +++ b/images.go @@ -7,8 +7,6 @@ import ( "io/ioutil" "runtime" "unsafe" - - "github.com/tfriedel6/lm" ) type Image struct { @@ -174,7 +172,7 @@ func (img *Image) Delete() { img.deleted = true } -func (cv *Canvas) DrawImage(image interface{}, coords ...float32) { +func (cv *Canvas) DrawImage(image interface{}, coords ...float64) { var img *Image switch v := image.(type) { case *Image: @@ -200,9 +198,9 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float32) { cv.activate() - var sx, sy, sw, sh, dx, dy, dw, dh float32 - sw, sh = float32(img.w), float32(img.h) - dw, dh = float32(img.w), float32(img.h) + var sx, sy, sw, sh, dx, dy, dw, dh float64 + sw, sh = float64(img.w), float64(img.h) + dw, dh = float64(img.w), float64(img.h) if len(coords) == 2 { dx, dy = coords[0], coords[1] } else if len(coords) == 4 { @@ -215,18 +213,18 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float32) { dw, dh = coords[6], coords[7] } - sx /= float32(img.w) - sy /= float32(img.h) - sw /= float32(img.w) - sh /= float32(img.h) + sx /= float64(img.w) + sy /= float64(img.h) + sw /= float64(img.w) + sh /= float64(img.h) - p0 := cv.tf(lm.Vec2{dx, dy}) - p1 := cv.tf(lm.Vec2{dx, dy + dh}) - p2 := cv.tf(lm.Vec2{dx + dw, dy + dh}) - p3 := cv.tf(lm.Vec2{dx + dw, dy}) + p0 := cv.tf(vec{dx, dy}) + p1 := cv.tf(vec{dx, dy + dh}) + p2 := cv.tf(vec{dx + dw, dy + dh}) + p3 := cv.tf(vec{dx + dw, dy}) gli.BindBuffer(gl_ARRAY_BUFFER, buf) - data := [16]float32{p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], + data := [16]float64{p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], sx, sy, sx, sy + sh, sx + sw, sy + sh, sx + sw, sy} gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW) @@ -235,7 +233,7 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float32) { gli.UseProgram(ir.id) gli.Uniform1i(ir.image, 0) - gli.Uniform2f(ir.canvasSize, cv.fw, cv.fh) + gli.Uniform2f(ir.canvasSize, float32(cv.fw), float32(cv.fh)) gli.VertexAttribPointer(ir.vertex, 2, gl_FLOAT, false, 0, nil) gli.VertexAttribPointer(ir.texCoord, 2, gl_FLOAT, false, 0, gli.PtrOffset(8*4)) gli.EnableVertexAttribArray(ir.vertex) diff --git a/math.go b/math.go new file mode 100644 index 0000000..2b11098 --- /dev/null +++ b/math.go @@ -0,0 +1,141 @@ +package canvas + +import ( + "fmt" + "math" +) + +type vec [2]float64 + +func (v vec) String() string { + return fmt.Sprintf("[%f,%f]", v[0], v[1]) +} + +func (v1 vec) add(v2 vec) vec { + return vec{v1[0] + v2[0], v1[1] + v2[1]} +} + +func (v1 vec) sub(v2 vec) vec { + return vec{v1[0] - v2[0], v1[1] - v2[1]} +} + +func (v1 vec) mul(v2 vec) vec { + return vec{v1[0] * v2[0], v1[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, float64) { + return vec{ + m[0]*v[0] + m[3]*v[1] + m[6], + m[1]*v[0] + m[4]*v[1] + m[7]}, + m[2]*v[0] + m[5]*v[1] + m[8] +} + +func (v1 vec) div(v2 vec) vec { + return vec{v1[0] / v2[0], v1[1] / v2[1]} +} + +func (v vec) divf(f float64) vec { + return vec{v[0] / f, v[1] / f} +} + +func (v1 vec) dot(v2 vec) float64 { + return v1[0]*v2[0] + v1[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 [9]float64 + +func (m *mat) String() string { + return fmt.Sprintf("[%f,%f,%f,\n %f,%f,%f,\n %f,%f,%f,]", m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8]) +} + +func matIdentity() mat { + return mat{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1} +} + +func matTranslate(v vec) mat { + return mat{ + 1, 0, 0, + 0, 1, 0, + v[0], v[1], 1} +} + +func matScale(v vec) mat { + return mat{ + v[0], 0, 0, + 0, v[1], 0, + 0, 0, 1} +} + +func matRotate(radians float64) mat { + s, c := math.Sincos(radians) + return mat{ + c, s, 0, + -s, c, 0, + 0, 0, 1} +} + +func (m1 mat) mul(m2 mat) mat { + return mat{ + m1[0]*m2[0] + m1[1]*m2[3] + m1[2]*m2[6], + m1[0]*m2[1] + m1[1]*m2[4] + m1[2]*m2[7], + m1[0]*m2[2] + m1[1]*m2[5] + m1[2]*m2[8], + m1[3]*m2[0] + m1[4]*m2[3] + m1[5]*m2[6], + m1[3]*m2[1] + m1[4]*m2[4] + m1[5]*m2[7], + m1[3]*m2[2] + m1[4]*m2[5] + m1[5]*m2[8], + m1[6]*m2[0] + m1[7]*m2[3] + m1[8]*m2[6], + m1[6]*m2[1] + m1[7]*m2[4] + m1[8]*m2[7], + m1[6]*m2[2] + m1[7]*m2[5] + m1[8]*m2[8]} +} + +func (m mat) invert() mat { + var identity float64 = 1.0 / (m[0]*m[4]*m[8] + m[3]*m[7]*m[2] + m[6]*m[1]*m[5] - m[6]*m[4]*m[2] - m[3]*m[1]*m[8] - m[0]*m[7]*m[5]) + + return mat{ + (m[4]*m[8] - m[5]*m[7]) * identity, + (m[2]*m[7] - m[1]*m[8]) * identity, + (m[1]*m[5] - m[2]*m[4]) * identity, + (m[5]*m[6] - m[3]*m[8]) * identity, + (m[0]*m[8] - m[2]*m[6]) * identity, + (m[2]*m[3] - m[0]*m[5]) * identity, + (m[3]*m[7] - m[4]*m[6]) * identity, + (m[1]*m[6] - m[0]*m[7]) * identity, + (m[0]*m[4] - m[1]*m[3]) * identity} +} + +func (m mat) f32() [9]float32 { + return [9]float32{ + float32(m[0]), float32(m[1]), float32(m[2]), + float32(m[3]), float32(m[4]), float32(m[5]), + float32(m[6]), float32(m[7]), float32(m[8])} +} diff --git a/paths.go b/paths.go index c8a4f40..69f15ec 100644 --- a/paths.go +++ b/paths.go @@ -3,9 +3,6 @@ package canvas import ( "math" "unsafe" - - "github.com/barnex/fmath" - "github.com/tfriedel6/lm" ) func (cv *Canvas) BeginPath() { @@ -19,37 +16,37 @@ func (cv *Canvas) BeginPath() { cv.polyPath = cv.polyPath[:0] } -func isSamePoint(a, b lm.Vec2, maxDist float32) bool { - return fmath.Abs(b[0]-a[0]) <= maxDist && fmath.Abs(b[1]-a[1]) <= maxDist +func isSamePoint(a, b vec, maxDist float64) bool { + return math.Abs(b[0]-a[0]) <= maxDist && math.Abs(b[1]-a[1]) <= maxDist } -func (cv *Canvas) MoveTo(x, y float32) { - tf := cv.tf(lm.Vec2{x, y}) +func (cv *Canvas) MoveTo(x, y float64) { + tf := cv.tf(vec{x, y}) if len(cv.linePath) > 0 && isSamePoint(cv.linePath[len(cv.linePath)-1].tf, tf, 0.1) { return } - cv.linePath = append(cv.linePath, pathPoint{pos: lm.Vec2{x, y}, tf: tf, move: true}) - cv.polyPath = append(cv.polyPath, pathPoint{pos: lm.Vec2{x, y}, tf: tf, move: true}) + cv.linePath = append(cv.linePath, pathPoint{pos: vec{x, y}, tf: tf, move: true}) + cv.polyPath = append(cv.polyPath, pathPoint{pos: vec{x, y}, tf: tf, move: true}) } -func (cv *Canvas) LineTo(x, y float32) { +func (cv *Canvas) LineTo(x, y float64) { cv.strokeLineTo(x, y) cv.fillLineTo(x, y) } -func (cv *Canvas) strokeLineTo(x, y float32) { - if len(cv.linePath) > 0 && isSamePoint(cv.linePath[len(cv.linePath)-1].tf, cv.tf(lm.Vec2{x, y}), 0.1) { +func (cv *Canvas) strokeLineTo(x, y float64) { + if len(cv.linePath) > 0 && isSamePoint(cv.linePath[len(cv.linePath)-1].tf, cv.tf(vec{x, y}), 0.1) { return } if len(cv.linePath) == 0 { - cv.linePath = append(cv.linePath, pathPoint{pos: lm.Vec2{x, y}, tf: cv.tf(lm.Vec2{x, y}), move: true}) + cv.linePath = append(cv.linePath, pathPoint{pos: vec{x, y}, tf: cv.tf(vec{x, y}), move: true}) return } if len(cv.state.lineDash) > 0 { lp := cv.linePath[len(cv.linePath)-1].pos - tp := lm.Vec2{x, y} - v := tp.Sub(lp) - vl := v.Len() + tp := vec{x, y} + v := tp.sub(lp) + vl := v.len() prev := cv.state.lineDashOffset for vl > 0 { draw := cv.state.lineDashPoint%2 == 0 @@ -58,7 +55,7 @@ func (cv *Canvas) strokeLineTo(x, y float32) { if cv.state.lineDashOffset > cv.state.lineDash[cv.state.lineDashPoint] { cv.state.lineDashOffset = 0 dl := cv.state.lineDash[cv.state.lineDashPoint] - prev - p = lp.Add(v.MulF(dl / vl)) + p = lp.add(v.mulf(dl / vl)) vl -= dl cv.state.lineDashPoint++ cv.state.lineDashPoint %= len(cv.state.lineDash) @@ -76,36 +73,36 @@ func (cv *Canvas) strokeLineTo(x, y float32) { } lp = p - v = tp.Sub(lp) + v = tp.sub(lp) } } else { - tf := cv.tf(lm.Vec2{x, y}) + tf := cv.tf(vec{x, y}) cv.linePath[len(cv.linePath)-1].next = tf cv.linePath[len(cv.linePath)-1].attach = true - cv.linePath = append(cv.linePath, pathPoint{pos: lm.Vec2{x, y}, tf: tf, move: false}) + cv.linePath = append(cv.linePath, pathPoint{pos: vec{x, y}, tf: tf, move: false}) } } -func (cv *Canvas) fillLineTo(x, y float32) { - if len(cv.polyPath) > 0 && isSamePoint(cv.polyPath[len(cv.polyPath)-1].tf, cv.tf(lm.Vec2{x, y}), 0.1) { +func (cv *Canvas) fillLineTo(x, y float64) { + if len(cv.polyPath) > 0 && isSamePoint(cv.polyPath[len(cv.polyPath)-1].tf, cv.tf(vec{x, y}), 0.1) { return } if len(cv.polyPath) == 0 { - cv.polyPath = append(cv.polyPath, pathPoint{pos: lm.Vec2{x, y}, tf: cv.tf(lm.Vec2{x, y}), move: true}) + cv.polyPath = append(cv.polyPath, pathPoint{pos: vec{x, y}, tf: cv.tf(vec{x, y}), move: true}) return } - tf := cv.tf(lm.Vec2{x, y}) + tf := cv.tf(vec{x, y}) cv.polyPath[len(cv.polyPath)-1].next = tf cv.polyPath[len(cv.polyPath)-1].attach = true - cv.polyPath = append(cv.polyPath, pathPoint{pos: lm.Vec2{x, y}, tf: tf, move: false}) + cv.polyPath = append(cv.polyPath, pathPoint{pos: vec{x, y}, tf: tf, move: false}) } -func (cv *Canvas) Arc(x, y, radius, startAngle, endAngle float32, anticlockwise bool) { - startAngle = fmath.Mod(startAngle, math.Pi*2) +func (cv *Canvas) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise bool) { + startAngle = math.Mod(startAngle, math.Pi*2) if startAngle < 0 { startAngle += math.Pi * 2 } - endAngle = fmath.Mod(endAngle, math.Pi*2) + endAngle = math.Mod(endAngle, math.Pi*2) if endAngle < 0 { endAngle += math.Pi * 2 } @@ -114,8 +111,8 @@ func (cv *Canvas) Arc(x, y, radius, startAngle, endAngle float32, anticlockwise } else if anticlockwise && endAngle >= startAngle { endAngle -= math.Pi * 2 } - tr := cv.tf(lm.Vec2{radius, radius}) - step := 6 / fmath.Max(tr[0], tr[1]) + tr := cv.tf(vec{radius, radius}) + step := 6 / math.Max(tr[0], tr[1]) if step > 0.8 { step = 0.8 } else if step < 0.05 { @@ -123,106 +120,106 @@ func (cv *Canvas) Arc(x, y, radius, startAngle, endAngle float32, anticlockwise } if anticlockwise { for a := startAngle; a > endAngle; a -= step { - s, c := fmath.Sincos(a) + s, c := math.Sincos(a) cv.LineTo(x+radius*c, y+radius*s) } } else { for a := startAngle; a < endAngle; a += step { - s, c := fmath.Sincos(a) + s, c := math.Sincos(a) cv.LineTo(x+radius*c, y+radius*s) } } - s, c := fmath.Sincos(endAngle) + s, c := math.Sincos(endAngle) cv.LineTo(x+radius*c, y+radius*s) } -func (cv *Canvas) ArcTo(x1, y1, x2, y2, radius float32) { +func (cv *Canvas) ArcTo(x1, y1, x2, y2, radius float64) { if len(cv.linePath) == 0 { return } - p0, p1, p2 := cv.linePath[len(cv.linePath)-1].pos, lm.Vec2{x1, y1}, lm.Vec2{x2, y2} - v0, v1 := p0.Sub(p1).Norm(), p2.Sub(p1).Norm() - angle := fmath.Acos(v0.Dot(v1)) + p0, p1, p2 := cv.linePath[len(cv.linePath)-1].pos, vec{x1, y1}, vec{x2, y2} + 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 { cv.LineTo(x2, y2) return } // cv are the vectors orthogonal to the lines that point to the center of the circle - cv0 := lm.Vec2{-v0[1], v0[0]} - cv1 := lm.Vec2{v1[1], -v1[0]} - x := cv1.Sub(cv0).Div(v0.Sub(v1))[0] * radius + cv0 := vec{-v0[1], v0[0]} + cv1 := 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(fmath.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() cv.Arc(center[0], center[1], radius, a0, a1, x > 0) } -func (cv *Canvas) QuadraticCurveTo(x1, y1, x2, y2 float32) { +func (cv *Canvas) QuadraticCurveTo(x1, y1, x2, y2 float64) { if len(cv.linePath) == 0 { return } p0 := cv.linePath[len(cv.linePath)-1].pos - p1 := lm.Vec2{x1, y1} - p2 := lm.Vec2{x2, y2} - v0 := p1.Sub(p0) - v1 := p2.Sub(p1) + p1 := vec{x1, y1} + p2 := vec{x2, y2} + v0 := p1.sub(p0) + v1 := p2.sub(p1) tp0, tp1, tp2 := cv.tf(p0), cv.tf(p1), cv.tf(p2) - tv0 := tp1.Sub(tp0) - tv1 := tp2.Sub(tp1) + tv0 := tp1.sub(tp0) + tv1 := tp2.sub(tp1) - step := 1 / fmath.Max(fmath.Max(tv0[0], tv0[1]), fmath.Max(tv1[0], tv1[1])) + step := 1 / math.Max(math.Max(tv0[0], tv0[1]), math.Max(tv1[0], tv1[1])) if step > 0.1 { step = 0.1 } else if step < 0.005 { step = 0.005 } - for r := float32(0); r < 1; r += step { - i0 := v0.MulF(r).Add(p0) - i1 := v1.MulF(r).Add(p1) - p := i1.Sub(i0).MulF(r).Add(i0) + for r := 0.0; r < 1; r += step { + i0 := v0.mulf(r).add(p0) + i1 := v1.mulf(r).add(p1) + p := i1.sub(i0).mulf(r).add(i0) cv.LineTo(p[0], p[1]) } } -func (cv *Canvas) BezierCurveTo(x1, y1, x2, y2, x3, y3 float32) { +func (cv *Canvas) BezierCurveTo(x1, y1, x2, y2, x3, y3 float64) { if len(cv.linePath) == 0 { return } p0 := cv.linePath[len(cv.linePath)-1].pos - p1 := lm.Vec2{x1, y1} - p2 := lm.Vec2{x2, y2} - p3 := lm.Vec2{x3, y3} - v0 := p1.Sub(p0) - v1 := p2.Sub(p1) - v2 := p3.Sub(p2) + p1 := vec{x1, y1} + p2 := vec{x2, y2} + p3 := vec{x3, y3} + v0 := p1.sub(p0) + v1 := p2.sub(p1) + v2 := p3.sub(p2) tp0, tp1, tp2, tp3 := cv.tf(p0), cv.tf(p1), cv.tf(p2), cv.tf(p3) - tv0 := tp1.Sub(tp0) - tv1 := tp2.Sub(tp1) - tv2 := tp3.Sub(tp2) + tv0 := tp1.sub(tp0) + tv1 := tp2.sub(tp1) + tv2 := tp3.sub(tp2) - step := 1 / fmath.Max(fmath.Max(fmath.Max(tv0[0], tv0[1]), fmath.Max(tv1[0], tv1[1])), fmath.Max(tv2[0], tv2[1])) + step := 1 / math.Max(math.Max(math.Max(tv0[0], tv0[1]), math.Max(tv1[0], tv1[1])), math.Max(tv2[0], tv2[1])) if step > 0.1 { step = 0.1 } else if step < 0.005 { step = 0.005 } - for r := float32(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) - p := j1.Sub(j0).MulF(r).Add(j0) + 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) + p := j1.sub(j0).mulf(r).add(j0) cv.LineTo(p[0], p[1]) } } @@ -255,10 +252,10 @@ func (cv *Canvas) Stroke() { var buf [1000]float32 tris := buf[:0] - tris = append(tris, 0, 0, cv.fw, 0, cv.fw, cv.fh, 0, 0, cv.fw, cv.fh, 0, cv.fh) + tris = append(tris, 0, 0, float32(cv.fw), 0, float32(cv.fw), float32(cv.fh), 0, 0, float32(cv.fw), float32(cv.fh), 0, float32(cv.fh)) start := true - var p0 lm.Vec2 + var p0 vec for _, p := range cv.linePath { if p.move { p0 = p.tf @@ -267,22 +264,22 @@ func (cv *Canvas) Stroke() { } p1 := p.tf - v0 := p1.Sub(p0).Norm() - v1 := lm.Vec2{v0[1], -v0[0]}.MulF(cv.state.lineWidth * 0.5) - v0 = v0.MulF(cv.state.lineWidth * 0.5) + 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) - 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.lineEnd { 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: tris = cv.addCircleTris(p0, cv.state.lineWidth*0.5, tris) } @@ -293,16 +290,16 @@ func (cv *Canvas) Stroke() { 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: tris = cv.addCircleTris(p1, cv.state.lineWidth*0.5, tris) } } tris = append(tris, - lp0[0], lp0[1], lp1[0], lp1[1], lp3[0], lp3[1], - lp0[0], lp0[1], lp3[0], lp3[1], lp2[0], lp2[1]) + float32(lp0[0]), float32(lp0[1]), float32(lp1[0]), float32(lp1[1]), float32(lp3[0]), float32(lp3[1]), + float32(lp0[0]), float32(lp0[1]), float32(lp3[0]), float32(lp3[1]), float32(lp2[0]), float32(lp2[1])) if p.attach { tris = cv.lineJoint(p, p0, p1, p.next, lp0, lp1, lp2, lp3, tris) @@ -316,7 +313,7 @@ func (cv *Canvas) Stroke() { gli.UseProgram(sr.id) gli.Uniform4f(sr.color, 0, 0, 0, 0) - gli.Uniform2f(sr.canvasSize, cv.fw, cv.fh) + gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh)) gli.ColorMask(false, false, false, false) @@ -344,41 +341,41 @@ func (cv *Canvas) Stroke() { gli.StencilMask(0xFF) } -func (cv *Canvas) lineJoint(p pathPoint, p0, p1, p2, l0p0, l0p1, l0p2, l0p3 lm.Vec2, tris []float32) []float32 { - v2 := p1.Sub(p2).Norm() - v3 := lm.Vec2{v2[1], -v2[0]}.MulF(cv.state.lineWidth * 0.5) +func (cv *Canvas) lineJoint(p pathPoint, p0, p1, p2, l0p0, l0p1, l0p2, l0p3 vec, tris []float32) []float32 { + v2 := p1.sub(p2).norm() + v3 := 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 lm.Vec2 - if l0p1.Sub(l1p1).LenSqr() < 0.000000001 { - ip0 = l0p1.Sub(l1p1).MulF(0.5).Add(l1p1) + var ip0, ip1 vec + if l0p1.sub(l1p1).lenSqr() < 0.000000001 { + ip0 = l0p1.sub(l1p1).mulf(0.5).add(l1p1) } else { ip0, _, _ = lineIntersection(l0p0, l0p1, l1p1, l1p0) } - 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 { ip1, _, _ = lineIntersection(l0p2, l0p3, l1p3, l1p2) } tris = append(tris, - p1[0], p1[1], l0p1[0], l0p1[1], ip0[0], ip0[1], - p1[0], p1[1], ip0[0], ip0[1], l1p1[0], l1p1[1], - p1[0], p1[1], l1p3[0], l1p3[1], ip1[0], ip1[1], - p1[0], p1[1], ip1[0], ip1[1], l0p3[0], l0p3[1]) + float32(p1[0]), float32(p1[1]), float32(l0p1[0]), float32(l0p1[1]), float32(ip0[0]), float32(ip0[1]), + float32(p1[0]), float32(p1[1]), float32(ip0[0]), float32(ip0[1]), float32(l1p1[0]), float32(l1p1[1]), + float32(p1[0]), float32(p1[1]), float32(l1p3[0]), float32(l1p3[1]), float32(ip1[0]), float32(ip1[1]), + float32(p1[0]), float32(p1[1]), float32(ip1[0]), float32(ip1[1]), float32(l0p3[0]), float32(l0p3[1])) case Bevel: - l1p1 := p1.Sub(v3) - l1p3 := p1.Add(v3) + l1p1 := p1.sub(v3) + l1p3 := p1.add(v3) tris = append(tris, - p1[0], p1[1], l0p1[0], l0p1[1], l1p1[0], l1p1[1], - p1[0], p1[1], l1p3[0], l1p3[1], l0p3[0], l0p3[1]) + float32(p1[0]), float32(p1[1]), float32(l0p1[0]), float32(l0p1[1]), float32(l1p1[0]), float32(l1p1[1]), + float32(p1[0]), float32(p1[1]), float32(l1p3[0]), float32(l1p3[1]), float32(l0p3[0]), float32(l0p3[1])) case Round: tris = cv.addCircleTris(p1, cv.state.lineWidth*0.5, tris) } @@ -386,8 +383,8 @@ func (cv *Canvas) lineJoint(p pathPoint, p0, p1, p2, l0p0, l0p1, l0p2, l0p3 lm.V return tris } -func (cv *Canvas) addCircleTris(center lm.Vec2, radius float32, tris []float32) []float32 { - p0 := lm.Vec2{center[0], center[1] + radius} +func (cv *Canvas) addCircleTris(center vec, radius float64, tris []float32) []float32 { + p0 := vec{center[0], center[1] + radius} step := 6 / radius if step > 0.8 { step = 0.8 @@ -395,30 +392,30 @@ func (cv *Canvas) addCircleTris(center lm.Vec2, radius float32, tris []float32) step = 0.05 } for angle := step; angle <= math.Pi*2+step; angle += step { - s, c := fmath.Sincos(angle) - p1 := lm.Vec2{center[0] + s*radius, center[1] + c*radius} - tris = append(tris, center[0], center[1], p0[0], p0[1], p1[0], p1[1]) + s, c := math.Sincos(angle) + p1 := vec{center[0] + s*radius, center[1] + c*radius} + tris = append(tris, float32(center[0]), float32(center[1]), float32(p0[0]), float32(p0[1]), float32(p1[0]), float32(p1[1])) p0 = p1 } return tris } -func lineIntersection(a0, a1, b0, b1 lm.Vec2) (lm.Vec2, float32, float32) { - va := a1.Sub(a0) - vb := b1.Sub(b0) +func lineIntersection(a0, a1, b0, b1 vec) (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 lm.Vec2{}, float32(math.Inf(1)), float32(math.Inf(1)) + return vec{}, float64(math.Inf(1)), float64(math.Inf(1)) } p := (vb[1]*(a0[0]-b0[0]) - a0[1]*vb[0] + b0[1]*vb[0]) / (va[1]*vb[0] - va[0]*vb[1]) - var q float32 + var q float64 if vb[0] == 0 { q = (a0[1] + p*va[1] - b0[1]) / vb[1] } else { 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 (cv *Canvas) Fill() { @@ -471,7 +468,7 @@ func (cv *Canvas) clip(path []pathPoint) { var triBuf [1000]float32 tris := triBuf[:0] - tris = append(tris, 0, 0, cv.fw, 0, cv.fw, cv.fh, 0, 0, cv.fw, cv.fh, 0, cv.fh) + tris = append(tris, 0, 0, float32(cv.fw), 0, float32(cv.fw), float32(cv.fh), 0, 0, float32(cv.fw), float32(cv.fh), 0, float32(cv.fh)) tris = triangulatePath(path, tris) if len(tris) > 0 { return @@ -483,7 +480,7 @@ func (cv *Canvas) clip(path []pathPoint) { gli.UseProgram(sr.id) gli.Uniform4f(sr.color, 1, 1, 1, 1) - gli.Uniform2f(sr.canvasSize, cv.fw, cv.fh) + gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh)) gli.EnableVertexAttribArray(sr.vertex) gli.DrawArrays(gl_TRIANGLES, 0, 6) diff --git a/text.go b/text.go index 31156cd..01a3db6 100644 --- a/text.go +++ b/text.go @@ -7,7 +7,6 @@ import ( "github.com/golang/freetype" "github.com/golang/freetype/truetype" - "github.com/tfriedel6/lm" "golang.org/x/image/font" "golang.org/x/image/math/fixed" ) @@ -51,7 +50,7 @@ func LoadFont(src interface{}, name string) (*Font, error) { return f, nil } -func (cv *Canvas) FillText(str string, x, y float32) { +func (cv *Canvas) FillText(str string, x, y float64) { cv.activate() frc := fontRenderingContext @@ -83,7 +82,7 @@ func (cv *Canvas) FillText(str string, x, y float32) { if frc.hinting != font.HintingNone { kern = (kern + 32) &^ 63 } - x += float32(kern) / 64 + x += float64(kern) / 64 } advance, mask, offset, err := frc.glyph(idx, fixed.Point26_6{}) if err != nil { @@ -98,13 +97,13 @@ func (cv *Canvas) FillText(str string, x, y float32) { gli.TexSubImage2D(gl_TEXTURE_2D, 0, 0, int32(alphaTexSize-1-y), int32(w), 1, gl_ALPHA, gl_UNSIGNED_BYTE, gli.Ptr(&mask.Pix[off])) } - p0 := cv.tf(lm.Vec2{float32(bounds.Min.X) + x, float32(bounds.Min.Y) + y}) - p1 := cv.tf(lm.Vec2{float32(bounds.Min.X) + x, float32(bounds.Max.Y) + y}) - p2 := cv.tf(lm.Vec2{float32(bounds.Max.X) + x, float32(bounds.Max.Y) + y}) - p3 := cv.tf(lm.Vec2{float32(bounds.Max.X) + x, float32(bounds.Min.Y) + y}) - tw := float32(bounds.Dx()) / alphaTexSize - th := float32(bounds.Dy()) / alphaTexSize - data := [16]float32{p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], + p0 := cv.tf(vec{float64(bounds.Min.X) + x, float64(bounds.Min.Y) + y}) + p1 := cv.tf(vec{float64(bounds.Min.X) + x, float64(bounds.Max.Y) + y}) + p2 := cv.tf(vec{float64(bounds.Max.X) + x, float64(bounds.Max.Y) + y}) + p3 := cv.tf(vec{float64(bounds.Max.X) + x, float64(bounds.Min.Y) + y}) + tw := float64(bounds.Dx()) / alphaTexSize + th := float64(bounds.Dy()) / alphaTexSize + data := [16]float64{p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], 0, 1, 0, 1 - th, tw, 1 - th, tw, 1} gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW) @@ -116,7 +115,7 @@ func (cv *Canvas) FillText(str string, x, y float32) { gli.TexSubImage2D(gl_TEXTURE_2D, 0, 0, int32(alphaTexSize-1-y), int32(w), 1, gl_ALPHA, gl_UNSIGNED_BYTE, gli.Ptr(&zeroes[0])) } - x += float32(advance) / 64 + x += float64(advance) / 64 } gli.DisableVertexAttribArray(vertex) diff --git a/triangulation.go b/triangulation.go index d384d68..a1fc2be 100644 --- a/triangulation.go +++ b/triangulation.go @@ -3,11 +3,9 @@ package canvas import ( "math" "sort" - - "github.com/tfriedel6/lm" ) -func pointIsRightOfLine(a, b, p lm.Vec2) bool { +func pointIsRightOfLine(a, b, p vec) bool { if a[1] == b[1] { return false } @@ -17,13 +15,13 @@ func pointIsRightOfLine(a, b, p lm.Vec2) bool { if p[1] < a[1] || p[1] > b[1] { return 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 } -func triangleContainsPoint(a, b, c, p lm.Vec2) bool { +func triangleContainsPoint(a, b, c, p 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 @@ -52,7 +50,7 @@ func triangleContainsPoint(a, b, c, p lm.Vec2) bool { return count == 1 } -func polygonContainsPoint(polygon []lm.Vec2, p lm.Vec2) bool { +func polygonContainsPoint(polygon []vec, p vec) bool { a := polygon[len(polygon)-1] count := 0 for _, b := range polygon { @@ -65,7 +63,7 @@ func polygonContainsPoint(polygon []lm.Vec2, p lm.Vec2) bool { } func triangulatePath(path []pathPoint, target []float32) []float32 { - var buf [500]lm.Vec2 + var buf [500]vec polygon := buf[:0] for _, p := range path { polygon = append(polygon, p.tf) @@ -78,7 +76,7 @@ func triangulatePath(path []pathPoint, target []float32) []float32 { a := polygon[i] b := polygon[(i+1)%len(polygon)] c := polygon[(i+2)%len(polygon)] - if isSamePoint(a, c, math.SmallestNonzeroFloat32) { + if isSamePoint(a, c, math.SmallestNonzeroFloat64) { break } for i2, p := range polygon { @@ -88,13 +86,13 @@ func triangulatePath(path []pathPoint, target []float32) []float32 { if triangleContainsPoint(a, b, c, p) { continue triangles } - center := a.Add(b).Add(c).DivF(3) + center := a.add(b).add(c).divf(3) if !polygonContainsPoint(polygon, center) { continue triangles } break } - target = append(target, a[0], a[1], b[0], b[1], c[0], c[1]) + target = append(target, float32(a[0]), float32(a[1]), float32(b[0]), float32(b[1]), float32(c[0]), float32(c[1])) break } remove := (i + 1) % len(polygon) @@ -108,8 +106,8 @@ func (cv *Canvas) cutIntersections(path []pathPoint) []pathPoint { from, to int j int b bool - ratio float32 - point lm.Vec2 + ratio float64 + point vec } var cutBuf [50]cut