98 lines
2.6 KiB
Go
98 lines
2.6 KiB
Go
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
|
|
}
|
|
}
|