package ggfx import "sort" import "image" func (this Image[T]) FillPolygon (fill []T, points ...image.Point) { this.assertWidth(fill) if len(points) < 2 { return } // figure out the bounds of the polygon so we don't test empty space var area image.Rectangle area.Min = points[0] area.Max = points[0] for _, point := range points[1:] { if point.X < area.Min.X { area.Min.X = point.X } if point.Y < area.Min.Y { area.Min.Y = point.Y } if point.X < area.Max.X { area.Max.X = point.X } if point.Y < area.Max.Y { area.Max.Y = point.Y } } area = this.Bounds.Intersect(area) // this algorithm is adapted from: // https://www.alienryderflex.com/polygon_fill/ // (if you write C like that i will disassemble you) boundaries := make([]int, len(points)) for y := area.Min.Y; y < area.Max.Y; y ++ { // build boundary list boundaryCount := 0 prevPoint := points[len(points) - 1] for _, point := range points { addboundary := point.Y < y && prevPoint.Y >= y || prevPoint.Y < y && point.Y >= y if addboundary { boundaries[boundaryCount] = point.X + (y - point.Y) / (prevPoint.Y - point.Y) * (prevPoint.X - point.X) boundaryCount ++ } prevPoint = point } // sort boundary list boundaries = boundaries[:boundaryCount] sort.Ints(boundaries) // fill pixels between boundary pairs min := area.Min.X max := area.Max.X for index := 0; index < len(boundaries); index ++ { left := boundaries[index] right := boundaries[index + 1] // stop if we have exited the polygon if left >= max { break } // begin filling if we are within the polygon if right > min { // constrain boundaries to image size if left < min { left = min } if right > max { right = max } // fill for each pixel part for offset, part := range fill { for x := left; x < right; x ++ { index := (y * this.Stride + x) * this.Width + offset this.Pix[index] = part } } } } } } func (this Image[T]) StrokePolygon (stroke []T, weight int, points ...image.Point) { prevPoint := points[len(points) - 1] for _, point := range points { this.Line(stroke, weight, prevPoint, point) prevPoint = point } }