diff --git a/gpu/pack.go b/gpu/pack.go index c4dbaadb..abf738c7 100644 --- a/gpu/pack.go +++ b/gpu/pack.go @@ -46,40 +46,54 @@ func (p *packer) newPage() { } func (p *packer) tryAdd(s image.Point) (placement, bool) { - // Go backwards to prioritize smaller spaces first. - for i := len(p.spaces) - 1; i >= 0; i-- { - space := p.spaces[i] + var ( + bestIdx = -1 + bestSpace image.Rectangle + bestSize = image.Pt(p.maxDim, p.maxDim) + ) + // Go backwards to prioritize smaller spaces. + for i, space := range p.spaces { rightSpace := space.Dx() - s.X bottomSpace := space.Dy() - s.Y - if rightSpace >= 0 && bottomSpace >= 0 { - // Remove space. - p.spaces[i] = p.spaces[len(p.spaces)-1] - p.spaces = p.spaces[:len(p.spaces)-1] - // Put s in the top left corner and add the (at most) - // two smaller spaces. - pos := space.Min - if bottomSpace > 0 { - p.spaces = append(p.spaces, image.Rectangle{ - Min: image.Point{X: pos.X, Y: pos.Y + s.Y}, - Max: image.Point{X: space.Max.X, Y: space.Max.Y}, - }) - } - if rightSpace > 0 { - p.spaces = append(p.spaces, image.Rectangle{ - Min: image.Point{X: pos.X + s.X, Y: pos.Y}, - Max: image.Point{X: space.Max.X, Y: pos.Y + s.Y}, - }) - } - idx := len(p.sizes) - 1 - size := &p.sizes[idx] - if x := pos.X + s.X; x > size.X { - size.X = x - } - if y := pos.Y + s.Y; y > size.Y { - size.Y = y - } - return placement{Idx: idx, Pos: pos}, true + if rightSpace < 0 || bottomSpace < 0 { + continue + } + idx := len(p.sizes) - 1 + size := p.sizes[idx] + if x := space.Min.X + s.X; x > size.X { + size.X = x + } + if y := space.Min.Y + s.Y; y > size.Y { + size.Y = y + } + if size.X*size.Y < bestSize.X*bestSize.Y { + bestIdx = i + bestSpace = space + bestSize = size } } - return placement{}, false + if bestIdx == -1 { + return placement{}, false + } + // Remove space. + p.spaces[bestIdx] = p.spaces[len(p.spaces)-1] + p.spaces = p.spaces[:len(p.spaces)-1] + // Put s in the top left corner and add the (at most) + // two smaller spaces. + pos := bestSpace.Min + if rem := bestSpace.Dy() - s.Y; rem > 0 { + p.spaces = append(p.spaces, image.Rectangle{ + Min: image.Point{X: pos.X, Y: pos.Y + s.Y}, + Max: image.Point{X: bestSpace.Max.X, Y: bestSpace.Max.Y}, + }) + } + if rem := bestSpace.Dx() - s.X; rem > 0 { + p.spaces = append(p.spaces, image.Rectangle{ + Min: image.Point{X: pos.X + s.X, Y: pos.Y}, + Max: image.Point{X: bestSpace.Max.X, Y: pos.Y + s.Y}, + }) + } + idx := len(p.sizes) - 1 + p.sizes[idx] = bestSize + return placement{Idx: idx, Pos: pos}, true }