github.com/Kintar/etxt@v0.0.0-20221224033739-2fc69f000137/emask/outline_segment.go (about) 1 package emask 2 3 type outlineSegment struct { 4 // starting position 5 ox float64 6 oy float64 7 fx float64 8 fy float64 9 10 // coefficients for line equations in the form Ax + By + C = 0 11 a float64 // a1 and a2 are the same, lines are parallel 12 b float64 // b1 and b2 are the same, lines are parallel 13 c1 float64 14 c2 float64 15 } 16 17 func (self *outlineSegment) Fill(buffer *buffer, prevSegment, nextSegment *outlineSegment) { //, maxOutDist float64) { 18 oxOut, oyOut, oxIn, oyIn, oxx, oxy := prevSegment.intersect(self) 19 fxOut, fyOut, fxIn, fyIn, fxx, fxy := self.intersect(nextSegment) 20 21 // TODO: would still need further triangle clipping for thickness 22 // shorter than segment len cases 23 _, _ = oxx, oxy 24 _, _ = fxx, fxy 25 26 // if outer points get too far away, clamp them 27 // TODO: this clamping must also be mirrored on CutHead and CutTail 28 // once I confirm it's working... 29 // TODO: the mirroring is unlikely to be ok here. 30 // TODO: if this starts getting more complex, consider reimplementing 31 // CutHead and CutTail as Fills with synthetic outlineSegments. 32 // TODO: I should have a clearer idea of the whole, complete strategy 33 // before I go into any other bs 34 35 // maxOutDist2 := maxOutDist*maxOutDist 36 // oDist2 := dist2(self.ox, self.oy, oxOut, oyOut) 37 // if oDist2 > maxOutDist2 { 38 // factor := math.Sqrt(oDist2)/maxOutDist 39 // oxOut, oyOut = lerp(self.ox, self.oy, oxOut, oyOut, factor) 40 // buffer.FillConvexQuad(oxOut, oyOut, oxOut, oyOut, oxIn, oyIn, oxx, oxy) 41 // oxOut, oyOut = oxx, oxy 42 // } 43 // fDist2 := dist2(self.fx, self.fy, fxOut, fyOut) 44 // if fDist2 > maxOutDist2 { 45 // factor := math.Sqrt(fDist2)/maxOutDist 46 // fxOut, fyOut = lerp(self.fx, self.fy, fxOut, fyOut, factor) 47 // buffer.FillConvexQuad(fxOut, fyOut, fxOut, fyOut, fxIn, fyIn, fxx, fxy) 48 // fxOut, fyOut = fxx, fxy 49 // } 50 51 // main quad fill 52 buffer.FillConvexQuad(oxOut, oyOut, oxIn, oyIn, fxOut, fyOut, fxIn, fyIn) 53 } 54 55 func (self *outlineSegment) CutHead(buffer *buffer, prevSegment *outlineSegment) { //, maxOutDist float64) { 56 oxOut, oyOut, oxIn, oyIn, oxx, oxy := prevSegment.intersect(self) 57 _, _ = oxx, oxy 58 // maxOutDist2 := maxOutDist*maxOutDist 59 // oDist2 := dist2(self.ox, self.oy, oxOut, oyOut) 60 // if oDist2 > maxOutDist2 { // clamping for the outer point 61 // factor := math.Sqrt(oDist2)/maxOutDist 62 // oxOut, oyOut = lerp(self.ox, self.oy, oxOut, oyOut, factor) 63 // buffer.FillConvexQuad(oxOut, oyOut, oxOut, oyOut, oxIn, oyIn, oxx, oxy) 64 // oxOut, oyOut = oxx, oxy 65 // } 66 67 a, b, oc := perpendicularABC(self.a, self.b, self.fx, self.fy) 68 xdiv := a*self.b - self.a*b 69 ox1, oy1 := shortCramer(xdiv, a, b, oc, self.a, self.b, self.c1) 70 ox2, oy2 := shortCramer(xdiv, a, b, oc, self.a, self.b, self.c2) 71 buffer.FillConvexQuad(oxOut, oyOut, oxIn, oyIn, ox1, oy1, ox2, oy2) 72 } 73 74 func (self *outlineSegment) CutTail(buffer *buffer, nextSegment *outlineSegment) { //, maxOutDist float64) { 75 a, b, oc := perpendicularABC(self.a, self.b, self.ox, self.oy) 76 xdiv := a*self.b - self.a*b 77 ox1, oy1 := shortCramer(xdiv, a, b, oc, self.a, self.b, self.c1) 78 ox2, oy2 := shortCramer(xdiv, a, b, oc, self.a, self.b, self.c2) 79 fxOut, fyOut, fxIn, fyIn, fxx, fxy := self.intersect(nextSegment) 80 81 _, _ = fxx, fxy 82 // maxOutDist2 := maxOutDist*maxOutDist 83 // fDist2 := dist2(self.fx, self.fy, fxOut, fyOut) 84 // if fDist2 > maxOutDist2 { // clamping for the outer point 85 // factor := math.Sqrt(fDist2)/maxOutDist 86 // fxOut, fyOut = lerp(self.fx, self.fy, fxOut, fyOut, factor) 87 // buffer.FillConvexQuad(fxOut, fyOut, fxOut, fyOut, fxIn, fyIn, fxx, fxy) 88 // fxOut, fyOut = fxx, fxy 89 // } 90 91 buffer.FillConvexQuad(ox1, oy1, ox2, oy2, fxOut, fyOut, fxIn, fyIn) 92 } 93 94 func (self *outlineSegment) Cut(buffer *buffer) { 95 a, b, oc := perpendicularABC(self.a, self.b, self.ox, self.oy) 96 xdiv := a*self.b - self.a*b 97 ox1, oy1 := shortCramer(xdiv, a, b, oc, self.a, self.b, self.c1) 98 ox2, oy2 := shortCramer(xdiv, a, b, oc, self.a, self.b, self.c2) 99 fc := -(a*self.fx + b*self.fy) // ax + by + c = 0 100 fx1, fy1 := shortCramer(xdiv, a, b, fc, self.a, self.b, self.c1) 101 fx2, fy2 := shortCramer(xdiv, a, b, fc, self.a, self.b, self.c2) 102 buffer.FillConvexQuad(ox1, oy1, ox2, oy2, fx1, fy1, fx2, fy2) 103 } 104 105 // Intersects two outline segments and determines the inner and outer 106 // points at which they intersect. The returned values are outer vertex 107 // x and outer vertex y, inner vertex x and inner vertex y, and the 108 // other intersecting vertex on self before outer x/y. 109 func (self *outlineSegment) intersect(other *outlineSegment) (float64, float64, float64, float64, float64, float64) { 110 // find 4 intersection points 111 xdiv := self.a*other.b - other.a*self.b 112 x11, y11 := shortCramer(xdiv, self.a, self.b, self.c1, other.a, other.b, other.c1) 113 x12, y12 := shortCramer(xdiv, self.a, self.b, self.c1, other.a, other.b, other.c2) 114 x21, y21 := shortCramer(xdiv, self.a, self.b, self.c2, other.a, other.b, other.c1) 115 x22, y22 := shortCramer(xdiv, self.a, self.b, self.c2, other.a, other.b, other.c2) 116 117 // determine which point among the 4 intersection points falls 118 // at each side of the line equations to determine inner and 119 // outer vertices 120 ac, bc := -(self.a*self.fx + self.b*self.fy), -(other.a*self.fx + other.b*self.fy) 121 boa := (self.b*other.fy > -self.a*other.fx-ac) 122 bob := (other.b*self.oy > -other.a*self.ox-bc) 123 var inX, inY, outX, outY float64 124 var auxX, auxY float64 125 for _, pt := range []struct{ x, y float64 }{{x11, y11}, {x12, y12}, {x21, y21}, {x22, y22}} { 126 jaCmp := (self.b*pt.y > -self.a*pt.x-ac) 127 jbCmp := (other.b*pt.y > -other.a*pt.x-bc) 128 if (boa == jaCmp) == (bob == jbCmp) { 129 if boa == jaCmp { 130 inX, inY = pt.x, pt.y 131 } else { 132 outX, outY = pt.x, pt.y 133 } 134 } else if boa != jaCmp { 135 auxX, auxY = pt.x, pt.y 136 } 137 } 138 return outX, outY, inX, inY, auxX, auxY 139 }