From 8270d11024b6204edbf3c3c14c11216f79f769e2 Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Mon, 3 Oct 2016 11:37:16 -0700 Subject: [PATCH] Z optimized, fixed #61 --- controller/collection/collection.go | 28 ++- controller/crud.go | 14 +- controller/fence.go | 3 +- controller/search.go | 9 +- controller/token.go | 13 + geojson/bbox.go | 4 +- index/index.go | 36 +-- index/index_test.go | 18 +- index/rtree/rtree.go | 32 +-- index/rtree/rtree_test.go | 24 +- index/rtree/rtreed.go | 352 ++++++++++++++-------------- 11 files changed, 280 insertions(+), 253 deletions(-) diff --git a/controller/collection/collection.go b/controller/collection/collection.go index 1b80c4f5..b6a18d62 100644 --- a/controller/collection/collection.go +++ b/controller/collection/collection.go @@ -36,13 +36,13 @@ func (i *itemT) Less(item btree.Item, ctx interface{}) bool { } } -func (i *itemT) Rect() (minX, minY, maxX, maxY float64) { +func (i *itemT) Rect() (minX, minY, minZ, maxX, maxY, maxZ float64) { bbox := i.object.CalculatedBBox() - return bbox.Min.X, bbox.Min.Y, bbox.Max.X, bbox.Max.Y + return bbox.Min.X, bbox.Min.Y, bbox.Min.Z, bbox.Max.X, bbox.Max.Y, bbox.Max.Z } -func (i *itemT) Point() (x, y float64) { - x, y, _, _ = i.Rect() +func (i *itemT) Point() (x, y, z float64) { + x, y, z, _, _, _ = i.Rect() return } @@ -87,7 +87,7 @@ func (c *Collection) TotalWeight() int { } // Bounds returns the bounds of all the items in the collection. -func (c *Collection) Bounds() (minX, minY, maxX, maxY float64) { +func (c *Collection) Bounds() (minX, minY, minZ, maxX, maxY, maxZ float64) { return c.index.Bounds() } @@ -333,7 +333,7 @@ func (c *Collection) ScanGreaterOrEqual(id string, cursor uint64, desc bool, } func (c *Collection) geoSearch(cursor uint64, bbox geojson.BBox, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) { - return c.index.Search(cursor, bbox.Min.Y, bbox.Min.X, bbox.Max.Y, bbox.Max.X, func(item index.Item) bool { + return c.index.Search(cursor, bbox.Min.Y, bbox.Min.X, bbox.Max.Y, bbox.Max.X, bbox.Min.Z, bbox.Max.Z, func(item index.Item) bool { var iitm *itemT iitm, ok := item.(*itemT) if !ok { @@ -347,12 +347,13 @@ func (c *Collection) geoSearch(cursor uint64, bbox geojson.BBox, iterator func(i } // Nearby returns all object that are nearby a point. -func (c *Collection) Nearby(cursor uint64, sparse uint8, lat, lon, meters float64, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) { +func (c *Collection) Nearby(cursor uint64, sparse uint8, lat, lon, meters, minZ, maxZ float64, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) { center := geojson.Position{X: lon, Y: lat, Z: 0} bbox := geojson.BBoxesFromCenter(lat, lon, meters) bboxes := bbox.Sparse(sparse) if sparse > 0 { for _, bbox := range bboxes { + bbox.Min.Z, bbox.Max.Z = minZ, maxZ c.geoSearch(cursor, bbox, func(id string, obj geojson.Object, fields []float64) bool { if obj.Nearby(center, meters) { if iterator(id, obj, fields) { @@ -364,6 +365,7 @@ func (c *Collection) Nearby(cursor uint64, sparse uint8, lat, lon, meters float6 } return 0 } + bbox.Min.Z, bbox.Max.Z = minZ, maxZ return c.geoSearch(cursor, bbox, func(id string, obj geojson.Object, fields []float64) bool { if obj.Nearby(center, meters) { return iterator(id, obj, fields) @@ -373,12 +375,12 @@ func (c *Collection) Nearby(cursor uint64, sparse uint8, lat, lon, meters float6 } // Within returns all object that are fully contained within an object or bounding box. Set obj to nil in order to use the bounding box. -func (c *Collection) Within(cursor uint64, sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon float64, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) { +func (c *Collection) Within(cursor uint64, sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon, minZ, maxZ float64, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) { var bbox geojson.BBox if obj != nil { bbox = obj.CalculatedBBox() } else { - bbox = geojson.BBox{Min: geojson.Position{X: minLon, Y: minLat, Z: 0}, Max: geojson.Position{X: maxLon, Y: maxLat, Z: 0}} + bbox = geojson.BBox{Min: geojson.Position{X: minLon, Y: minLat, Z: minZ}, Max: geojson.Position{X: maxLon, Y: maxLat, Z: maxZ}} } bboxes := bbox.Sparse(sparse) if sparse > 0 { @@ -421,12 +423,12 @@ func (c *Collection) Within(cursor uint64, sparse uint8, obj geojson.Object, min } // Intersects returns all object that are intersect an object or bounding box. Set obj to nil in order to use the bounding box. -func (c *Collection) Intersects(cursor uint64, sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon float64, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) { +func (c *Collection) Intersects(cursor uint64, sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon, maxZ, minZ float64, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) { var bbox geojson.BBox if obj != nil { bbox = obj.CalculatedBBox() } else { - bbox = geojson.BBox{Min: geojson.Position{X: minLon, Y: minLat, Z: 0}, Max: geojson.Position{X: maxLon, Y: maxLat, Z: 0}} + bbox = geojson.BBox{Min: geojson.Position{X: minLon, Y: minLat, Z: minZ}, Max: geojson.Position{X: maxLon, Y: maxLat, Z: maxZ}} } var bboxes []geojson.BBox if sparse > 0 { @@ -436,8 +438,8 @@ func (c *Collection) Intersects(cursor uint64, sparse uint8, obj geojson.Object, for y := bbox.Min.Y; y < bbox.Max.Y; y += ypart { for x := bbox.Min.X; x < bbox.Max.X; x += xpart { bboxes = append(bboxes, geojson.BBox{ - Min: geojson.Position{X: x, Y: y, Z: 0}, - Max: geojson.Position{X: x + xpart, Y: y + ypart, Z: 0}, + Min: geojson.Position{X: x, Y: y, Z: minZ}, + Max: geojson.Position{X: x + xpart, Y: y + ypart, Z: maxZ}, }) } } diff --git a/controller/crud.go b/controller/crud.go index ed927b88..aecb7fa9 100644 --- a/controller/crud.go +++ b/controller/crud.go @@ -73,19 +73,23 @@ func (c *Controller) cmdBounds(msg *server.Message) (string, error) { if msg.OutputType == server.JSON { buf.WriteString(`{"ok":true`) } - bbox := geojson.New2DBBox(col.Bounds()) + minX, minY, minZ, maxX, maxY, maxZ := col.Bounds() + + bbox := geojson.New2DBBox(minX, minY, maxX, maxY) if msg.OutputType == server.JSON { buf.WriteString(`,"bounds":`) buf.WriteString(bbox.ExternalJSON()) } else { vals = append(vals, resp.ArrayValue([]resp.Value{ resp.ArrayValue([]resp.Value{ - resp.FloatValue(bbox.Min.Y), - resp.FloatValue(bbox.Min.X), + resp.FloatValue(minX), + resp.FloatValue(minY), + resp.FloatValue(minZ), }), resp.ArrayValue([]resp.Value{ - resp.FloatValue(bbox.Max.Y), - resp.FloatValue(bbox.Max.X), + resp.FloatValue(maxX), + resp.FloatValue(maxY), + resp.FloatValue(maxZ), }), })) } diff --git a/controller/fence.go b/controller/fence.go index 86d2d630..16bb48f7 100644 --- a/controller/fence.go +++ b/controller/fence.go @@ -1,6 +1,7 @@ package controller import ( + "math" "strconv" "strings" @@ -196,7 +197,7 @@ func fenceMatchRoam(c *Controller, fence *liveFenceSwitches, tkey, tid string, o return } p := obj.CalculatedPoint() - col.Nearby(0, 0, p.Y, p.X, fence.roam.meters, + col.Nearby(0, 0, p.Y, p.X, fence.roam.meters, math.Inf(-1), math.Inf(+1), func(id string, obj geojson.Object, fields []float64) bool { var match bool if id == tid { diff --git a/controller/search.go b/controller/search.go index 8e8542f6..0a775210 100644 --- a/controller/search.go +++ b/controller/search.go @@ -264,6 +264,8 @@ func (c *Controller) cmdNearby(msg *server.Message) (res string, err error) { if s.fence { return "", s } + + minZ, maxZ := zMinMaxFromWheres(s.wheres) sw, err := c.newScanWriter(wr, msg, s.key, s.output, s.precision, s.glob, false, s.limit, s.wheres, s.nofields) if err != nil { return "", err @@ -273,7 +275,7 @@ func (c *Controller) cmdNearby(msg *server.Message) (res string, err error) { } sw.writeHead() if sw.col != nil { - s.cursor = sw.col.Nearby(s.cursor, s.sparse, s.lat, s.lon, s.meters, func(id string, o geojson.Object, fields []float64) bool { + s.cursor = sw.col.Nearby(s.cursor, s.sparse, s.lat, s.lon, s.meters, minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { return sw.writeObject(id, o, fields, false) }) } @@ -316,14 +318,15 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res wr.WriteString(`{"ok":true`) } sw.writeHead() + minZ, maxZ := zMinMaxFromWheres(s.wheres) if cmd == "within" { - s.cursor = sw.col.Within(s.cursor, s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, + s.cursor = sw.col.Within(s.cursor, s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { return sw.writeObject(id, o, fields, false) }, ) } else if cmd == "intersects" { - s.cursor = sw.col.Intersects(s.cursor, s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, + s.cursor = sw.col.Intersects(s.cursor, s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { return sw.writeObject(id, o, fields, false) }, diff --git a/controller/token.go b/controller/token.go index da7deda1..f24201fa 100644 --- a/controller/token.go +++ b/controller/token.go @@ -117,6 +117,19 @@ func (where whereT) match(value float64) bool { return true } +func zMinMaxFromWheres(wheres []whereT) (minZ, maxZ float64) { + for _, w := range wheres { + if w.field == "z" { + minZ = w.min + maxZ = w.max + return + } + } + minZ = math.Inf(-1) + maxZ = math.Inf(+1) + return +} + type searchScanBaseTokens struct { key string cursor uint64 diff --git a/geojson/bbox.go b/geojson/bbox.go index e14fb0de..133c7254 100644 --- a/geojson/bbox.go +++ b/geojson/bbox.go @@ -163,8 +163,8 @@ func (b BBox) Sparse(amount byte) []BBox { for y := b.Min.Y; y < b.Max.Y; y += ysize { for x := b.Min.X; x < b.Max.X; x += xsize { bboxes = append(bboxes, BBox{ - Min: Position{X: x, Y: y, Z: 0}, - Max: Position{X: x + xsize, Y: y + ysize, Z: 0}, + Min: Position{X: x, Y: y, Z: b.Min.Z}, + Max: Position{X: x + xsize, Y: y + ysize, Z: b.Max.Z}, }) } } diff --git a/index/index.go b/index/index.go index 2b400225..53c5b4d8 100644 --- a/index/index.go +++ b/index/index.go @@ -1,26 +1,30 @@ package index -import "github.com/tidwall/tile38/index/rtree" +import ( + "math" + + "github.com/tidwall/tile38/index/rtree" +) // Item represents an index item. type Item interface { - Point() (x, y float64) - Rect() (minX, minY, maxX, maxY float64) + Point() (x, y, z float64) + Rect() (minX, minY, minZ, maxX, maxY, maxZ float64) } // FlexItem can represent a point or a rectangle type FlexItem struct { - MinX, MinY, MaxX, MaxY float64 + MinX, MinY, MinZ, MaxX, MaxY, MaxZ float64 } // Rect returns the rectangle -func (item *FlexItem) Rect() (minX, minY, maxX, maxY float64) { - return item.MinX, item.MinY, item.MaxX, item.MaxY +func (item *FlexItem) Rect() (minX, minY, minZ, maxX, maxY, maxZ float64) { + return item.MinX, item.MinY, item.MinZ, item.MaxX, item.MaxY, item.MaxZ } // Point returns the point -func (item *FlexItem) Point() (x, y float64) { - return item.MinX, item.MinY +func (item *FlexItem) Point() (x, y, z float64) { + return item.MinX, item.MinY, item.MinZ } // Index is a geospatial index @@ -43,11 +47,11 @@ func New() *Index { // Insert inserts an item into the index func (ix *Index) Insert(item Item) { - minX, minY, maxX, maxY := item.Rect() + minX, minY, minZ, maxX, maxY, maxZ := item.Rect() if minX == maxX && minY == maxY { x, y, normd := normPoint(minY, minX) if normd { - nitem := &rtree.Rect{MinX: x, MinY: y, MaxX: x, MaxY: y} + nitem := &rtree.Rect{MinX: x, MinY: y, MinZ: minZ, MaxX: x, MaxY: y, MaxZ: maxZ} ix.nr[nitem] = item ix.nrr[item] = []*rtree.Rect{nitem} ix.r.Insert(nitem) @@ -60,7 +64,7 @@ func (ix *Index) Insert(item Item) { var nitems []*rtree.Rect for i := range mins { minX, minY, maxX, maxY := mins[i][0], mins[i][1], maxs[i][0], maxs[i][1] - nitem := &rtree.Rect{MinX: minX, MinY: minY, MaxX: maxX, MaxY: maxY} + nitem := &rtree.Rect{MinX: minX, MinY: minY, MinZ: minZ, MaxX: maxX, MaxY: maxY, MaxZ: maxZ} ix.nr[nitem] = item nitems = append(nitems, nitem) ix.r.Insert(nitem) @@ -92,7 +96,7 @@ func (ix *Index) Remove(item Item) { // Count counts all items in the index. func (ix *Index) Count() int { count := 0 - ix.Search(0, -90, -180, 90, 180, func(item Item) bool { + ix.Search(0, -90, -180, 90, 180, math.Inf(-1), math.Inf(+1), func(item Item) bool { count++ return true }) @@ -100,7 +104,7 @@ func (ix *Index) Count() int { } // Bounds returns the minimum bounding rectangle of all items in the index. -func (ix *Index) Bounds() (MinX, MinY, MaxX, MaxY float64) { +func (ix *Index) Bounds() (MinX, MinY, MinZ, MaxX, MaxY, MaxZ float64) { return ix.r.Bounds() } @@ -120,7 +124,7 @@ func (ix *Index) getRTreeItem(item rtree.Item) Item { } // Search returns all items that intersect the bounding box. -func (ix *Index) Search(cursor uint64, swLat, swLon, neLat, neLon float64, iterator func(item Item) bool) (ncursor uint64) { +func (ix *Index) Search(cursor uint64, swLat, swLon, neLat, neLon, minZ, maxZ float64, iterator func(item Item) bool) (ncursor uint64) { var idx uint64 var active = true var idm = make(map[Item]bool) @@ -130,7 +134,7 @@ func (ix *Index) Search(cursor uint64, swLat, swLon, neLat, neLon float64, itera // There is only one rectangle. // It's possible that a r rect may span multiple entries. Check mulm map for spanning rects. if active { - ix.r.Search(mins[0][0], mins[0][1], maxs[0][0], maxs[0][1], func(item rtree.Item) bool { + ix.r.Search(mins[0][0], mins[0][1], minZ, maxs[0][0], maxs[0][1], maxZ, func(item rtree.Item) bool { if idx >= cursor { iitm := ix.getRTreeItem(item) if iitm != nil { @@ -152,7 +156,7 @@ func (ix *Index) Search(cursor uint64, swLat, swLon, neLat, neLon float64, itera // There are multiple rectangles. Duplicates might occur. for i := range mins { if active { - ix.r.Search(mins[i][0], mins[i][1], maxs[i][0], maxs[i][1], func(item rtree.Item) bool { + ix.r.Search(mins[i][0], mins[i][1], minZ, maxs[i][0], maxs[i][1], maxZ, func(item rtree.Item) bool { if idx >= cursor { iitm := ix.getRTreeItem(item) if iitm != nil { diff --git a/index/index_test.go b/index/index_test.go index 80453956..24f33d1c 100644 --- a/index/index_test.go +++ b/index/index_test.go @@ -60,7 +60,7 @@ func TestRandomInserts(t *testing.T) { } count = 0 items := make([]Item, 0, l) - tr.Search(0, -90, -180, 90, 180, func(item Item) bool { + tr.Search(0, -90, -180, 90, 180, 0, 0, func(item Item) bool { count++ items = append(items, item) return true @@ -70,7 +70,7 @@ func TestRandomInserts(t *testing.T) { } start = time.Now() count1 := 0 - tr.Search(0, 33, -115, 34, -114, func(item Item) bool { + tr.Search(0, 33, -115, 34, -114, 0, 0, func(item Item) bool { count1++ return true }) @@ -79,7 +79,7 @@ func TestRandomInserts(t *testing.T) { start = time.Now() count2 := 0 - tr.Search(0, 33-180, -115-360, 34-180, -114-360, func(item Item) bool { + tr.Search(0, 33-180, -115-360, 34-180, -114-360, 0, 0, func(item Item) bool { count2++ return true }) @@ -87,7 +87,7 @@ func TestRandomInserts(t *testing.T) { start = time.Now() count3 := 0 - tr.Search(0, -10, 170, 20, 200, func(item Item) bool { + tr.Search(0, -10, 170, 20, 200, 0, 0, func(item Item) bool { count3++ return true }) @@ -99,16 +99,16 @@ func TestRandomInserts(t *testing.T) { fmt.Printf("Searched %d items in %s.\n", count2, searchdur2.String()) fmt.Printf("Searched %d items in %s.\n", count3, searchdur3.String()) - tr.Search(0, -10, 170, 20, 200, func(item Item) bool { - lat1, lon1, lat2, lon2 := item.Rect() + tr.Search(0, -10, 170, 20, 200, 0, 0, func(item Item) bool { + lat1, lon1, _, lat2, lon2, _ := item.Rect() if lat1 == lat2 && lon1 == lon2 { return false } return true }) - tr.Search(0, -10, 170, 20, 200, func(item Item) bool { - lat1, lon1, lat2, lon2 := item.Rect() + tr.Search(0, -10, 170, 20, 200, 0, 0, func(item Item) bool { + lat1, lon1, _, lat2, lon2, _ := item.Rect() if lat1 != lat2 || lon1 != lon2 { return false } @@ -173,7 +173,7 @@ func TestInsertVarious(t *testing.T) { t.Fatalf("count = %d, expect 1", count) } found := false - tr.Search(0, -90, -180, 90, 180, func(item2 Item) bool { + tr.Search(0, -90, -180, 90, 180, 0, 0, func(item2 Item) bool { if item2 == item { found = true } diff --git a/index/rtree/rtree.go b/index/rtree/rtree.go index 53f41c8f..97e5ae0e 100644 --- a/index/rtree/rtree.go +++ b/index/rtree/rtree.go @@ -2,46 +2,46 @@ package rtree // Item is an rtree item type Item interface { - Rect() (minX, minY, maxX, maxY float64) + Rect() (minX, minY, minZ, maxX, maxY, maxZ float64) } // Rect is a rectangle type Rect struct { - MinX, MinY, MaxX, MaxY float64 + MinX, MinY, MinZ, MaxX, MaxY, MaxZ float64 } // Rect returns the rectangle -func (item *Rect) Rect() (minX, minY, maxX, maxY float64) { - return item.MinX, item.MinY, item.MaxX, item.MaxY +func (item *Rect) Rect() (minX, minY, minZ, maxX, maxY, maxZ float64) { + return item.MinX, item.MinY, item.MinZ, item.MaxX, item.MaxY, item.MaxZ } // RTree is an implementation of an rtree type RTree struct { - tr *d2RTree + tr *d3RTree } // New creates a new RTree func New() *RTree { return &RTree{ - tr: d2New(), + tr: d3New(), } } // Insert inserts item into rtree func (tr *RTree) Insert(item Item) { - minX, minY, maxX, maxY := item.Rect() - tr.tr.Insert([2]float64{minX, minY}, [2]float64{maxX, maxY}, item) + minX, minY, minZ, maxX, maxY, maxZ := item.Rect() + tr.tr.Insert([3]float64{minX, minY, minZ}, [3]float64{maxX, maxY, maxZ}, item) } // Remove removes item from rtree func (tr *RTree) Remove(item Item) { - minX, minY, maxX, maxY := item.Rect() - tr.tr.Remove([2]float64{minX, minY}, [2]float64{maxX, maxY}, item) + minX, minY, minZ, maxX, maxY, maxZ := item.Rect() + tr.tr.Remove([3]float64{minX, minY, minZ}, [3]float64{maxX, maxY, maxZ}, item) } // Search finds all items in bounding box. -func (tr *RTree) Search(minX, minY, maxX, maxY float64, iterator func(item Item) bool) { - tr.tr.Search([2]float64{minX, minY}, [2]float64{maxX, maxY}, func(data interface{}) bool { +func (tr *RTree) Search(minX, minY, minZ, maxX, maxY, maxZ float64, iterator func(item Item) bool) { + tr.tr.Search([3]float64{minX, minY, minZ}, [3]float64{maxX, maxY, maxZ}, func(data interface{}) bool { return iterator(data.(Item)) }) } @@ -56,17 +56,17 @@ func (tr *RTree) RemoveAll() { tr.tr.RemoveAll() } -func (tr *RTree) Bounds() (minX, minY, maxX, maxY float64) { - var rect d2rectT +func (tr *RTree) Bounds() (minX, minY, minZ, maxX, maxY, maxZ float64) { + var rect d3rectT if tr.tr.root != nil { if tr.tr.root.count > 0 { rect = tr.tr.root.branch[0].rect for i := 1; i < tr.tr.root.count; i++ { rect2 := tr.tr.root.branch[i].rect - rect = d2combineRect(&rect, &rect2) + rect = d3combineRect(&rect, &rect2) } } } - minX, minY, maxX, maxY = rect.min[0], rect.min[1], rect.max[0], rect.max[1] + minX, minY, minZ, maxX, maxY, maxZ = rect.min[0], rect.min[1], rect.min[2], rect.max[0], rect.max[1], rect.max[2] return } diff --git a/index/rtree/rtree_test.go b/index/rtree/rtree_test.go index a8c4a900..c1d89c00 100644 --- a/index/rtree/rtree_test.go +++ b/index/rtree/rtree_test.go @@ -38,10 +38,10 @@ func wp(min, max []float64) *Rect { MaxY: max[1], } } -func wpp(x, y float64) *Rect { +func wpp(x, y, z float64) *Rect { return &Rect{ - x, y, - x, y, + x, y, z, + x, y, z, } } func TestA(t *testing.T) { @@ -51,7 +51,7 @@ func TestA(t *testing.T) { tr.Insert(item1) tr.Insert(item2) var itemA Item - tr.Search(21, 20, 25, 25, func(item Item) bool { + tr.Search(21, 20, 0, 25, 25, 0, func(item Item) bool { itemA = item return true }) @@ -77,14 +77,14 @@ func TestMemory(t *testing.T) { } func TestBounds(t *testing.T) { tr := New() - tr.Insert(wpp(10, 10)) - tr.Insert(wpp(10, 20)) - tr.Insert(wpp(10, 30)) - tr.Insert(wpp(20, 10)) - tr.Insert(wpp(30, 10)) - minX, minY, maxX, maxY := tr.Bounds() - if minX != 10 || minY != 10 || maxX != 30 || maxY != 30 { - t.Fatalf("expected 10,10 30,30, got %v,%v %v,%v\n", minX, minY, maxX, maxY) + tr.Insert(wpp(10, 10, 0)) + tr.Insert(wpp(10, 20, 0)) + tr.Insert(wpp(10, 30, 0)) + tr.Insert(wpp(20, 10, 0)) + tr.Insert(wpp(30, 10, 0)) + minX, minY, minZ, maxX, maxY, maxZ := tr.Bounds() + if minX != 10 || minY != 10 || minZ != 0 || maxX != 30 || maxY != 30 || maxZ != 0 { + t.Fatalf("expected 10,10,0 30,30,0, got %v,%v %v,%v\n", minX, minY, minZ, maxX, maxY, maxZ) } } func BenchmarkInsert(b *testing.B) { diff --git a/index/rtree/rtreed.go b/index/rtree/rtreed.go index d7153ba4..fb947c86 100644 --- a/index/rtree/rtreed.go +++ b/index/rtree/rtreed.go @@ -2,13 +2,13 @@ package rtree import "math" -func d2fmin(a, b float64) float64 { +func d3fmin(a, b float64) float64 { if a < b { return a } return b } -func d2fmax(a, b float64) float64 { +func d3fmax(a, b float64) float64 { if a > b { return a } @@ -16,13 +16,13 @@ func d2fmax(a, b float64) float64 { } const ( - d2numDims = 2 - d2maxNodes = 8 - d2minNodes = d2maxNodes / 2 - d2useSphericalVolume = true // Better split classification, may be slower on some systems + d3numDims = 3 + d3maxNodes = 8 + d3minNodes = d3maxNodes / 2 + d3useSphericalVolume = true // Better split classification, may be slower on some systems ) -var d2unitSphereVolume = []float64{ +var d3unitSphereVolume = []float64{ 0.000000, 2.000000, 3.141593, // Dimension 0,1,2 4.188790, 4.934802, 5.263789, // Dimension 3,4,5 5.167713, 4.724766, 4.058712, // Dimension 6,7,8 @@ -30,69 +30,69 @@ var d2unitSphereVolume = []float64{ 1.335263, 0.910629, 0.599265, // Dimension 12,13,14 0.381443, 0.235331, 0.140981, // Dimension 15,16,17 0.082146, 0.046622, 0.025807, // Dimension 18,19,20 -}[d2numDims] +}[d3numDims] -type d2RTree struct { - root *d2nodeT ///< Root of tree +type d3RTree struct { + root *d3nodeT ///< Root of tree } /// Minimal bounding rectangle (n-dimensional) -type d2rectT struct { - min [d2numDims]float64 ///< Min dimensions of bounding box - max [d2numDims]float64 ///< Max dimensions of bounding box +type d3rectT struct { + min [d3numDims]float64 ///< Min dimensions of bounding box + max [d3numDims]float64 ///< Max dimensions of bounding box } /// May be data or may be another subtree /// The parents level determines this. /// If the parents level is 0, then this is data -type d2branchT struct { - rect d2rectT ///< Bounds - child *d2nodeT ///< Child node +type d3branchT struct { + rect d3rectT ///< Bounds + child *d3nodeT ///< Child node data interface{} ///< Data Id or Ptr } -/// d2nodeT for each branch level -type d2nodeT struct { +/// d3nodeT for each branch level +type d3nodeT struct { count int ///< Count level int ///< Leaf is zero, others positive - branch [d2maxNodes]d2branchT ///< Branch + branch [d3maxNodes]d3branchT ///< Branch } -func (node *d2nodeT) isInternalNode() bool { +func (node *d3nodeT) isInternalNode() bool { return (node.level > 0) // Not a leaf, but a internal node } -func (node *d2nodeT) isLeaf() bool { +func (node *d3nodeT) isLeaf() bool { return (node.level == 0) // A leaf, contains data } /// A link list of nodes for reinsertion after a delete operation -type d2listNodeT struct { - next *d2listNodeT ///< Next in list - node *d2nodeT ///< Node +type d3listNodeT struct { + next *d3listNodeT ///< Next in list + node *d3nodeT ///< Node } -const d2notTaken = -1 // indicates that position +const d3notTaken = -1 // indicates that position /// Variables for finding a split partition -type d2partitionVarsT struct { - partition [d2maxNodes + 1]int +type d3partitionVarsT struct { + partition [d3maxNodes + 1]int total int minFill int count [2]int - cover [2]d2rectT + cover [2]d3rectT area [2]float64 - branchBuf [d2maxNodes + 1]d2branchT + branchBuf [d3maxNodes + 1]d3branchT branchCount int - coverSplit d2rectT + coverSplit d3rectT coverSplitArea float64 } -func d2New() *d2RTree { +func d3New() *d3RTree { // We only support machine word size simple data type eg. integer index or object pointer. // Since we are storing as union with non data branch - return &d2RTree{ - root: &d2nodeT{}, + return &d3RTree{ + root: &d3nodeT{}, } } @@ -100,63 +100,63 @@ func d2New() *d2RTree { /// \param a_min Min of bounding rect /// \param a_max Max of bounding rect /// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed. -func (tr *d2RTree) Insert(min, max [d2numDims]float64, dataId interface{}) { - var branch d2branchT +func (tr *d3RTree) Insert(min, max [d3numDims]float64, dataId interface{}) { + var branch d3branchT branch.data = dataId - for axis := 0; axis < d2numDims; axis++ { + for axis := 0; axis < d3numDims; axis++ { branch.rect.min[axis] = min[axis] branch.rect.max[axis] = max[axis] } - d2insertRect(&branch, &tr.root, 0) + d3insertRect(&branch, &tr.root, 0) } /// Remove entry /// \param a_min Min of bounding rect /// \param a_max Max of bounding rect /// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed. -func (tr *d2RTree) Remove(min, max [d2numDims]float64, dataId interface{}) { - var rect d2rectT - for axis := 0; axis < d2numDims; axis++ { +func (tr *d3RTree) Remove(min, max [d3numDims]float64, dataId interface{}) { + var rect d3rectT + for axis := 0; axis < d3numDims; axis++ { rect.min[axis] = min[axis] rect.max[axis] = max[axis] } - d2removeRect(&rect, dataId, &tr.root) + d3removeRect(&rect, dataId, &tr.root) } -/// Find all within d2search rectangle -/// \param a_min Min of d2search bounding rect -/// \param a_max Max of d2search bounding rect -/// \param a_searchResult d2search result array. Caller should set grow size. Function will reset, not append to array. +/// Find all within d3search rectangle +/// \param a_min Min of d3search bounding rect +/// \param a_max Max of d3search bounding rect +/// \param a_searchResult d3search result array. Caller should set grow size. Function will reset, not append to array. /// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching /// \param a_context User context to pass as parameter to a_resultCallback /// \return Returns the number of entries found -func (tr *d2RTree) Search(min, max [d2numDims]float64, resultCallback func(data interface{}) bool) int { - var rect d2rectT - for axis := 0; axis < d2numDims; axis++ { +func (tr *d3RTree) Search(min, max [d3numDims]float64, resultCallback func(data interface{}) bool) int { + var rect d3rectT + for axis := 0; axis < d3numDims; axis++ { rect.min[axis] = min[axis] rect.max[axis] = max[axis] } - foundCount, _ := d2search(tr.root, rect, 0, resultCallback) + foundCount, _ := d3search(tr.root, rect, 0, resultCallback) return foundCount } /// Count the data elements in this container. This is slow as no internal counter is maintained. -func (tr *d2RTree) Count() int { +func (tr *d3RTree) Count() int { var count int - d2countRec(tr.root, &count) + d3countRec(tr.root, &count) return count } /// Remove all entries from tree -func (tr *d2RTree) RemoveAll() { +func (tr *d3RTree) RemoveAll() { // Delete all existing nodes - tr.root = &d2nodeT{} + tr.root = &d3nodeT{} } -func d2countRec(node *d2nodeT, count *int) { +func d3countRec(node *d3nodeT, count *int) { if node.isInternalNode() { // not a leaf node for index := 0; index < node.count; index++ { - d2countRec(node.branch[index].child, count) + d3countRec(node.branch[index].child, count) } } else { // A leaf node *count += node.count @@ -170,40 +170,40 @@ func d2countRec(node *d2nodeT, count *int) { // new_node to point to the new node. Old node updated to become one of two. // The level argument specifies the number of steps up from the leaf // level to insert; e.g. a data rectangle goes in at level = 0. -func d2insertRectRec(branch *d2branchT, node *d2nodeT, newNode **d2nodeT, level int) bool { +func d3insertRectRec(branch *d3branchT, node *d3nodeT, newNode **d3nodeT, level int) bool { // recurse until we reach the correct level for the new record. data records // will always be called with a_level == 0 (leaf) if node.level > level { // Still above level for insertion, go down tree recursively - var otherNode *d2nodeT - //var newBranch d2branchT + var otherNode *d3nodeT + //var newBranch d3branchT // find the optimal branch for this record - index := d2pickBranch(&branch.rect, node) + index := d3pickBranch(&branch.rect, node) // recursively insert this record into the picked branch - childWasSplit := d2insertRectRec(branch, node.branch[index].child, &otherNode, level) + childWasSplit := d3insertRectRec(branch, node.branch[index].child, &otherNode, level) if !childWasSplit { // Child was not split. Merge the bounding box of the new record with the // existing bounding box - node.branch[index].rect = d2combineRect(&branch.rect, &(node.branch[index].rect)) + node.branch[index].rect = d3combineRect(&branch.rect, &(node.branch[index].rect)) return false } else { // Child was split. The old branches are now re-partitioned to two nodes // so we have to re-calculate the bounding boxes of each node - node.branch[index].rect = d2nodeCover(node.branch[index].child) - var newBranch d2branchT + node.branch[index].rect = d3nodeCover(node.branch[index].child) + var newBranch d3branchT newBranch.child = otherNode - newBranch.rect = d2nodeCover(otherNode) + newBranch.rect = d3nodeCover(otherNode) // The old node is already a child of a_node. Now add the newly-created // node to a_node as well. a_node might be split because of that. - return d2addBranch(&newBranch, node, newNode) + return d3addBranch(&newBranch, node, newNode) } } else if node.level == level { // We have reached level for insertion. Add rect, split if necessary - return d2addBranch(branch, node, newNode) + return d3addBranch(branch, node, newNode) } else { // Should never occur return false @@ -211,32 +211,32 @@ func d2insertRectRec(branch *d2branchT, node *d2nodeT, newNode **d2nodeT, level } // Insert a data rectangle into an index structure. -// d2insertRect provides for splitting the root; +// d3insertRect provides for splitting the root; // returns 1 if root was split, 0 if it was not. // The level argument specifies the number of steps up from the leaf // level to insert; e.g. a data rectangle goes in at level = 0. // InsertRect2 does the recursion. // -func d2insertRect(branch *d2branchT, root **d2nodeT, level int) bool { - var newNode *d2nodeT +func d3insertRect(branch *d3branchT, root **d3nodeT, level int) bool { + var newNode *d3nodeT - if d2insertRectRec(branch, *root, &newNode, level) { // Root split + if d3insertRectRec(branch, *root, &newNode, level) { // Root split // Grow tree taller and new root - newRoot := &d2nodeT{} + newRoot := &d3nodeT{} newRoot.level = (*root).level + 1 - var newBranch d2branchT + var newBranch d3branchT // add old root node as a child of the new root - newBranch.rect = d2nodeCover(*root) + newBranch.rect = d3nodeCover(*root) newBranch.child = *root - d2addBranch(&newBranch, newRoot, nil) + d3addBranch(&newBranch, newRoot, nil) // add the split node as a child of the new root - newBranch.rect = d2nodeCover(newNode) + newBranch.rect = d3nodeCover(newNode) newBranch.child = newNode - d2addBranch(&newBranch, newRoot, nil) + d3addBranch(&newBranch, newRoot, nil) // set the new root as the root node *root = newRoot @@ -247,10 +247,10 @@ func d2insertRect(branch *d2branchT, root **d2nodeT, level int) bool { } // Find the smallest rectangle that includes all rectangles in branches of a node. -func d2nodeCover(node *d2nodeT) d2rectT { +func d3nodeCover(node *d3nodeT) d3rectT { rect := node.branch[0].rect for index := 1; index < node.count; index++ { - rect = d2combineRect(&rect, &(node.branch[index].rect)) + rect = d3combineRect(&rect, &(node.branch[index].rect)) } return rect } @@ -259,20 +259,20 @@ func d2nodeCover(node *d2nodeT) d2rectT { // Returns 0 if node not split. Old node updated. // Returns 1 if node split, sets *new_node to address of new node. // Old node updated, becomes one of two. -func d2addBranch(branch *d2branchT, node *d2nodeT, newNode **d2nodeT) bool { - if node.count < d2maxNodes { // Split won't be necessary +func d3addBranch(branch *d3branchT, node *d3nodeT, newNode **d3nodeT) bool { + if node.count < d3maxNodes { // Split won't be necessary node.branch[node.count] = *branch node.count++ return false } else { - d2splitNode(node, branch, newNode) + d3splitNode(node, branch, newNode) return true } } // Disconnect a dependent node. // Caller must return (or stop using iteration index) after this as count has changed -func d2disconnectBranch(node *d2nodeT, index int) { +func d3disconnectBranch(node *d3nodeT, index int) { // Remove element by swapping with the last element to prevent gaps in array node.branch[index] = node.branch[node.count-1] node.branch[node.count-1].data = nil @@ -285,20 +285,20 @@ func d2disconnectBranch(node *d2nodeT, index int) { // least total area for the covering rectangles in the current node. // In case of a tie, pick the one which was smaller before, to get // the best resolution when searching. -func d2pickBranch(rect *d2rectT, node *d2nodeT) int { +func d3pickBranch(rect *d3rectT, node *d3nodeT) int { var firstTime bool = true var increase float64 var bestIncr float64 = -1 var area float64 var bestArea float64 var best int - var tempRect d2rectT + var tempRect d3rectT for index := 0; index < node.count; index++ { curRect := &node.branch[index].rect - area = d2calcRectVolume(curRect) - tempRect = d2combineRect(rect, curRect) - increase = d2calcRectVolume(&tempRect) - area + area = d3calcRectVolume(curRect) + tempRect = d3combineRect(rect, curRect) + increase = d3calcRectVolume(&tempRect) - area if (increase < bestIncr) || firstTime { best = index bestArea = area @@ -314,12 +314,12 @@ func d2pickBranch(rect *d2rectT, node *d2nodeT) int { } // Combine two rectangles into larger one containing both -func d2combineRect(rectA, rectB *d2rectT) d2rectT { - var newRect d2rectT +func d3combineRect(rectA, rectB *d3rectT) d3rectT { + var newRect d3rectT - for index := 0; index < d2numDims; index++ { - newRect.min[index] = d2fmin(rectA.min[index], rectB.min[index]) - newRect.max[index] = d2fmax(rectA.max[index], rectB.max[index]) + for index := 0; index < d3numDims; index++ { + newRect.min[index] = d3fmin(rectA.min[index], rectB.min[index]) + newRect.max[index] = d3fmax(rectA.max[index], rectB.max[index]) } return newRect @@ -329,41 +329,41 @@ func d2combineRect(rectA, rectB *d2rectT) d2rectT { // Divides the nodes branches and the extra one between two nodes. // Old node is one of the new ones, and one really new one is created. // Tries more than one method for choosing a partition, uses best result. -func d2splitNode(node *d2nodeT, branch *d2branchT, newNode **d2nodeT) { +func d3splitNode(node *d3nodeT, branch *d3branchT, newNode **d3nodeT) { // Could just use local here, but member or external is faster since it is reused - var localVars d2partitionVarsT + var localVars d3partitionVarsT parVars := &localVars // Load all the branches into a buffer, initialize old node - d2getBranches(node, branch, parVars) + d3getBranches(node, branch, parVars) // Find partition - d2choosePartition(parVars, d2minNodes) + d3choosePartition(parVars, d3minNodes) // Create a new node to hold (about) half of the branches - *newNode = &d2nodeT{} + *newNode = &d3nodeT{} (*newNode).level = node.level // Put branches from buffer into 2 nodes according to the chosen partition node.count = 0 - d2loadNodes(node, *newNode, parVars) + d3loadNodes(node, *newNode, parVars) } // Calculate the n-dimensional volume of a rectangle -func d2rectVolume(rect *d2rectT) float64 { +func d3rectVolume(rect *d3rectT) float64 { var volume float64 = 1 - for index := 0; index < d2numDims; index++ { + for index := 0; index < d3numDims; index++ { volume *= rect.max[index] - rect.min[index] } return volume } -// The exact volume of the bounding sphere for the given d2rectT -func d2rectSphericalVolume(rect *d2rectT) float64 { +// The exact volume of the bounding sphere for the given d3rectT +func d3rectSphericalVolume(rect *d3rectT) float64 { var sumOfSquares float64 = 0 var radius float64 - for index := 0; index < d2numDims; index++ { + for index := 0; index < d3numDims; index++ { halfExtent := (rect.max[index] - rect.min[index]) * 0.5 sumOfSquares += halfExtent * halfExtent } @@ -371,43 +371,43 @@ func d2rectSphericalVolume(rect *d2rectT) float64 { radius = math.Sqrt(sumOfSquares) // Pow maybe slow, so test for common dims just use x*x, x*x*x. - if d2numDims == 5 { - return (radius * radius * radius * radius * radius * d2unitSphereVolume) - } else if d2numDims == 4 { - return (radius * radius * radius * radius * d2unitSphereVolume) - } else if d2numDims == 3 { - return (radius * radius * radius * d2unitSphereVolume) - } else if d2numDims == 2 { - return (radius * radius * d2unitSphereVolume) + if d3numDims == 5 { + return (radius * radius * radius * radius * radius * d3unitSphereVolume) + } else if d3numDims == 4 { + return (radius * radius * radius * radius * d3unitSphereVolume) + } else if d3numDims == 3 { + return (radius * radius * radius * d3unitSphereVolume) + } else if d3numDims == 2 { + return (radius * radius * d3unitSphereVolume) } else { - return (math.Pow(radius, d2numDims) * d2unitSphereVolume) + return (math.Pow(radius, d3numDims) * d3unitSphereVolume) } } // Use one of the methods to calculate retangle volume -func d2calcRectVolume(rect *d2rectT) float64 { - if d2useSphericalVolume { - return d2rectSphericalVolume(rect) // Slower but helps certain merge cases +func d3calcRectVolume(rect *d3rectT) float64 { + if d3useSphericalVolume { + return d3rectSphericalVolume(rect) // Slower but helps certain merge cases } else { // RTREE_USE_SPHERICAL_VOLUME - return d2rectVolume(rect) // Faster but can cause poor merges + return d3rectVolume(rect) // Faster but can cause poor merges } // RTREE_USE_SPHERICAL_VOLUME } // Load branch buffer with branches from full node plus the extra branch. -func d2getBranches(node *d2nodeT, branch *d2branchT, parVars *d2partitionVarsT) { +func d3getBranches(node *d3nodeT, branch *d3branchT, parVars *d3partitionVarsT) { // Load the branch buffer - for index := 0; index < d2maxNodes; index++ { + for index := 0; index < d3maxNodes; index++ { parVars.branchBuf[index] = node.branch[index] } - parVars.branchBuf[d2maxNodes] = *branch - parVars.branchCount = d2maxNodes + 1 + parVars.branchBuf[d3maxNodes] = *branch + parVars.branchCount = d3maxNodes + 1 // Calculate rect containing all in the set parVars.coverSplit = parVars.branchBuf[0].rect - for index := 1; index < d2maxNodes+1; index++ { - parVars.coverSplit = d2combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect) + for index := 1; index < d3maxNodes+1; index++ { + parVars.coverSplit = d3combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect) } - parVars.coverSplitArea = d2calcRectVolume(&parVars.coverSplit) + parVars.coverSplitArea = d3calcRectVolume(&parVars.coverSplit) } // Method #0 for choosing a partition: @@ -421,24 +421,24 @@ func d2getBranches(node *d2nodeT, branch *d2branchT, parVars *d2partitionVarsT) // If one group gets too full (more would force other group to violate min // fill requirement) then other group gets the rest. // These last are the ones that can go in either group most easily. -func d2choosePartition(parVars *d2partitionVarsT, minFill int) { +func d3choosePartition(parVars *d3partitionVarsT, minFill int) { var biggestDiff float64 var group, chosen, betterGroup int - d2initParVars(parVars, parVars.branchCount, minFill) - d2pickSeeds(parVars) + d3initParVars(parVars, parVars.branchCount, minFill) + d3pickSeeds(parVars) for ((parVars.count[0] + parVars.count[1]) < parVars.total) && (parVars.count[0] < (parVars.total - parVars.minFill)) && (parVars.count[1] < (parVars.total - parVars.minFill)) { biggestDiff = -1 for index := 0; index < parVars.total; index++ { - if d2notTaken == parVars.partition[index] { + if d3notTaken == parVars.partition[index] { curRect := &parVars.branchBuf[index].rect - rect0 := d2combineRect(curRect, &parVars.cover[0]) - rect1 := d2combineRect(curRect, &parVars.cover[1]) - growth0 := d2calcRectVolume(&rect0) - parVars.area[0] - growth1 := d2calcRectVolume(&rect1) - parVars.area[1] + rect0 := d3combineRect(curRect, &parVars.cover[0]) + rect1 := d3combineRect(curRect, &parVars.cover[1]) + growth0 := d3calcRectVolume(&rect0) - parVars.area[0] + growth1 := d3calcRectVolume(&rect1) - parVars.area[1] diff := growth1 - growth0 if diff >= 0 { group = 0 @@ -457,7 +457,7 @@ func d2choosePartition(parVars *d2partitionVarsT, minFill int) { } } } - d2classify(chosen, betterGroup, parVars) + d3classify(chosen, betterGroup, parVars) } // If one group too full, put remaining rects in the other @@ -468,26 +468,26 @@ func d2choosePartition(parVars *d2partitionVarsT, minFill int) { group = 0 } for index := 0; index < parVars.total; index++ { - if d2notTaken == parVars.partition[index] { - d2classify(index, group, parVars) + if d3notTaken == parVars.partition[index] { + d3classify(index, group, parVars) } } } } // Copy branches from the buffer into two nodes according to the partition. -func d2loadNodes(nodeA, nodeB *d2nodeT, parVars *d2partitionVarsT) { +func d3loadNodes(nodeA, nodeB *d3nodeT, parVars *d3partitionVarsT) { for index := 0; index < parVars.total; index++ { targetNodeIndex := parVars.partition[index] - targetNodes := []*d2nodeT{nodeA, nodeB} + targetNodes := []*d3nodeT{nodeA, nodeB} - // It is assured that d2addBranch here will not cause a node split. - d2addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil) + // It is assured that d3addBranch here will not cause a node split. + d3addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil) } } -// Initialize a d2partitionVarsT structure. -func d2initParVars(parVars *d2partitionVarsT, maxRects, minFill int) { +// Initialize a d3partitionVarsT structure. +func d3initParVars(parVars *d3partitionVarsT, maxRects, minFill int) { parVars.count[0] = 0 parVars.count[1] = 0 parVars.area[0] = 0 @@ -495,24 +495,24 @@ func d2initParVars(parVars *d2partitionVarsT, maxRects, minFill int) { parVars.total = maxRects parVars.minFill = minFill for index := 0; index < maxRects; index++ { - parVars.partition[index] = d2notTaken + parVars.partition[index] = d3notTaken } } -func d2pickSeeds(parVars *d2partitionVarsT) { +func d3pickSeeds(parVars *d3partitionVarsT) { var seed0, seed1 int var worst, waste float64 - var area [d2maxNodes + 1]float64 + var area [d3maxNodes + 1]float64 for index := 0; index < parVars.total; index++ { - area[index] = d2calcRectVolume(&parVars.branchBuf[index].rect) + area[index] = d3calcRectVolume(&parVars.branchBuf[index].rect) } worst = -parVars.coverSplitArea - 1 for indexA := 0; indexA < parVars.total-1; indexA++ { for indexB := indexA + 1; indexB < parVars.total; indexB++ { - oneRect := d2combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect) - waste = d2calcRectVolume(&oneRect) - area[indexA] - area[indexB] + oneRect := d3combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect) + waste = d3calcRectVolume(&oneRect) - area[indexA] - area[indexB] if waste > worst { worst = waste seed0 = indexA @@ -521,35 +521,35 @@ func d2pickSeeds(parVars *d2partitionVarsT) { } } - d2classify(seed0, 0, parVars) - d2classify(seed1, 1, parVars) + d3classify(seed0, 0, parVars) + d3classify(seed1, 1, parVars) } // Put a branch in one of the groups. -func d2classify(index, group int, parVars *d2partitionVarsT) { +func d3classify(index, group int, parVars *d3partitionVarsT) { parVars.partition[index] = group // Calculate combined rect if parVars.count[group] == 0 { parVars.cover[group] = parVars.branchBuf[index].rect } else { - parVars.cover[group] = d2combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group]) + parVars.cover[group] = d3combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group]) } // Calculate volume of combined rect - parVars.area[group] = d2calcRectVolume(&parVars.cover[group]) + parVars.area[group] = d3calcRectVolume(&parVars.cover[group]) parVars.count[group]++ } // Delete a data rectangle from an index structure. -// Pass in a pointer to a d2rectT, the tid of the record, ptr to ptr to root node. +// Pass in a pointer to a d3rectT, the tid of the record, ptr to ptr to root node. // Returns 1 if record not found, 0 if success. -// d2removeRect provides for eliminating the root. -func d2removeRect(rect *d2rectT, id interface{}, root **d2nodeT) bool { - var reInsertList *d2listNodeT +// d3removeRect provides for eliminating the root. +func d3removeRect(rect *d3rectT, id interface{}, root **d3nodeT) bool { + var reInsertList *d3listNodeT - if !d2removeRectRec(rect, id, *root, &reInsertList) { + if !d3removeRectRec(rect, id, *root, &reInsertList) { // Found and deleted a data item // Reinsert any branches from eliminated nodes for reInsertList != nil { @@ -557,7 +557,7 @@ func d2removeRect(rect *d2rectT, id interface{}, root **d2nodeT) bool { for index := 0; index < tempNode.count; index++ { // TODO go over this code. should I use (tempNode->m_level - 1)? - d2insertRect(&tempNode.branch[index], root, tempNode.level) + d3insertRect(&tempNode.branch[index], root, tempNode.level) } reInsertList = reInsertList.next } @@ -575,21 +575,21 @@ func d2removeRect(rect *d2rectT, id interface{}, root **d2nodeT) bool { } // Delete a rectangle from non-root part of an index structure. -// Called by d2removeRect. Descends tree recursively, +// Called by d3removeRect. Descends tree recursively, // merges branches on the way back up. // Returns 1 if record not found, 0 if success. -func d2removeRectRec(rect *d2rectT, id interface{}, node *d2nodeT, listNode **d2listNodeT) bool { +func d3removeRectRec(rect *d3rectT, id interface{}, node *d3nodeT, listNode **d3listNodeT) bool { if node.isInternalNode() { // not a leaf node for index := 0; index < node.count; index++ { - if d2overlap(*rect, node.branch[index].rect) { - if !d2removeRectRec(rect, id, node.branch[index].child, listNode) { - if node.branch[index].child.count >= d2minNodes { + if d3overlap(*rect, node.branch[index].rect) { + if !d3removeRectRec(rect, id, node.branch[index].child, listNode) { + if node.branch[index].child.count >= d3minNodes { // child removed, just resize parent rect - node.branch[index].rect = d2nodeCover(node.branch[index].child) + node.branch[index].rect = d3nodeCover(node.branch[index].child) } else { // child removed, not enough entries in node, eliminate node - d2reInsert(node.branch[index].child, listNode) - d2disconnectBranch(node, index) // Must return after this call as count has changed + d3reInsert(node.branch[index].child, listNode) + d3disconnectBranch(node, index) // Must return after this call as count has changed } return false } @@ -599,7 +599,7 @@ func d2removeRectRec(rect *d2rectT, id interface{}, node *d2nodeT, listNode **d2 } else { // A leaf node for index := 0; index < node.count; index++ { if node.branch[index].data == id { - d2disconnectBranch(node, index) // Must return after this call as count has changed + d3disconnectBranch(node, index) // Must return after this call as count has changed return false } } @@ -607,9 +607,9 @@ func d2removeRectRec(rect *d2rectT, id interface{}, node *d2nodeT, listNode **d2 } } -// Decide whether two rectangles d2overlap. -func d2overlap(rectA, rectB d2rectT) bool { - for index := 0; index < d2numDims; index++ { +// Decide whether two rectangles d3overlap. +func d3overlap(rectA, rectB d3rectT) bool { + for index := 0; index < d3numDims; index++ { if rectA.min[index] > rectB.max[index] || rectB.min[index] > rectA.max[index] { return false @@ -620,21 +620,21 @@ func d2overlap(rectA, rectB d2rectT) bool { // Add a node to the reinsertion list. All its branches will later // be reinserted into the index structure. -func d2reInsert(node *d2nodeT, listNode **d2listNodeT) { - newListNode := &d2listNodeT{} +func d3reInsert(node *d3nodeT, listNode **d3listNodeT) { + newListNode := &d3listNodeT{} newListNode.node = node newListNode.next = *listNode *listNode = newListNode } -// d2search in an index tree or subtree for all data retangles that d2overlap the argument rectangle. -func d2search(node *d2nodeT, rect d2rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) { +// d3search in an index tree or subtree for all data retangles that d3overlap the argument rectangle. +func d3search(node *d3nodeT, rect d3rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) { if node.isInternalNode() { // This is an internal node in the tree for index := 0; index < node.count; index++ { - if d2overlap(rect, node.branch[index].rect) { + if d3overlap(rect, node.branch[index].rect) { var ok bool - foundCount, ok = d2search(node.branch[index].child, rect, foundCount, resultCallback) + foundCount, ok = d3search(node.branch[index].child, rect, foundCount, resultCallback) if !ok { // The callback indicated to stop searching return foundCount, false @@ -644,7 +644,7 @@ func d2search(node *d2nodeT, rect d2rectT, foundCount int, resultCallback func(d } else { // This is a leaf node for index := 0; index < node.count; index++ { - if d2overlap(rect, node.branch[index].rect) { + if d3overlap(rect, node.branch[index].rect) { id := node.branch[index].data foundCount++ if !resultCallback(id) {