【Godot4.3】图形碰撞相关函数库ShapeTests

news2024/11/24 20:27:07

概述

最近积累了一些图形重叠检测,以及求图形的轴对齐包围盒Rect2,还有求Rect2的外接圆等函数。感觉可以作为一个单独的函数库,提供日常的使用,所以汇总成了ShapeTests

注意:函数名和写法可能会不断改进。

代码

# ==============================================
# ShapeTests
# 一个用于求取基础图形轴对齐包围盒、外接圆以及重叠检测的函数库
# 巽星石 v4.3.stable.steam [77dcf97d8]
# 202410215:02:18
# 202410221:52:23
# ==============================================
class_name ShapeTests
# =======================Rect2 =======================
# 获取点集的轴对齐包围盒(Rect2static func get_points_rect2(points:PackedVector2Array) -> Rect2:
	var rect = Rect2()
	var xs = Array(points).map(func(num):return num.x)
	var ys = Array(points).map(func(num):return num.y)
	rect.position = Vector2(xs.min(),ys.min())
	rect.end = Vector2(xs.max(),ys.max())
	return rect

# 获取圆的Rect2
static func get_circle_rect2(center:Vector2,radius:float) -> Rect2:
	var rect = Rect2()
	rect.position = center - Vector2.ONE * radius
	rect.size = Vector2.ONE * radius * 2
	return rect
# =======================Rect2的外接圆 =======================
class Circle:
	var center:Vector2
	var radius:float	

# 返回 Rect2 的外接圆
static func get_rect2_out_circle(rect2:Rect2) -> Circle:
	var c = Circle.new()
	c.center = rect2.get_center()
	c.radius = (rect2.get_center() - rect2.position).length()
	return c

# 返回 points 的外接圆
static func get_points_out_circle(points:PackedVector2Array) -> Circle:
	var rect:Rect2  = get_points_rect2(points)
	return get_rect2_out_circle(rect)

# ======================= 判断图形是否完全在Rect2=======================
# 判断圆是否完全在 rect2 内
static func is_circle_in_rect2(center:Vector2,radius:float,rect2:Rect2) -> bool:
	var rect1 = get_circle_rect2(center,radius)
	return is_rect_in_rect2(rect1,rect2)

# 判断 rect1 是否完全在 rect2 内
static func is_rect_in_rect2(rect1:Rect2,rect2:Rect2) -> bool:
	return rect2.has_point(rect1.position) and rect2.has_point(rect1.end)

# ======================= 图形间的重叠检测 =======================

# 判断两个圆形是否重叠
static func is_circle_circle_overlap(c1:Vector2,r1:float,c2:Vector2,r2:float):
	var dis = c1.distance_to(c2)  # 距离
	return dis <= r1 + r2 

# 获取两个圆形重叠部分的向量
static func get_circle_circle_overlap(c1:Vector2,r1:float,c2:Vector2,r2:float):
	var dis = c1.distance_to(c2)  # 距离
	var dir = c1.direction_to(c2) # 方向
	var min_dis = r1 + r2 
	var over_dis := 0.0   # 重叠部分的距离
	if dis < min_dis:
		over_dis = min_dis - dis
	return dir * over_dis

# 返回矩形四个角点
static func get_rect_points(rect:Rect2) -> PackedVector2Array:
	var pots:PackedVector2Array
	var pos = rect.position;	var end = rect.end;	var size = rect.size
	pots = [	pos,pos + Vector2(size.x,0),	end,end - Vector2(size.x,0)]
	return pots

# 判断rect2和rect1是否有重叠
static func is_rect_rect_overlap(rect1:Rect2,rect2:Rect2) -> bool:
	var points = Array(get_rect_points(rect2))
	# 矩形1的任何一个点在矩形2var bol = points.any(func(p):
		return rect1.has_point(p)
	)
	return bol

# 返回rect2和rect1是重叠的点集合
static func get_rect_rect_overlap_points(rect1:Rect2,rect2:Rect2) -> PackedVector2Array:
	var pots:PackedVector2Array
	var points = Array(get_rect_points(rect2))
	# 矩形1的任何一个点在矩形2for p in points:
		if rect1.has_point(p):
			pots.append(p)
	return pots

# 获取Rect24分矩形集合
static func get_rect_4_parts(rect:Rect2) -> Array[Rect2]:
	var rects:Array[Rect2]
	var c = rect.get_center()
	var half_size = rect.size/2.0
	var r1 = Rect2(rect.position,half_size)
	rects.append(r1)
	rects.append(Transform2D(0,Vector2(half_size.x,0)) * r1)
	rects.append(Transform2D(0,half_size) * r1)
	rects.append(Transform2D(0,Vector2(0,half_size.y)) * r1)
	return rects

# 获取两个矩形重叠部分的向量
static func get_rect_rect_overlap(rect1:Rect2,rect2:Rect2) -> PackedVector2Array:
	var c1 = rect1.get_center()
	var c2 = rect2.get_center()
	var vec12 = c2 - c1
	
	
	var half_size = (rect1.size + rect2.size)/2.0
	
	
	var ove_points = get_rect_rect_overlap_points(rect1,rect2)  # rect2进入rect1的点集合
	
	var points2 = Array(get_rect_points(rect2))
	var points1 = Array(get_rect_points(rect1))
	var len = 0
	var vec = Vector2()           # 方向
	var pos = Vector2()           # 位置
	
	var sub_rects = get_rect_4_parts(rect1) # 将rect1划分为四个象限
	
	match ove_points.size():
		4:  # rect2完全进入rect1
			if sub_rects[0].has_point(c2):
				vec = points1[0] - points2[2]
				pos = points2[2]
			if sub_rects[1].has_point(c2):
				vec = points1[1] - points2[3]
				pos = points2[3]
			if sub_rects[2].has_point(c2):
				vec = points1[2] - points2[0]
				pos = points2[0]
			if sub_rects[3].has_point(c2):
				vec = points1[3] - points2[1]
				pos = points2[1]
		2:  # rect2一侧进入rect1
			var dw = half_size.x - abs(vec12.x)
			var dh = half_size.y - abs(vec12.y)
			
			if ove_points == PackedVector2Array([points2[0],points2[1]]):
				vec = Vector2.DOWN * dh
				pos = points2[0].lerp(points2[1],0.5)
			if ove_points == PackedVector2Array([points2[2],points2[3]]):
				vec = Vector2.UP * dh
				pos = points2[2].lerp(points2[3],0.5)
			if ove_points == PackedVector2Array([points2[0],points2[3]]):
				vec = Vector2.RIGHT * dw
				pos = points2[0].lerp(points2[3],0.5)
			if ove_points == PackedVector2Array([points2[1],points2[2]]):
				vec = Vector2.LEFT * dw
				pos = points2[1].lerp(points2[2],0.5)
		1: # rect2仅一个角进入rect1
			if ove_points == PackedVector2Array([points2[3]]):
				vec = points1[1] - points2[3]
				pos = points2[3]
			if ove_points == PackedVector2Array([points2[2]]):
				vec = points1[0] - points2[2]
				pos = points2[2]
			if ove_points == PackedVector2Array([points2[1]]):
				vec = points1[3] - points2[1]
				pos = points2[1]
			if ove_points == PackedVector2Array([points2[0]]):
				vec = points1[2] - points2[0]
				pos = points2[0]
	return PackedVector2Array([pos,pos + vec]) 

使用测试

获取圆的Rect2

extends Node2D

var c = Vector2(300,300)  # 圆心
var r = 100               # 半径

func _draw() -> void:
	var rect = ShapeTests.get_circle_rect2(c,r) # 获取圆的Rect2
	draw_rect(rect,Color.AQUAMARINE,false,1)    # 绘制圆的Rect2
	draw_circle(c,r,Color.ORANGE_RED,false,1)	# 绘制圆

求点集的Rect2

extends Node2D

var points = Points2D.Vec2Arr("100,200 300,400 200,100")  # 定义点集

func _draw() -> void:
	var rect = ShapeTests.get_points_rect2(points)    # 求点集的Rect2
	draw_rect(rect,Color.AQUAMARINE,false,1)          # 绘制Rect2

	draw_polyline(points,Color.YELLOW_GREEN,1)  # 绘制折线
    # 绘制顶点
	for p in points:
		draw_circle(p,3,Color.ORANGE_RED)

求Rect2的外接圆

extends Node2D

var rect = Rect2(100,100,300,200)  # 定义Rect2

func _draw() -> void:
	var c = ShapeTests.get_rect2_out_circle(rect)            # 求Rect2的外接圆
	draw_circle(c.center,c.radius,Color.ORANGE_RED,false,1)  # 绘制外接圆
	draw_rect(rect,Color.AQUAMARINE,false,1)                 # 绘制Rect2

求任意点集的外接圆

extends Node2D

var points = Points2D.Vec2Arr("100,200 300,400 200,100")

func _draw() -> void:
	var c = ShapeTests.get_points_out_circle(points)
	draw_circle(c.center,c.radius,Color.ORANGE_RED,false,1)
	draw_polyline(points,Color.YELLOW_GREEN,1)
	for p in points:
		draw_circle(p,3,Color.ORANGE_RED)

可以看到,实际上是先求点集的Rect2然后求Rect2的外接圆,这里并不太精确,是因为它是基于Rect2的,但是已经满足无论如何旋转,所有点都始终在外接圆内的要求。

判断圆是否在矩形内

extends Node2D

var rect = Rect2(100,100,900,400)  # 定义边界矩形
var c = Vector2(300,300)  # 圆心
var r = 100               # 半径


func _process(delta: float) -> void:
	c = get_global_mouse_position() # 用鼠标位置更新圆的位置
	queue_redraw()

func _draw() -> void:
	draw_rect(rect,Color.AQUAMARINE,false,1)      # 绘制边界矩形
	if ShapeTests.is_circle_in_rect2(c,r,rect):
		draw_circle(c,r,Color.ORANGE_RED,false,1)  # 绘制圆
	else:
		draw_circle(c,r,Color.ORANGE_RED,true)  # 绘制圆

判断矩形是否在矩形内

extends Node2D

var rect = Rect2(100,100,900,400)  # 定义边界矩形
var rect2 = Rect2(200,200,100,80)  # 小矩形

func _process(delta: float) -> void:
	# 用鼠标位置更新矩形的位置
	rect2.position = get_global_mouse_position() - rect2.size/2.0
	queue_redraw()

func _draw() -> void:
	draw_rect(rect,Color.AQUAMARINE,false,1)       # 绘制边界矩形
	if ShapeTests.is_rect_in_rect2(rect2,rect):
		draw_rect(rect2,Color.ORANGE_RED,false,1)  # 绘制非填充矩形
	else:
		draw_rect(rect2,Color.ORANGE_RED,true)     # 绘制填充矩形

注意这里是判断小矩形是否完完全全在大矩形内:

圆与圆的重叠向量

extends Node2D

var c1 = Vector2(300,300)  # 圆心
var r1 = 100               # 半径

var c2 = Vector2(300,300)  # 圆心
var r2 = 60               # 半径

# 箭头
var arrow:PackedVector2Array = [
	Vector2.LEFT.rotated(deg_to_rad(-30)) * 5,
	Vector2(),
	Vector2.LEFT.rotated(deg_to_rad(30)) * 5,
]

func _process(delta: float) -> void:
	# 用鼠标位置圆2的圆心
	c2 = get_global_mouse_position()
	queue_redraw()

func _draw() -> void:
	var overlap = ShapeTests.get_circle_circle_overlap(c1,r1,c2,r2)
	
	
	var a = c1.distance_to(c2) - r2
	
	draw_circle(c1,r1,Color.YELLOW_GREEN,false,1)  # 绘制圆1
	draw_circle(c2,r2,Color.YELLOW_GREEN,false,1)  # 绘制圆2
	
	var p1 = c1.move_toward(c2,a)
	draw_line(p1,p1+overlap,Color.ORANGE_RED,1)    # 绘制重叠向量
	draw_polyline( # 绘制箭头
		Transform2D(c1.direction_to(c2).angle(),p1+overlap) * arrow,
		Color.ORANGE_RED,1
	)

重叠向量是我自己发明的一个词,是能够表现一个图形与另一个图形重叠程度的向量。可以使用反向重叠向量移动被动物体,或者使用重叠向量将移动物体排出被动物体。

矩形与矩形的重叠检测

判断一个举行与另一个矩形是否重合

矩形A的4个点,有任意一个点在另一个矩形B中,就认为矩形A与矩形B重叠。

# 判断rect2和rect1是否有重叠
static func is_rect_rect_overlap(rect1:Rect2,rect2:Rect2) -> bool:
	var pos = rect2.position
	var end = rect2.end
	var size = rect2.size
	var points = [
		pos,pos + Vector2(size.x,0),
		end,end - Vector2(size.x,0)
	]
	# 矩形1的任何一个点在矩形2var bol = points.any(func(p):
		return rect1.has_point(p)
	)
	return bol

测试:

extends Node2D

var rect1 = Rect2(100,100,400,350)
var rect2 = Rect2(200,200,100,80)

# 箭头
var arrow:PackedVector2Array = [
	Vector2.LEFT.rotated(deg_to_rad(-30)) * 5,
	Vector2(),
	Vector2.LEFT.rotated(deg_to_rad(30)) * 5,
]

func _process(delta: float) -> void:
	# 用鼠标位置控制Rect2的位置
	rect2.position = get_global_mouse_position() - rect2.size/2.0
	queue_redraw()

func _draw() -> void:
	var overlap = ShapeTests.get_rect_rect_overlap(rect1,rect2)
	var c1 = rect1.get_center()
	var c2 = rect2.get_center()
	
	
	for rect in ShapeTests.get_rect_4_parts(rect1):
		draw_rect(rect,Color.YELLOW_GREEN,false,1)  # 绘制矩形1
	
	#draw_rect(rect1,Color.YELLOW_GREEN,false,1)  # 绘制矩形1
	draw_rect(rect2,Color.YELLOW_GREEN,false,1)  # 绘制矩形2

	draw_line(overlap[0],overlap[1],Color.ORANGE_RED,1)    # 绘制重叠向量
	draw_polyline( # 绘制箭头
		Transform2D(overlap[0].direction_to(overlap[1]).angle(),overlap[1]) * arrow,
		Color.ORANGE_RED,1
	)

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

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

相关文章

基于SSM的北京冬奥会志愿者服务系统

文未可获取一份本项目的java源码和数据库参考。 本课题国内外研究现状 当前&#xff0c;国外志愿者服务活动开展的十分活跃。志愿服务正以其突出的社会效益受到越来越多国家政府的重视。许多国家的志愿服务活动起步早、规模大&#xff0c;社会效益好。他们在国内有广泛的群众…

第四届生物医学与智能系统国际学术会议(IC-BIS 2025)

在线投稿&#xff1a;学术会议-学术交流征稿-学术会议在线-艾思科蓝 2025年第四届生物医学与智能系统国际学术会议&#xff08;IC-BIS 2025&#xff09; 将于2025年4月11-13日在意大利隆重举行。 该会议旨在汇集全球学术界和工业界的研究人员、专家和从业人员&#xff0c;共…

C(十)for循环 --- 黑神话情景

前言&#xff1a; "踏过三界宝刹&#xff0c;阅过四洲繁华。笑过五蕴痴缠&#xff0c;舍过六根牵挂。怕什么欲念不休&#xff0c;怕什么浪迹天涯。步履不停&#xff0c;便是得救之法。" 国际惯例&#xff0c;开篇先喝碗鸡汤。 今天&#xff0c;杰哥写的 for 循环相…

android Activity生命周期

android 中一个 activity 在其生命周期中会经历多种状态。 您可以使用一系列回调来处理状态之间的转换。下面我们来介绍这些回调。 onCreate&#xff08;创建阶段&#xff09; 初始化组件&#xff1a;在这个阶段&#xff0c;Activity的主要工作是进行初始化操作。这包括为Ac…

【Bug】STM32F1的PB3和PB4无法正常输出

Bug 使用标准库配置STM32F103C8T6的PB3和PB4引脚输出控制LED灯时&#xff0c;发现引脚电平没有变化无法正常输出高低电平&#xff0c;配置代码如下&#xff1a; GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); GPIO_InitStruc…

Maven项目管理入门:POM文件详解与依赖管理

目录 1、pom配置详解 2、依赖管理 2.1 Maven坐标 2.2 依赖导入 1、使用IDEA工具导入&#xff1a; 2、从远程仓库中获取坐标 3、maven插件 maven是一个项目管理工具&#xff0c;其中依靠于一个很重要的xml文件&#xff1a;pom.xml。我们接下来学习下pom.xml文件的配置。 …

论文阅读:PET/CT Cross-modal medical image fusion of lung tumors based on DCIF-GAN

摘要 背景&#xff1a; 基于GAN的融合方法存在训练不稳定&#xff0c;提取图像的局部和全局上下文语义信息能力不足&#xff0c;交互融合程度不够等问题 贡献&#xff1a; 提出双耦合交互式融合GAN&#xff08;Dual-Coupled Interactive Fusion GAN&#xff0c;DCIF-GAN&…

第十三章 集合

一、集合的概念 集合&#xff1a;将若干用途、性质相同或相近的“数据”组合而成的一个整体 Java集合中只能保存引用类型的数据&#xff0c;不能保存基本类型数据 数组的缺点&#xff1a;长度不可变 Java中常用集合&#xff1a; 1.Set(集):集合中的对象不按特定方式排序&a…

FastGPT的使用

fastGPT的介绍&#xff1a; fastGPT其实和chatGPT差不多 但是好处是可以自行搭建&#xff0c;而且很方便 链接&#xff1a;https://cloud.fastgpt.cn/app/list 首先我们可以根据红框点击&#xff0c;创建一个简易的对话引导 这个机器人就非常的简易&#xff0c;只能完成一些翻…

自动驾驶系列—LDW(车道偏离预警):智能驾驶的安全守护者

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

【Linux系统编程】权限

目录 1、shell命令以及运行原理 2、Linux权限的概念 3、Linux权限管理 3.1 文件访问者的分类(人) 3.2 文件类型和访问权限(事物属性) 4、目录的权限 5、粘滞位 1、shell命令以及运行原理 首先&#xff0c;我们来了解一条指令是如何跑起来的 一般操作系统是不会让用户直…

大数据开发--1.2 Linux介绍及虚拟机网络配置

目录 一. 计算机入门知识介绍 软件和硬件的概述 硬件 软件 操作系统概述 简单介绍 常见的系统操作 学习Linux系统 二. Linux系统介绍 简单介绍 发行版介绍 常用的发行版 三. Linux系统的安装和体验 Linux系统的安装 介绍 虚拟机原理 常见的虚拟机软件 体验Li…

招联金融秋招内推2025

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…

四、网络层(下)

4.9 CIDR CIDR&#xff08;Classless Inter-Domain Routing&#xff09;&#xff0c;是IPv4地址分配和路由表选择的一种灵活且高效的方法。 1992年&#xff0c;由于分类地址中的B类地址很快就被分配完了&#xff0c;且路由表中的表项也急剧增加&#xff0c;分类的IP地址并不能…

高校实训产品:教育AI人工智能实训与科研解决方案

保持前沿、提升就业、低成本的教育AI实训全场景方案 产品概述 AIGC实训云图站解决方案为高校提供了灵活、高效的人工智能实训平台。通过弹性裸金属调度技术和GPU虚拟化&#xff0c;实现高性能与低成本的兼顾&#xff0c;为学生和教师提供不受时间和空间限制的实操机会。平台涵…

Linux查看触摸坐标点的方法,触觉智能RK3562开发板,瑞芯微、全志等通用

平时遇到键盘、鼠标、触摸板等输入设备无响应等异常情况时&#xff0c;一般通过更换设备判断异常。但在遇到更换正常设备后&#xff0c;输入仍然异常的情况下&#xff0c;可以借助evtest工具查看内核的上报事件信息&#xff0c;协助定位问题所在。 本次使用的是触觉智能EVB356…

Yolo v11目标检测实战1:对象分割和人流跟踪(附源码)

一、运行效果演示 多目标跟踪 二、基本理论和核心概念 2.1 对象分割 对象分割是指将图像中的每个像素标记为属于某一特定对象或背景的过程。对于YOLO来说&#xff0c;对象分割是其功能的一个扩展&#xff0c;通过添加额外的分支来预测每个检测框内的像素级掩码&#xff0c;从…

Python画笔案例-073 绘制晃悠悠的海龟

1、绘制晃悠悠的海龟 通过 python 的turtle 库绘制 晃悠悠的海龟,如下图: 2、实现代码 绘制晃悠悠的海龟,以下为实现代码: """晃悠悠的海龟.py """ import time # 导入时间模块 import math # 导…

TypeScript 算法手册 【计数排序】

文章目录 1. 计数排序简介1.1 计数排序定义1.2 计数排序特点 2. 计数排序步骤过程拆解2.1 找出数组中的最大值2.2 创建计数数组2.3 统计每个数字出现的次数2.4 重建排序后的数组 3. 计数排序的优化3.1 处理负数3.2 对象数组排序案例代码和动态图 4. 计数排序的优点5. 计数排序的…

[VULFOCUS刷题]tomcat-pass-getshell 弱口令

tomcat-pass-getshell 弱口令 启动容器&#xff0c;打开网站 点开manageapp&#xff0c;输入弱口令 tomcat/tomcat 之后在下面上传jsp大马&#xff0c;首先生成一个jsp马 这里我直接使用github别人生成好的 tennc/webshell: This is a webshell open source project (github.…