mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 03:38:55 +01:00
packing: Add Extend
This commit is contained in:
parent
dbe5c9c0ab
commit
c37dd9d961
@ -15,17 +15,23 @@
|
|||||||
// Package packing offers a packing algorithm in 2D space.
|
// Package packing offers a packing algorithm in 2D space.
|
||||||
package packing
|
package packing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minSize = 1
|
minSize = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
root *Node
|
root *Node
|
||||||
|
size int
|
||||||
maxSize int
|
maxSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPage(maxSize int) *Page {
|
func NewPage(initSize int, maxSize int) *Page {
|
||||||
return &Page{
|
return &Page{
|
||||||
|
size: initSize,
|
||||||
maxSize: maxSize,
|
maxSize: maxSize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,14 +141,18 @@ func (p *Page) alloc(n *Node, width, height int) *Node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Page) Size() int {
|
||||||
|
return p.size
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Page) Alloc(width, height int) *Node {
|
func (p *Page) Alloc(width, height int) *Node {
|
||||||
if width <= 0 || height <= 0 {
|
if width <= 0 || height <= 0 {
|
||||||
panic("bsp: width and height must > 0")
|
panic("bsp: width and height must > 0")
|
||||||
}
|
}
|
||||||
if p.root == nil {
|
if p.root == nil {
|
||||||
p.root = &Node{
|
p.root = &Node{
|
||||||
width: p.maxSize,
|
width: p.size,
|
||||||
height: p.maxSize,
|
height: p.size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if width < minSize {
|
if width < minSize {
|
||||||
@ -172,3 +182,89 @@ func (p *Page) Free(node *Node) {
|
|||||||
p.Free(node.parent)
|
p.Free(node.parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func walk(n *Node, f func(n *Node) error) error {
|
||||||
|
if err := f(n); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n.child0 != nil {
|
||||||
|
if err := walk(n.child0, f); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n.child1 != nil {
|
||||||
|
if err := walk(n.child1, f); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Page) Extend() bool {
|
||||||
|
if p.size >= p.maxSize {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
newSize := p.size * 2
|
||||||
|
edgeNodes := []*Node{}
|
||||||
|
abort := errors.New("abort")
|
||||||
|
aborted := false
|
||||||
|
_ = walk(p.root, func(n *Node) error {
|
||||||
|
if n.x+n.width < p.size && n.y+n.height < p.size {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n.used {
|
||||||
|
aborted = true
|
||||||
|
return abort
|
||||||
|
}
|
||||||
|
edgeNodes = append(edgeNodes, n)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if aborted {
|
||||||
|
leftUpper := p.root
|
||||||
|
leftLower := &Node{
|
||||||
|
x: 0,
|
||||||
|
y: p.size,
|
||||||
|
width: p.size,
|
||||||
|
height: newSize - p.size,
|
||||||
|
}
|
||||||
|
left := &Node{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: p.size,
|
||||||
|
height: p.size,
|
||||||
|
child0: leftUpper,
|
||||||
|
child1: leftLower,
|
||||||
|
}
|
||||||
|
leftUpper.parent = left
|
||||||
|
leftLower.parent = left
|
||||||
|
|
||||||
|
right := &Node{
|
||||||
|
x: p.size,
|
||||||
|
y: 0,
|
||||||
|
width: newSize - p.size,
|
||||||
|
height: newSize,
|
||||||
|
}
|
||||||
|
p.root = &Node{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: newSize,
|
||||||
|
height: newSize,
|
||||||
|
child0: left,
|
||||||
|
child1: right,
|
||||||
|
}
|
||||||
|
left.parent = p.root
|
||||||
|
right.parent = p.root
|
||||||
|
} else {
|
||||||
|
for _, n := range edgeNodes {
|
||||||
|
if n.x+n.width == p.size {
|
||||||
|
n.width += newSize - p.size
|
||||||
|
}
|
||||||
|
if n.y+n.height == p.size {
|
||||||
|
n.height += newSize - p.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.size = newSize
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -211,7 +211,7 @@ func TestPage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
p := NewPage(1024)
|
p := NewPage(1024, 1024)
|
||||||
nodes := []*Node{}
|
nodes := []*Node{}
|
||||||
nnodes := 0
|
nnodes := 0
|
||||||
for i, in := range c.In {
|
for i, in := range c.In {
|
||||||
@ -252,3 +252,46 @@ func TestPage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExtend(t *testing.T) {
|
||||||
|
p := NewPage(1024, 4096)
|
||||||
|
s := p.Size()
|
||||||
|
p.Alloc(s/2, s/2)
|
||||||
|
p.Extend()
|
||||||
|
if p.Size() != s*2 {
|
||||||
|
t.Errorf("p.Size(): got: %d, want: %d", p.Size(), s*2)
|
||||||
|
}
|
||||||
|
if p.Alloc(s*3/2, s*2) == nil {
|
||||||
|
t.Errorf("p.Alloc failed: width: %d, height: %d", s*3/2, s*2)
|
||||||
|
}
|
||||||
|
if p.Alloc(s/2, s*3/2) == nil {
|
||||||
|
t.Errorf("p.Alloc failed: width: %d, height: %d", s/2, s*3/2)
|
||||||
|
}
|
||||||
|
if p.Alloc(1, 1) != nil {
|
||||||
|
t.Errorf("p.Alloc must fail: width: %d, height: %d", 1, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtend2(t *testing.T) {
|
||||||
|
p := NewPage(1024, 4096)
|
||||||
|
s := p.Size()
|
||||||
|
p.Alloc(s/2, s/2)
|
||||||
|
n1 := p.Alloc(s/2, s/2)
|
||||||
|
n2 := p.Alloc(s/2, s/2)
|
||||||
|
p.Alloc(s/2, s/2)
|
||||||
|
p.Free(n1)
|
||||||
|
p.Free(n2)
|
||||||
|
p.Extend()
|
||||||
|
if p.Size() != s*2 {
|
||||||
|
t.Errorf("p.Size(): got: %d, want: %d", p.Size(), s*2)
|
||||||
|
}
|
||||||
|
if p.Alloc(s, s*2) == nil {
|
||||||
|
t.Errorf("p.Alloc failed: width: %d, height: %d", s, s*2)
|
||||||
|
}
|
||||||
|
if p.Alloc(s, s) == nil {
|
||||||
|
t.Errorf("p.Alloc failed: width: %d, height: %d", s, s)
|
||||||
|
}
|
||||||
|
if p.Alloc(s, s) != nil {
|
||||||
|
t.Errorf("p.Alloc must fail: width: %d, height: %d", s, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -201,7 +201,7 @@ func NewImage(width, height int) *Image {
|
|||||||
}
|
}
|
||||||
s := &backend{
|
s := &backend{
|
||||||
restorable: restorable.NewImage(maxSize, maxSize, false),
|
restorable: restorable.NewImage(maxSize, maxSize, false),
|
||||||
page: packing.NewPage(maxSize),
|
page: packing.NewPage(maxSize, maxSize), // TODO: Utilize 'Extend' page.
|
||||||
}
|
}
|
||||||
theBackends = append(theBackends, s)
|
theBackends = append(theBackends, s)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user