85 lines
2.2 KiB
Go
85 lines
2.2 KiB
Go
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
|
|
}
|
|
}
|