空间数据结构(四叉树,八叉树,BVH树,BSP树,K-d树)

下文参考:https://www.cnblogs.com/KillerAery/p/10878367.html

游戏编程知识课程 - 四分树(quadtree)_哔哩哔哩_bilibili 

利用空间数据结构可以加速计算,是重要的优化思想。空间数据结构常用于场景管理,渲染,物理,游戏逻辑

四叉树 / 八叉树 (Quadtree / Octree )

四叉树索引的基本思想是将地理空间递归划分成不同层次的树结构

可以将已知范围的空间等分成四个相等的子空间,直到树的层次达到一定深度或者满足某种要求

什么是四叉树:

  • 一个四叉树结构是一个存储单元(点,线,或者三角形)
  • 每个节点只能有四个子节点或者没有子节点
  • 每个节点表示了一块区域,他的四个孩子节点表示这个区域的四分
  • 四叉树是二维的概念
  • 每个节点在它们的区域中存储一些objects,有一个最大存储值(对于整颗树来说都是一样的,每个块存储的数量都是根据这个值来调整,如果一个节点存储的值多于最大存储值,就把这个节点四分)
  • 有一个最大深度值(就是树的深度)

怎么把一个物体添加到 Quadtree 里面

当一个quadtree 第一次初始化时,它只有根节点(表现的是整个空间)

从根节点开始,递归执行下面的方法

如果这个节点是叶子节点:

  • 如果递归已经到达最大的深度或者没有达到最大容量,就把这个物体添加到当前节点
  • 否则就把这个区域四分,创建四个子节点,用每个子节点对应一块。然后把这个程序再跑一遍

如果节点不是叶子节点

  • 如果这个物体只是覆盖现在区域的一个四分之一,在这个节点的四分之一子节点上运行这个程序。否则把这个物体加到现在的节点(因为一个点不能同时被不同的节点存储)

怎么去找我们想要的物体

如果给了一个盒子,我们需要去找所有与这个盒子相交的物体

从根节点开始然后递归调用以下方法:

  • 遍历当前节点里面的所有物体,如果与盒子相交,就把这个物体添加到 intersected object list
  • 如果当前节点不是叶子节点:遍历每个孩子节点,如果盒子和这个区域相交,再孩子节点上执行这个方法

如何 remove 物体从 Quadtree

从根节点开始递归调用下面的方法:

如果节点是叶子节点:

  • 如果这个节点有这个物体,就把这个物体移除

如果节点不是叶子节点:

 --  如果这个物体只覆盖当前节点区域的一个四分区域

  • 使用这个四分区域对应的子节点运行这个程序,然后优化一下

--  否则如果这个节点有这个物体就从当前节点移除这个物体

如何去优化节点

如果当前节点的孩子节点不是叶子节点,就 return

计算当前节点以及它的四个孩子节点的所有物体数量,如果小于最大容量,就摧毁掉孩子节点然后将物体都添加到当前节点上

代码来自 chatgpt

using System;
using System.Collections.Generic;// 表示一个矩形区域
public class Rectangle
{public float xMin, xMax, yMin, yMax;public Rectangle(float x, float y, float width, float height){xMin = x;xMax = x + width;yMin = y;yMax = y + height;}// 判断点是否在矩形内部public bool Contains(float x, float y){return x >= xMin && x <= xMax && y >= yMin && y <= yMax;}
}// 表示四叉树节点
public class QuadTreeNode<T>
{public Rectangle boundary; // 节点代表的矩形区域public List<T> objects; // 存储在当前节点中的对象public QuadTreeNode<T>[] children; // 子节点public QuadTreeNode(Rectangle rect){boundary = rect;objects = new List<T>();children = new QuadTreeNode<T>[4];}
}// 四叉树类
public class Quadtree<T>
{private QuadTreeNode<T> root; // 根节点private int maxObjectsPerNode = 10; // 每个节点最多存储的对象数量private int maxLevels = 5; // 最大递归深度public Quadtree(Rectangle bounds){root = new QuadTreeNode<T>(bounds);}// 插入对象public void Insert(T obj, float x, float y){Insert(obj, root, x, y, 0);}// 递归插入对象private void Insert(T obj, QuadTreeNode<T> node, float x, float y, int level){if (node.children[0] != null) // 如果有子节点{int index = GetIndex(node, x, y); // 获取对象应该插入的子节点索引if (index != -1) // 如果对象属于子节点{Insert(obj, node.children[index], x, y, level + 1);return;}}node.objects.Add(obj); // 将对象添加到当前节点if (node.objects.Count > maxObjectsPerNode && level < maxLevels) // 如果当前节点对象数量超过阈值且还可以递归分割{if (node.children[0] == null) // 如果没有子节点,创建子节点{SplitNode(node);}// 将当前节点的对象重新分配到子节点中int i = 0;while (i < node.objects.Count){T item = node.objects[i];int index = GetIndex(node, x, y);if (index != -1){node.objects.RemoveAt(i);node.children[index].objects.Add(item);}else{i++;}}}}// 分割节点private void SplitNode(QuadTreeNode<T> node){float subWidth = node.boundary.xMax - node.boundary.xMin;float subHeight = node.boundary.yMax - node.boundary.yMin;float xMid = node.boundary.xMin + (subWidth / 2);float yMid = node.boundary.yMin + (subHeight / 2);// 创建四个子节点node.children[0] = new QuadTreeNode<T>(new Rectangle(xMid, yMid, subWidth, subHeight));node.children[1] = new QuadTreeNode<T>(new Rectangle(node.boundary.xMin, yMid, subWidth, subHeight));node.children[2] = new QuadTreeNode<T>(new Rectangle(node.boundary.xMin, node.boundary.yMin, subWidth, subHeight));node.children[3] = new QuadTreeNode<T>(new Rectangle(xMid, node.boundary.yMin, subWidth, subHeight));}// 获取对象应该插入的子节点索引private int GetIndex(QuadTreeNode<T> node, float x, float y){int index = -1;float xMid = node.boundary.xMin + (node.boundary.xMax - node.boundary.xMin) / 2;float yMid = node.boundary.yMin + (node.boundary.yMax - node.boundary.yMin) / 2;bool topQuadrant = (y >= yMid);bool bottomQuadrant = (y < yMid);bool leftQuadrant = (x < xMid);bool rightQuadrant = (x >= xMid);if (leftQuadrant){if (topQuadrant){index = 0;}else if (bottomQuadrant){index = 2;}}else if (rightQuadrant){if (topQuadrant){index = 1;}else if (bottomQuadrant){index = 3;}}return index;}// 查询区域内的对象public List<T> QueryRange(Rectangle range){List<T> result = new List<T>();QueryRange(root, range, result);return result;}// 递归查询区域内的对象private void QueryRange(QuadTreeNode<T> node, Rectangle range, List<T> result){if (!node.boundary.Contains(range.xMin, range.yMin) && !node.boundary.Contains(range.xMax, range.yMax)){return;}foreach (T obj in node.objects){if (range.Contains(range.xMin, range.yMin)){result.Add(obj);}}if (node.children[0] != null){foreach (QuadTreeNode<T> child in node.children){QueryRange(child, range, result);}}}
}// 示例使用
class Program
{static void Main(string[] args){Rectangle bounds = new Rectangle(0, 0, 100, 100);Quadtree<int> quadtree = new Quadtree<int>(bounds);// 插入对象quadtree.Insert(1, 10, 10);quadtree.Insert(2, 20, 20);quadtree.Insert(3, 30, 30);quadtree.Insert(4, 40, 40);quadtree.Insert(5, 50, 50);quadtree.Insert(6, 60, 60);quadtree.Insert(7, 70, 70);quadtree.Insert(8, 80, 80);quadtree.Insert(9, 90, 90);quadtree.Insert(10, 100, 100);// 查询区域内的对象Rectangle queryRange = new Rectangle(25, 25, 30, 30);List<int> result = quadtree.QueryRange(queryRange);foreach (int obj in result){Console.WriteLine("对象:" + obj);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/579022.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java】LinkedList模拟实现

目录 整体框架IMyLinkedList接口IndexNotLegalException异常类MyLinkedList类成员变量(节点信息)addFirst(头插)addLast(尾插)在指定位置插入数据判断是否存在移除第一个相等的节点移除所有相等的节点链表的长度打印链表释放回收链表 整体框架 IMyLinkedList接口 这个接口用来…

QT+Opencv+yolov5实现监测

功能说明&#xff1a;使用QTOpencvyolov5实现监测 仓库链接&#xff1a;https://gitee.com/wangyoujie11/qt_yolov5.git git本仓库到本地 一、环境配置 1.opencv配置 将OpenCV-MinGW-Build-OpenCV-4.5.2-x64文件夹放在自己的一个目录下&#xff0c;如我的路径&#xff1a; …

【RedHat】使用cron安排周期性任务——周期性创建用户实例

cron用来管理周期性重复执行的任务调度&#xff0c;非常适合日常系统维护工作。计划任务分为系统的计划任务和用户自定义的计划任务。 cron服务每分钟都检查/etc/crontab文件、/etc/cron.d目录和/var/spool/cron目录中的变化。/var/spool/cron目录下的任务需要通过crontab -e 命…

SEH异常之编译器原理探究(1)

_try_except原理 调用_except_handle3这个异常处理函数&#xff0c;这里并不是每个编译器的异常处理函数都是相同的&#xff0c;然后存入结构体&#xff0c;将esp的值赋给fs:[0]&#xff0c;再就是提升堆栈的操作 每个使用 _try _except的函数&#xff0c;不管其内部嵌套或反复…

系统资源紧缺?不用担心,Linux命令和Shell脚本帮你搞定

在之前的文章中介绍了如何申请AWS免费主机使用WordPress搭建自己的个人网站&#xff0c;但是在我使用过程中发现了一个问题&#xff0c;由于陆陆续续安装了好几个插件&#xff0c;偶尔在访问网站时会出现数据库连接出错的异常情况&#xff0c;导致页面无法访问。稍等一会儿刷新…

基于Springboot+vue的鲜花销售商城网站

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;鲜花销售商城当然也不能排除在外。鲜花销售商城是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#x…

vue-v-for遍历index与id

一.遍历列表key的作用&#xff08;index作为key&#xff09; 虚拟DOM上有key,是虚拟的&#xff0c;但是真实DOM上没有&#xff0c;key是Vue内部的 当使用index作为key的时候&#xff0c;Vue会根据初识数据生成一个初始的虚DOM&#xff0c; 然后在页面上映射出真实DOM 如果向数据…

C#_事件_多线程(基础)

文章目录 事件通过事件使用委托 多线程(基础)进程:线程: 多线程线程生命周期主线程Thread 类中的属性和方法创建线程管理线程销毁线程 事件 事件&#xff08;Event&#xff09;本质上来讲是一种特殊的多播委托&#xff0c;只能从声明它的类中进行调用,基本上说是一个用户操作&…

kubernetes(K8S)学习(五):K8S进阶(Lifecycle......偏理论)

K8S进阶&#xff08;Lifecycle......偏理论&#xff09; 一、Pod进阶学习之路1.1 Lifecycle1.2 重启策略1.3 静态Pod1.4 健康检查1.5 ConfigMap1.6 Secret1.7 指定Pod所运行的Node 二、Controller进阶学习之路2.1 Job & CronJob2.2 StatefulSet2.3 DaemonSet2.4 Horizontal…

039—pandas 不规则表头转换为规整DataFrame

使用步骤 读入数据 代码如下&#xff08;示例&#xff09;&#xff1a; import pandas as pd import numpy as np df pd.DataFrame({0: [姓名, 性别],1: [张三, 男],2: [年龄,np.nan],3: [18,np.nan]}) dfdf.values.reshape([4,2])r len(df.columns)(pd.DataFrame(df.valu…

MQTT.fx连接新版OneNet平台的一些问题

对于使用通信主题publish给OneNET时&#xff0c;如图所示&#xff1a; 但是点击Publish后&#xff0c;出现了Broker connection lost的问题 原因在于&#xff1a;新版OneNET和旧版OneNET的通信主题不一致了&#xff0c;查阅文档获知&#xff0c;格式如下&#xff1a; $sys/{p…

家庭网络防御系统搭建-配置流量镜像到NDR系统

由于需要将家庭网络中的全部流量送到NDR分析系统进行分析&#xff0c;因此需要一个具备流量镜像功能的交换机或者路由器。在前面文章所提及的家庭网络架构中&#xff0c;需要一台交换机即可拷贝东西向流量以及南北向流量。当然如果家庭中的路由器或者其他设备具备交换机镜像功能…