github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/gpu/clip.go (about) 1 package gpu 2 3 import ( 4 "github.com/cybriq/giocore/f32" 5 "github.com/cybriq/giocore/internal/stroke" 6 ) 7 8 type quadSplitter struct { 9 bounds f32.Rectangle 10 contour uint32 11 d *drawOps 12 } 13 14 func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) { 15 // NW. 16 encodeVertex(data, meta, -1, 1, from, ctrl, to) 17 // NE. 18 encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to) 19 // SW. 20 encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to) 21 // SE. 22 encodeVertex(data[vertStride*3:], meta, 1, -1, from, ctrl, to) 23 } 24 25 func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) { 26 var corner float32 27 if cornerx == 1 { 28 corner += .5 29 } 30 if cornery == 1 { 31 corner += .25 32 } 33 v := vertex{ 34 Corner: corner, 35 FromX: from.X, 36 FromY: from.Y, 37 CtrlX: ctrl.X, 38 CtrlY: ctrl.Y, 39 ToX: to.X, 40 ToY: to.Y, 41 } 42 v.encode(data, meta) 43 } 44 45 func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) { 46 data := qs.d.writeVertCache(vertStride * 4) 47 encodeQuadTo(data, qs.contour, from, ctrl, to) 48 } 49 50 func (qs *quadSplitter) splitAndEncode(quad stroke.QuadSegment) { 51 cbnd := f32.Rectangle{ 52 Min: quad.From, 53 Max: quad.To, 54 }.Canon() 55 from, ctrl, to := quad.From, quad.Ctrl, quad.To 56 57 // If the curve contain areas where a vertical line 58 // intersects it twice, split the curve in two x monotone 59 // lower and upper curves. The stencil fragment program 60 // expects only one intersection per curve. 61 62 // Find the t where the derivative in x is 0. 63 v0 := ctrl.Sub(from) 64 v1 := to.Sub(ctrl) 65 d := v0.X - v1.X 66 // t = v0 / d. Split if t is in ]0;1[. 67 if v0.X > 0 && d > v0.X || v0.X < 0 && d < v0.X { 68 t := v0.X / d 69 ctrl0 := from.Mul(1 - t).Add(ctrl.Mul(t)) 70 ctrl1 := ctrl.Mul(1 - t).Add(to.Mul(t)) 71 mid := ctrl0.Mul(1 - t).Add(ctrl1.Mul(t)) 72 qs.encodeQuadTo(from, ctrl0, mid) 73 qs.encodeQuadTo(mid, ctrl1, to) 74 if mid.X > cbnd.Max.X { 75 cbnd.Max.X = mid.X 76 } 77 if mid.X < cbnd.Min.X { 78 cbnd.Min.X = mid.X 79 } 80 } else { 81 qs.encodeQuadTo(from, ctrl, to) 82 } 83 // Find the y extremum, if any. 84 d = v0.Y - v1.Y 85 if v0.Y > 0 && d > v0.Y || v0.Y < 0 && d < v0.Y { 86 t := v0.Y / d 87 y := (1-t)*(1-t)*from.Y + 2*(1-t)*t*ctrl.Y + t*t*to.Y 88 if y > cbnd.Max.Y { 89 cbnd.Max.Y = y 90 } 91 if y < cbnd.Min.Y { 92 cbnd.Min.Y = y 93 } 94 } 95 96 qs.bounds = unionRect(qs.bounds, cbnd) 97 } 98 99 // Union is like f32.Rectangle.Union but ignores empty rectangles. 100 func unionRect(r, s f32.Rectangle) f32.Rectangle { 101 if r.Min.X > s.Min.X { 102 r.Min.X = s.Min.X 103 } 104 if r.Min.Y > s.Min.Y { 105 r.Min.Y = s.Min.Y 106 } 107 if r.Max.X < s.Max.X { 108 r.Max.X = s.Max.X 109 } 110 if r.Max.Y < s.Max.Y { 111 r.Max.Y = s.Max.Y 112 } 113 return r 114 }