package ggfx import "sort" import "image" func (this Image[T]) FillPolygon (fill []T, points ...image.Point) { this.assertWidth(fill) if len(points) < 3 { 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.Rect.Intersect(area) if area.Empty() { return } // 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 { fy := float64(y) fPointX := float64(point.X) fPointY := float64(point.Y) fPrevX := float64(prevPoint.X) fPrevY := float64(prevPoint.Y) addboundary := (fPointY < fy && fPrevY >= fy) || (fPrevY < fy && fPointY >= fy) if addboundary { boundaries[boundaryCount] = int ( fPointX + (fy - fPointY) / (fPrevY - fPointY) * (fPrevX - fPointX)) boundaryCount ++ } prevPoint = point } // sort boundary list cutBoundaries := boundaries[:boundaryCount] sort.Ints(cutBoundaries) // fill pixels between boundary pairs min := area.Min.X max := area.Max.X for index := 0; index < len(cutBoundaries); index += 2 { left := cutBoundaries[index] right := cutBoundaries[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 := this.PixOffset(x, y) + 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 } } func (this Image[T]) PolyLine (stroke []T, weight int, points ...image.Point) { if len(points) < 2 { return } prevPoint := points[0] for _, point := range points[1:] { this.Line(stroke, weight, prevPoint, point) prevPoint = point } }