From 46c68ca5ade4685f1948f13be57713a0789e6537 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Mon, 5 Feb 2018 11:10:50 +0100 Subject: [PATCH] added dashed lines --- canvas.go | 26 +++++++++++++++-- paths.go | 86 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 92 insertions(+), 20 deletions(-) diff --git a/canvas.go b/canvas.go index c29765c..d4e382f 100644 --- a/canvas.go +++ b/canvas.go @@ -14,8 +14,9 @@ type Canvas struct { x, y, w, h int fx, fy, fw, fh float32 - path []pathPoint - text struct { + polyPath []pathPoint + linePath []pathPoint + text struct { target *image.RGBA tex uint32 } @@ -44,6 +45,10 @@ type drawState struct { fontSize float32 lineJoin lineJoin lineEnd lineEnd + + lineDash []float32 + lineDashPoint int + lineDashOffset float32 /* The current transformation matrix. The current clipping region. @@ -231,6 +236,23 @@ func (cv *Canvas) SetLineEnd(end lineEnd) { cv.state.lineEnd = end } +// SetLineDash sets the line dash style +func (cv *Canvas) SetLineDash(dash []float32) { + l := len(dash) + if l%2 == 0 { + d2 := make([]float32, l) + copy(d2, dash) + cv.state.lineDash = d2 + } else { + d2 := make([]float32, l*2) + copy(d2[:l], dash) + copy(d2[l:], dash) + cv.state.lineDash = d2 + } + cv.state.lineDashPoint = 0 + cv.state.lineDashOffset = 0 +} + // Save saves the current draw state to a stack func (cv *Canvas) Save() { cv.stateStack = append(cv.stateStack, cv.state) diff --git a/paths.go b/paths.go index 2f8ee06..e0b4f86 100644 --- a/paths.go +++ b/paths.go @@ -9,24 +9,67 @@ import ( ) func (cv *Canvas) BeginPath() { - if cv.path == nil { - cv.path = make([]pathPoint, 0, 100) + if cv.linePath == nil { + cv.linePath = make([]pathPoint, 0, 100) } - cv.path = cv.path[:0] + if cv.polyPath == nil { + cv.polyPath = make([]pathPoint, 0, 100) + } + cv.linePath = cv.linePath[:0] + cv.polyPath = cv.polyPath[:0] } func (cv *Canvas) MoveTo(x, y float32) { - cv.path = append(cv.path, pathPoint{pos: lm.Vec2{x, y}, move: true}) + cv.linePath = append(cv.linePath, pathPoint{pos: lm.Vec2{x, y}, move: true}) + cv.polyPath = append(cv.polyPath, pathPoint{pos: lm.Vec2{x, y}, move: true}) } func (cv *Canvas) LineTo(x, y float32) { - if len(cv.path) == 0 { + if len(cv.linePath) == 0 { cv.MoveTo(x, y) return } - cv.path[len(cv.path)-1].next = lm.Vec2{x, y} - cv.path[len(cv.path)-1].attach = true - cv.path = append(cv.path, pathPoint{pos: lm.Vec2{x, y}, move: false}) + 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() + prev := cv.state.lineDashOffset + for vl > 0 { + draw := cv.state.lineDashPoint%2 == 0 + p := tp + cv.state.lineDashOffset += vl + 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)) + vl -= dl + cv.state.lineDashPoint++ + cv.state.lineDashPoint %= len(cv.state.lineDash) + prev = 0 + } else { + vl = 0 + } + + if draw { + cv.linePath[len(cv.linePath)-1].next = p + cv.linePath[len(cv.linePath)-1].attach = true + cv.linePath = append(cv.linePath, pathPoint{pos: p, move: false}) + } else { + cv.linePath = append(cv.linePath, pathPoint{pos: p, move: true}) + } + + lp = p + v = tp.Sub(lp) + } + } else { + cv.linePath[len(cv.linePath)-1].next = lm.Vec2{x, y} + cv.linePath[len(cv.linePath)-1].attach = true + cv.linePath = append(cv.linePath, pathPoint{pos: lm.Vec2{x, y}, move: false}) + } + cv.polyPath[len(cv.polyPath)-1].next = lm.Vec2{x, y} + cv.polyPath[len(cv.polyPath)-1].attach = true + cv.polyPath = append(cv.polyPath, pathPoint{pos: lm.Vec2{x, y}, move: false}) } func (cv *Canvas) Arc(x, y, radius, startAngle, endAngle float32, anticlockwise bool) { @@ -53,16 +96,23 @@ func (cv *Canvas) Arc(x, y, radius, startAngle, endAngle float32, anticlockwise } func (cv *Canvas) ClosePath() { - if len(cv.path) < 2 { + if len(cv.linePath) < 2 { return } - cv.path[len(cv.path)-1].next = cv.path[0].pos - cv.path[len(cv.path)-1].attach = true - cv.path = append(cv.path, pathPoint{pos: cv.path[0].pos, move: false, next: cv.path[1].pos, attach: true}) + if len(cv.state.lineDash) > 0 { + cv.LineTo(cv.linePath[0].pos[0], cv.linePath[0].pos[1]) + return + } + cv.linePath[len(cv.linePath)-1].next = cv.linePath[0].pos + cv.linePath[len(cv.linePath)-1].attach = true + cv.linePath = append(cv.linePath, pathPoint{pos: cv.linePath[0].pos, move: false, next: cv.linePath[1].pos, attach: true}) + cv.polyPath[len(cv.polyPath)-1].next = cv.polyPath[0].pos + cv.polyPath[len(cv.polyPath)-1].attach = true + cv.polyPath = append(cv.polyPath, pathPoint{pos: cv.polyPath[0].pos, move: false, next: cv.polyPath[1].pos, attach: true}) } func (cv *Canvas) Stroke() { - if len(cv.path) == 0 { + if len(cv.linePath) == 0 { return } @@ -87,7 +137,7 @@ func (cv *Canvas) Stroke() { start := true var p0 lm.Vec2 - for _, p := range cv.path { + for _, p := range cv.linePath { if p.move { p0 = p.pos start = true @@ -238,13 +288,13 @@ func lineIntersection(a0, a1, b0, b1 lm.Vec2) lm.Vec2 { func (cv *Canvas) Fill() { lastMove := 0 - for i, p := range cv.path { + for i, p := range cv.polyPath { if p.move { lastMove = i } } - path := cv.path[lastMove:] + path := cv.polyPath[lastMove:] if len(path) < 3 { return @@ -278,7 +328,7 @@ func (cv *Canvas) Fill() { } func (cv *Canvas) Clip() { - if len(cv.path) < 3 { + if len(cv.polyPath) < 3 { return } @@ -301,7 +351,7 @@ func (cv *Canvas) Clip() { tris := buf[:0] tris = append(tris, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1) - tris = triangulatePath(cv.path, tris) + tris = triangulatePath(cv.polyPath, tris) total := len(tris) for i := 12; i < total; i += 2 { x, y := tris[i], tris[i+1]