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

news2024/11/26 10:23:42

下文参考: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.coloradmin.cn/o/1565403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云计算对象存储服务

对象存储服务&#xff08;OSS&#xff09;中的存储桶(Bucket)叫做‘OBS桶 存储桶&#xff08;Bucket&#xff09;&#xff1a;存储桶式对象存储服务中用于存储对象的基本容器&#xff0c;类似于文件系统中的文件夹。每个存储桶具有唯一的名称&#xff0c;并且可以在桶中存储任…

用于自动驾驶,无人驾驶领域的IMU六轴陀螺仪传感器:M-G370

用于自动驾驶,无人驾驶的IMU惯导模块六轴陀螺仪传感器:M-G370。自2020年&#xff0c;自动驾驶,无人驾驶已经迎来新突破&#xff0c;自动驾驶汽车作为道路交通体系的一员&#xff0c;要能做到的就是先判断周边是否有障碍物&#xff0c;自身的行驶是否会对其他交通参与成员产生危…

STM32F4系列单片机的定时器讲解和计数器,PWM信号输出,PWM信号捕获的实现对电机进行控制和监测功能

1.定时器功能介绍&#xff1a; 在控制领域里面&#xff0c;我们可以用信号输出定时器&#xff0c;进行PWM的控制&#xff0c;从而达到控制电机的目的&#xff0c;通过输入捕获功能可以用来接收外部的数字信号&#xff0c;用于测量脉冲宽度、频率或周期等。在这里给大家介绍下&…

二维相位解包理论算法和软件【全文翻译-将相位分解为 “非旋转 “和 “旋转 “(2.4)】

2.4 将相位分解为 "非旋转 "和 "旋转 "部分 借用电磁场理论,可以用发散和卷曲来指定矢量场[9][10]。当且仅当矢量函数 F(r)(以及由其描述的场)在整个域 D 中不旋转或无旋转时,我们称之为矢量函数 F(r)、 因此,如果等式 2.30(也是第 2.2 节关于路径…

面对汽车充电桩隐私泄露威胁,应该怎么做?

想必各位车主在第一次扫码或刷卡使用汽车充电桩时&#xff0c;都会出现类似于上图的请求&#xff0c;除了上述的定位权限外&#xff0c;运营商还会索要你的网络权限、相机权限、通知权限、设备信息权限、存储权限、电话权限等。 那么你知道这些权限充电桩获取后到底用于什么吗&…

DELL服务器使用iDRAC升级BIOS等固件版本

前言 正值DELL推出DELL R730XD服务器最新的BIOS固件&#xff08;2.19.0 2024/3/18&#xff09;之际&#xff0c;本人也有合适的时间将手头的服务器BIOS固件进行升级操作。 本文博将DELL R730xd 的iDRAC8版本为例&#xff0c;介绍整个升级过程。其他DELL类型的服务器操作类似&…

黄金票据攻击

黄金票据攻击——域内横向移动技术 一、黄金票据攻击介绍&#xff1a; 黄金票据攻击是一种滥用Kerberos身份认证协议的攻击方式&#xff0c;它允许攻击者伪造域控krbtgt用户的TGT&#xff08;Ticket-Granting Ticket&#xff09;。通过这种方法&#xff0c;攻击者可以生成有效…

【C语言】2048小游戏【附源码】

一、游戏描述&#xff1a; 2048是一款数字益智类游戏&#xff0c;玩家需要使用键盘控制数字方块的移动&#xff0c;合并相同数字的方块&#xff0c;最终达到数字方块上出现“2048”的目标。 每次移动操作&#xff0c;所有数字方块会朝着指定方向同时滑动&#xff0c;并在靠近边…

supersqli-攻防世界

题目 加个报错 1 and 11 #没报错判断为单引号字符注入 爆显位 1 order by 2#回显正常 1 order by 3#报错 说明列数是2 尝试联合查询 -1 union select 1,2# 被过滤了 return preg_match("/select|update|delete|drop|insert|where|\./i",$inject); select|update|d…

vCenter Server出现no healthy upstream的解决方法

https://blog.51cto.com/wangchunhai/4907250 访问vCenter 7.0 地址后&#xff0c;页面出现“no healthy upstream”,无法正常登录vCenter&#xff0c;重启后依旧如此&#xff0c;该故障的前提是没有对vCenter做过任何配置&#xff0c;如下图所示。 尝试登录"VMware vCen…

快速排渣器与矿用快速除渣器

你不想虚度光阴&#xff0c;你想用自己的努力&#xff0c;书写自己人生的精彩&#xff0c;那么&#xff0c;你脚下的路不能回头&#xff0c;铺满掌声和鲜花的人生路上&#xff0c;脚下没有那么平坦&#xff0c;或是泥泞&#xff0c;或是陷阱&#xff0c;别管多么艰难的路程&…

前端跨页面通信方案介绍

在浏览器中&#xff0c;我们可以同时打开多个Tab页&#xff0c;每个Tab页可以粗略理解为一个“独立”的运行环境&#xff0c;即使是全局对象也不会在多个Tab间共享。然而有些时候&#xff0c;我们希望能在这些“独立”的Tab页面之间同步页面的数据、信息或状态。这就是本文说说…

ubuntu20.04 运行 lio-sam 流程记录

ubuntu20.04 运行 lio-sam 一、安装和编译1.1、安装 ROS11.2、安装 gtsam1.3、安装依赖1.4、下载源码1.5、修改文件1.6、编译和运行 二、官方数据集的运行2.1、casual_walk_2.bag2.2、outdoor.bag、west.bag2.3、park.bag 三、一些比较好的参考链接 记录流程&#xff0c;方便自…

新质生产力崛起,运营商前端运营如何跃升

“新质生产力”一个当前的热搜高频词&#xff0c;今年还被首次写进政府工作报告&#xff0c;是2024年十大工作任务的首位。那么什么是“新质生产力”&#xff1f;它对于我们的生活、学习、工作及未来发展有什么影响呢&#xff1f;今天小宝就抛砖引玉来讲一讲“新质生产力”对于…

数字化服务升级:数字乡村改善农民生活质量

随着信息技术的迅猛发展&#xff0c;数字化浪潮已经深入社会的各个角落&#xff0c;为人们的生活带来了翻天覆地的变化。在乡村地区&#xff0c;数字化服务的升级正在逐步改变农民的生活方式&#xff0c;提高他们的生活质量。本文将围绕数字化服务升级&#xff0c;探讨数字乡村…

【精心整理】2024最新Web安全攻防教程资料PPT大合集

以下是学习资料目录&#xff0c;如需要下载&#xff0c;请您前往星球查阅和获取&#xff1a;https://t.zsxq.com/18YLzWtDI 1、Sqlmap Tamper脚本编写介绍-01.pptx 2、Tamper脚本分析&#xff08;支持所有类型数据库的Tamper脚本&#xff09;-01.pptx 3 、Sqlmap Tamper脚本分析…

一款强大的逆向分析工具-Ghidra

工具介绍 Ghidra 是由美国国家安全局&#xff08;NSA&#xff09;研究部门开发的软件逆向工程&#xff08;SRE&#xff09;套件&#xff0c;用于支持网络安全任务。包括一套功能齐全的高端软件分析工具&#xff0c;使用户能够在各种平台(Windows、Mac OS和Linux)分析编译后的代…

Flutter应用发布流程详解:从开发到上架一站式指南

引言 Flutter是一款由Google推出的跨平台移动应用开发框架&#xff0c;其强大的性能和流畅的用户体验使其备受开发者青睐。然而&#xff0c;开发一款应用只是第一步&#xff0c;将其成功上架到苹果商店才是实现商业目标的关键一步。本文将详细介绍如何使用Flutter将应用程序上…

Qt使用opencv打开摄像头

1.效果图 2.代码 #include "widget.h"#include <QApplication>#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp>#include <QImage> #include <QLabel> #incl…

为什么蓝牙信道探测将引领高精度定位服务?

从蓝牙耳机到智能家居设备&#xff0c;蓝牙技术因其成熟的音频流和数据传输功能已成为我们生活中的一部分。一项新技术——蓝牙信道探测&#xff08;Bluetooth Channel Sounding&#xff09;正向高精度定位服务市场迈进。 本文信驰达&#xff08;RF-star&#xff09;将介绍蓝牙…