package gpu import ( "github.com/p9c/p9/pkg/gel/gio/f32" "github.com/p9c/p9/pkg/gel/gio/internal/stroke" ) type quadSplitter struct { bounds f32.Rectangle contour uint32 d *drawOps } func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) { // NW. encodeVertex(data, meta, -1, 1, from, ctrl, to) // NE. encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to) // SW. encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to) // SE. encodeVertex(data[vertStride*3:], meta, 1, -1, from, ctrl, to) } func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) { var corner float32 if cornerx == 1 { corner += .5 } if cornery == 1 { corner += .25 } v := vertex{ Corner: corner, FromX: from.X, FromY: from.Y, CtrlX: ctrl.X, CtrlY: ctrl.Y, ToX: to.X, ToY: to.Y, } v.encode(data, meta) } func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) { data := qs.d.writeVertCache(vertStride * 4) encodeQuadTo(data, qs.contour, from, ctrl, to) } func (qs *quadSplitter) splitAndEncode(quad stroke.QuadSegment) { cbnd := f32.Rectangle{ Min: quad.From, Max: quad.To, }.Canon() from, ctrl, to := quad.From, quad.Ctrl, quad.To // If the curve contain areas where a vertical line // intersects it twice, split the curve in two x monotone // lower and upper curves. The stencil fragment program // expects only one intersection per curve. // Find the t where the derivative in x is 0. v0 := ctrl.Sub(from) v1 := to.Sub(ctrl) d := v0.X - v1.X // t = v0 / d. Split if t is in ]0;1[. if v0.X > 0 && d > v0.X || v0.X < 0 && d < v0.X { t := v0.X / d ctrl0 := from.Mul(1 - t).Add(ctrl.Mul(t)) ctrl1 := ctrl.Mul(1 - t).Add(to.Mul(t)) mid := ctrl0.Mul(1 - t).Add(ctrl1.Mul(t)) qs.encodeQuadTo(from, ctrl0, mid) qs.encodeQuadTo(mid, ctrl1, to) if mid.X > cbnd.Max.X { cbnd.Max.X = mid.X } if mid.X < cbnd.Min.X { cbnd.Min.X = mid.X } } else { qs.encodeQuadTo(from, ctrl, to) } // Find the y extremum, if any. d = v0.Y - v1.Y if v0.Y > 0 && d > v0.Y || v0.Y < 0 && d < v0.Y { t := v0.Y / d y := (1-t)*(1-t)*from.Y + 2*(1-t)*t*ctrl.Y + t*t*to.Y if y > cbnd.Max.Y { cbnd.Max.Y = y } if y < cbnd.Min.Y { cbnd.Min.Y = y } } qs.bounds = qs.bounds.Union(cbnd) }