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  }