golang高效区域四叉树点定位与邻近查找插件库go-rquad的使用

Golang高效区域四叉树点定位与邻近查找插件库go-rquad的使用

Go中的区域四叉树

go-rquad提供了多种区域四叉树的实现。

区域四叉树是一种特殊的四叉树,它递归地将二维空间划分为4个更小且通常相等的矩形区域,直到达到所需的分辨率或无法进一步划分为止。

区域四叉树可以用于图像处理,在这种情况下,叶节点表示图像中所有颜色相同或颜色差异低于给定阈值的矩形区域。

区域四叉树也可以用于表示具有可变分辨率的数据字段。例如,一个区域的温度可以存储为四叉树,其中每个叶节点存储它所代表的子区域的平均温度。

API概述

Node接口

type Node interface {
        Parent() Node
        Child(Quadrant) Node
        Bounds() image.Rectangle
        Color() Color
        Location() Quadrant
}

Quadtree接口

Quadtree表示Node的分层集合,其API很简单:访问根节点和遍历所有叶节点的方法。

type Quadtree interface {
        ForEachLeaf(Color, func(Node))
        Root() Node
}

函数

Locate返回包含ptq的叶节点,如果q不包含pt则返回nil。

func Locate(q Quadtree, pt image.Point) Node

ForEachNeighbourn的每个邻居调用fn

func ForEachNeighbour(n Node, fn func(Node))

基本实现:BasicTreebasicNode

BasicTree在很多方面是Quadtree的标准实现,它只是完成工作。

最先进的实现:CNTreeCNNode

CNTreeCardinal Neighbour Quadtree实现了最先进的技术:

  • 从任何给定的叶节点,其邻居(任何大小)都可以在常数时间O(1)内访问,因为它们实现了Cardinal Neighbour Quadtree技术(Safwan Qasem 2015)。通过每个叶节点仅添加四个指针,实现了时间复杂度降低。
  • 快速点定位查询(定位包含特定点的叶节点),得益于binary branching method(Frisken Perry 2002)。这种简单高效的方法是非递归的,无需查找表,并减少了标准方法所需的具有较差预测行为的比较次数。

完整示例Demo

package main

import (
	"fmt"
	"image"
	
	"github.com/arl/go-rquad"
	"github.com/arl/go-rquad/color"
)

func main() {
	// 创建一个100x100的矩形区域
	bounds := image.Rect(0, 0, 100, 100)
	
	// 创建一个CNTree四叉树
	qt := rquad.NewCNTree(bounds, color.GrayModel, nil)
	
	// 插入一些点
	points := []image.Point{
		{10, 10},
		{20, 20},
		{30, 30},
		{40, 40},
		{50, 50},
	}
	
	for _, pt := range points {
		qt.Insert(pt, color.Gray{Y: 128}) // 使用灰色
	}
	
	// 定位一个点
	pt := image.Point{20, 20}
	node := rquad.Locate(qt, pt)
	if node != nil {
		fmt.Printf("Found point at %v in node with bounds %v\n", pt, node.Bounds())
	} else {
		fmt.Printf("Point %v not found\n", pt)
	}
	
	// 查找邻居
	if node != nil {
		fmt.Println("Neighbors of the node:")
		rquad.ForEachNeighbour(node, func(n rquad.Node) {
			fmt.Printf("  Neighbor bounds: %v\n", n.Bounds())
		})
	}
	
	// 遍历所有叶节点
	fmt.Println("All leaf nodes:")
	qt.ForEachLeaf(color.Gray{Y: 128}, func(n rquad.Node) {
		fmt.Printf("  Leaf node bounds: %v\n", n.Bounds())
	})
}

基准测试

Quadtree creation benchmark

Neighbour finding benchmark

Point location benchmark

许可证

go-rquad是根据MIT许可证发布的开源软件:

版权所有 © 2016 Aurélien Rainone

特此免费授予任何获得本软件及相关文档文件副本的人不受限制地使用本软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售本软件副本的权利,并允许向其提供本软件的人这样做,但须符合以下条件:

上述版权声明和本许可声明应包含在所有副本或实质性部分中。

本软件按"原样"提供,不作任何明示或暗示的保证,包括但不限于适销性、特定用途适用性和非侵权性的保证。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是在合同、侵权或其他行为中产生的,与本软件或使用或其他交易有关。


更多关于golang高效区域四叉树点定位与邻近查找插件库go-rquad的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高效区域四叉树点定位与邻近查找插件库go-rquad的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


go-rquad: 高效的Golang四叉树实现

go-rquad 是一个高效的Golang四叉树实现库,特别适合用于空间索引和邻近查找。下面我将详细介绍它的使用方法和示例代码。

安装

go get github.com/aurelien-rainone/go-rquad

基本概念

四叉树是一种树数据结构,其中每个内部节点恰好有四个子节点。它常用于二维空间数据的划分和索引,主要用途包括:

  • 点定位
  • 邻近查找
  • 碰撞检测
  • 空间分区

基本使用

1. 创建四叉树

package main

import (
	"fmt"
	"github.com/aurelien-rainone/go-rquad"
)

func main() {
	// 定义边界区域:x, y, width, height
	bounds := rquad.NewRect(0, 0, 100, 100)
	
	// 创建四叉树,最大深度为5
	qt := rquad.New(bounds, 5)
	
	fmt.Println("四叉树创建成功,边界:", qt.Bounds())
}

2. 插入点

type Point struct {
	X, Y float64
	Name string
}

func main() {
	bounds := rquad.NewRect(0, 0, 100, 100)
	qt := rquad.New(bounds, 5)
	
	// 插入点
	points := []Point{
		{X: 10, Y: 20, Name: "A"},
		{X: 30, Y: 40, Name: "B"},
		{X: 50, Y: 60, Name: "C"},
	}
	
	for _, p := range points {
		qt.Insert(rquad.Point{p.X, p.Y}, p)
	}
	
	fmt.Println("插入", len(points), "个点")
}

3. 查找点

func main() {
	// ... 创建四叉树和插入点的代码同上 ...
	
	// 查找特定位置的点
	target := rquad.Point{30, 40}
	found := qt.Find(target)
	
	if found != nil {
		fmt.Printf("在位置 %.1f,%.1f 找到点: %v\n", 
			target.X, target.Y, found.(Point))
	} else {
		fmt.Println("未找到点")
	}
}

4. 区域查询

func main() {
	// ... 创建四叉树和插入点的代码同上 ...
	
	// 定义查询区域:x, y, width, height
	queryArea := rquad.NewRect(20, 30, 40, 40)
	
	// 查找区域内的所有点
	var results []Point
	qt.ForEach(queryArea, func(p rquad.Point, v interface{}) bool {
		results = append(results, v.(Point))
		return true // 继续遍历
	})
	
	fmt.Printf("在区域 %v 内找到 %d 个点:\n", queryArea, len(results))
	for _, p := range results {
		fmt.Println(p)
	}
}

5. 邻近查找

func main() {
	// ... 创建四叉树和插入点的代码同上 ...
	
	// 查找距离某个点最近的邻居
	target := rquad.Point{35, 45}
	maxDistance := 30.0
	
	var nearest Point
	minDist := maxDistance
	
	qt.ForEach(nil, func(p rquad.Point, v interface{}) bool {
		dist := distance(target, p)
		if dist < minDist {
			minDist = dist
			nearest = v.(Point)
		}
		return true
	})
	
	if minDist < maxDistance {
		fmt.Printf("最近的邻居是 %v (距离 %.2f)\n", nearest, minDist)
	} else {
		fmt.Println("未找到邻近点")
	}
}

func distance(p1, p2 rquad.Point) float64 {
	dx := p1.X - p2.X
	dy := p1.Y - p2.Y
	return math.Sqrt(dx*dx + dy*dy)
}

高级功能

1. 自定义节点容量

func main() {
	bounds := rquad.NewRect(0, 0, 100, 100)
	
	// 创建四叉树,设置每个节点最多包含4个点,最大深度为5
	qt := rquad.NewOptions(bounds, &rquad.Options{
		MaxNodePoints: 4,
		MaxDepth:      5,
	})
	
	// ... 插入和查询操作 ...
}

2. 遍历整个四叉树

func main() {
	// ... 创建四叉树和插入点的代码 ...
	
	// 遍历所有节点
	qt.Walk(func(n *rquad.Node) bool {
		fmt.Printf("节点: %v, 点数: %d\n", n.Bounds(), len(n.Points()))
		return true // 继续遍历
	})
}

3. 可视化四叉树结构

func printTree(qt *rquad.Quadtree, level int) {
	prefix := ""
	for i := 0; i < level; i++ {
		prefix += "  "
	}
	
	qt.Walk(func(n *rquad.Node) bool {
		fmt.Printf("%s节点 %v (点数: %d)\n", prefix, n.Bounds(), len(n.Points()))
		return true
	})
}

func main() {
	// ... 创建四叉树和插入点的代码 ...
	
	fmt.Println("四叉树结构:")
	printTree(qt, 0)
}

性能优化建议

  1. 合理设置最大深度:太深会导致内存浪费,太浅会影响查询效率
  2. 批量插入:如果需要插入大量点,考虑批量插入后重建四叉树
  3. 重用四叉树:对于动态数据,考虑重用四叉树而非频繁创建销毁
  4. 选择合适的节点容量:根据数据分布调整每个节点的最大点数

实际应用场景

  1. 游戏开发:用于碰撞检测、可见性判断
  2. 地理信息系统:空间索引和邻近查询
  3. 图像处理:区域划分和处理
  4. 模拟系统:粒子系统或物理引擎的空间优化

go-rquad 是一个轻量级但功能强大的四叉树实现,通过合理使用可以显著提高空间查询的效率。

回到顶部