空间数据结构(四叉树,八叉树,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);
        }
    }
}

遗憾,执行出来的结果是错误的

    将就看个大概吧

  八叉树

松散四叉树/八叉树 :减少边界问题

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jamba: A Hybrid Transformer-Mamba Language Model

Jamba: A Hybrid Transformer-Mamba Language Model 相关链接&#xff1a;arXiv 关键字&#xff1a;hybrid architecture、Transformer、Mamba、mixture-of-experts (MoE)、language model 摘要 我们介绍了Jamba&#xff0c;一种新的基于新颖混合Transformer-Mamba混合专家&am…

VMware配置环境(安装运行问题)及系列dns端口网络类型IP远程连接学习之(详谈8000字)

安装vmware快速配置步骤 下载VMware安装包 在下载好VMware安装包之后双击运行 接受条款 关闭VMware自动更新 勾选快捷键方式 安装VMware安装 输入许可证&#xff08;有需要私信小编&#xff09; 安装完成 重启电脑即可 最终成功界面: 安装Linux系统 创建虚拟机 选择…

工业互联网网关软件分析与设计

一、 案例软件分析 一、总体目标 工业互联网是新一代信息技术与制造业深度融合形成的新兴业态和应用模式&#xff0c;其发展前景广阔。工业互联网网关是将各采集监测点的数据通过无线或有线传感网络进行数据汇集&#xff0c;进行统一有效的监管。在工业互联网体系架构中&…

聚焦丨酷雷曼亮相文旅虚拟现实应用推广活动

图片来源&#xff1a;虚拟现实与元宇宙产业联盟XRMA 2024年3月21日&#xff0c;由文化和旅游部产业发展司主办的数字赋能文旅场景建设行动——文化和旅游虚拟现实应用推广交流活动在北京市石景山区首钢园举办。 酷雷曼市场总监刘方磊出席活动&#xff0c;并在活动现场展示酷雷…

【云呐】固定资产盘点报告表怎么填

报告表以表述清晰为主,避免繁琐,重要数据及问题使用表格形式展示。通过签字对报告负责认同度。内容应全面反映本次盘点,提供参考依据。一、标题 包含单位名称、报告期间等基本信息二、前言 概括本次盘点的目的和任务签署三、盘点范围与时间 明确盘点的固定资产项目和时…

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

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 一、游戏描述&#xff1a; 2048是一款数字益智类游戏&#xff0c;玩家需要使用键盘控制数字方块的移动&#xff0c;合并相同数字的方块&#xff0c;最终达到数字方块上出现“2048”的目标。 每次移动操作&#xff0c;所…

C++心决之内联函数+auto关键字+指针空值

目录 7.内联函数 7.1 概念 7.2 特性 8. auto关键字(C11) 8.1 类型别名思考 8.2 auto简介 8.3 auto的使用细则 8.4 auto不能推导的场景 9. 基于范围的for循环(C11) 9.1 范围for的语法 9.2 范围for的使用条件 10. 指针空值nullptr(C11) 10.1 C98中的指针空值 7.内联…

RK3568驱动指南|第十四篇 单总线-第165章DS18B20驱动使用ioctl读取分辨率

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

门户系统商城模块

商城系统&#xff1a;快递商品本地团购到店核销购物场景全覆盖&#xff0c;全新商销解决方案 商城系统是指一套用于构建和运营电商平台的软件系统&#xff0c;可以帮助企业快速搭建网上商城&#xff0c;实现商品销售、订单管理、客户服务等功能。 商城系统的功能&#xff1a;…

蓝桥杯-dfs搜索模板题(二)

蓝桥杯-dfs搜索模板题&#xff08;二&#xff09; P1683 入门P1596[USACO10OCT] Lake Counting S1114 棋盘 acwingP1025 [NOIP2001 提高组] 数的划分P1019 [NOIP2000 提高组] 单词接龙结语 P1683 入门 这道题没有回溯的必要&#xff0c;重复走也不计数。最开始的部分要补上。 …

UTAustin最新提出!无相机姿态40秒重建3DGS方法

作者&#xff1a;小柠檬 | 来源&#xff1a;3DCV 在公众号「3DCV」后台&#xff0c;回复「原论文」可获取论文pdf 添加微信&#xff1a;dddvision&#xff0c;备注&#xff1a;3D高斯&#xff0c;拉你入群。文末附行业细分群 详细内容请关注3DCV 3D视觉精品课程&#xff1a;…

lombok与idea版本不兼容问题解决

lombok与idea版本不兼容问题&#xff0c;可以选择更改lombok版本&#xff1b;也可以选择加配置来解决此问题 1、选择 setting->Build,Execution,Deployment->compiler 2、在 Shared build process VM options中加入如下配置&#xff0c;即可解决此问题 -Djps.track.ap.…

uniapp,文字超出几行显示省略号...,展开显示更多

效果图&#xff1a; 代码&#xff1a; <template><view class"text-container"><text class"text-content" click"showDetail">{{ text }}</text><text v-if"showMore" class"view-detail" cli…

Redis 全景图(2)---- 关于 Redis 的三“高”

前言 我们继续写第一篇文章没写完的。其实我也不想将我写的一篇 Redis 文章分成几篇中短文来写&#xff0c;但是没办法&#xff0c;我一次写个1万字&#xff0c;会限流&#xff0c;所以将就一下吧。 上篇文章我用了 Redis 的6大模块这个思路来描绘我脑子中的 Redis。其实这6大…

WPF-基础及进阶扩展合集(持续更新)

目录 一、基础 1、GridSplitter分割线 2、x:static访问资源文件 3、wpf触发器 4、添加xaml资源文件 5、Convert转换器 6、多路绑定与多路转换器 二、进阶扩展 1、HierarchicalDataTemplate 2、XmlDataProvider从外部文件获取源 3、TextBox在CellTemplate中的焦点问题…

这些策略助力打造多元化、公平和包容性招聘流程

多样性、公平和包容(DEI)是企业招聘员工的最佳策略。顾名思义&#xff0c;DEI描述了三个关键组成部分: “多元化”&#xff0c;这取决于吸引、招聘、雇佣和留住多元化的员工队伍; “公平”部分是指确保不歧视和平等就业机会; “包容”要求建立一个尊重、支持和包容的环境&am…

【学习】兼容性测试为何如此重要

兼容性测试是软件测试中非常重要的一环&#xff0c;旨在确保软件在不同的平台、浏览器、操作系统等环境下能够正常运行&#xff0c;并且不会出现兼容性问题。本文将介绍兼容性测试的概念、重要性、实施步骤及实践案例&#xff0c;帮助读者更好地理解兼容性测试在软件开发中的重…

【解决问题】排查linux手动删除文件,但是文件标记为deleted,资源未释放

背景&#xff1a; 生产环境我们把程序生成的数据文件手动删除后&#xff0c;但是空间并没有释放&#xff0c;导致硬盘被占用&#xff0c;不够用 问题排查&#xff1a; 1.查看占用文件状态 使用命令&#xff1a; lsof | grep deleted 查看 文件已经删除了&#xff0c;但是都是…

人事管理系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)请假加班招聘考勤

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读300套最新项目持续更新中..... 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含ja…

idea编译一直失败处理

切换分支的时候&#xff0c;明明代码正常&#xff0c;但是编译的时候一直失败。。。。特别是多个项目的时候&#xff0c;经常失败。 配置 -Djps.track.ap.dependenciesfalse idea默认是增量编译&#xff0c;设置这个false之后就从头开始编译了。 设置之后&#xff0c;点击编译&…