当前位置: 首页 > article >正文

【Godot4.3】Geometry2D总结

概述

之前写过一篇总结:【Godot4.2】2D辅助类Geometry2D入门

本文是去年9月份新总结的。

Godot提供了一个名叫Geometry2D的类,它提供了一些用于2D几何图形(包括Polygon、PolyLine等)相关的函数,可以实现诸如多边形与多边形或多边形与折线的布尔运算等。本篇就是简单研究和总结这部分内容的一个简要笔记。

多边形与多边形的布尔运算

多边形与多边形可以进行四种布尔运算:交集、差集、并集和亦或,返回的结果可能是0个,一个或多个多边形。

测试场景

我们创建一个2D测试场景,添加3个Polygon2D节点,其中PolygonAPolygonB是用于布尔运算的多边形。lab用于显示布尔运算的结果的信息。

自定义多边形布尔运算函数

Geometry2D的布尔运算方法名起的优点随意,反倒是代表布尔运算的PolyBooleanOperation枚举常量更符合原意,所以简单封装一个函数polygon_boolean(),代表多边形的布尔运算,并用PolyBooleanOperation枚举常量值,来进行运算的区分,根据运算类型调用不同的方法。

# ============================ 自定义函数 ============================
# 多边形布尔运算
func polygon_boolean(
	polygon_a:PackedVector2Array,  # 多边形A
	polygon_b:PackedVector2Array,  # 多边形B
	operation:int = Geometry2D.PolyBooleanOperation.OPERATION_INTERSECTION  # 布尔运算形式
) -> Array[PackedVector2Array]:
	var result:Array[PackedVector2Array]
	# 根据运算类型调用不同的方法
	match operation:
		Geometry2D.OPERATION_INTERSECTION: # 交集
			result = Geometry2D.intersect_polygons(polygon_a,polygon_b)
		Geometry2D.OPERATION_DIFFERENCE:   # 差集
			result = Geometry2D.clip_polygons(polygon_a,polygon_b)
		Geometry2D.OPERATION_UNION:        # 并集
			result = Geometry2D.merge_polygons(polygon_a,polygon_b)
		Geometry2D.OPERATION_XOR:          # 亦或集
			result = Geometry2D.exclude_polygons(polygon_a,polygon_b)
	return result

下面是对应的Geometry2D方法和对应的PolyBooleanOperation枚举值的对照表:

布尔运算方法PolyBooleanOperation枚举值
交集intersect_polygons()OPERATION_INTERSECTION
并集merge_polygons()OPERATION_UNION
差集clip_polygons()OPERATION_DIFFERENCE
亦或exclude_polygons()OPERATION_XOR

求交集

  • intersect_polygons()将 polygon_a 与 polygon_b 相交,并返回一组相交的多边形。这会在多边形之间执行 OPERATION_INTERSECTION。换句话说,返回由各多边形共享的公共区域。如果没有交集,则返回一个空数组。
  • 该操作可能会产生一个外多边形(边界)和一个内多边形(孔),这可以通过调用 is_polygon_clockwise() 来区分。
# =============================================================
# Geometry2D 测试
# Godot v4.3.stable.steam [77dcf97d8]
# 20249400:17:00
# =============================================================
extends Node2D
# ============================ 参数 ============================
@export var polygon_a_color:Color = Color.WHITE
@export var polygon_b_color:Color = Color.AQUA
@export var polygon_result_color:Color = Color.ORANGE_RED
# ============================ 节点引用 ============================
@onready var polygon_a: Polygon2D = $PolygonA
@onready var polygon_b: Polygon2D = $PolygonB
@onready var lab: Label = $lab

# ============================ 测试代码 ============================
func _ready() -> void:
	polygon_a.color = polygon_a_color
	polygon_b.color = polygon_b_color
	# 求PolygonAPolygonB交集
	var result = polygon_boolean(polygon_a.polygon,polygon_b.polygon)
	# 显示信息
	lab.text = "result = %s\nresult.size = %d" % [str(result),result.size()]
	# 显示运算结果
	if result.size() > 0: # 运算结果不为空
		# 遍历结果动态生成Polygon2D节点显示布尔运算结果
		for polygon in result:
			var polygon_node = Polygon2D.new()
			polygon_node.polygon = polygon
			polygon_node.color = polygon_result_color
			add_child(polygon_node)

在上面的代码中:

  • polygon_boolean()默认进行求交集运算,传入PolygonAPolygonB的顶点数据后,返回的是一个Array[PackedVector2Array]类型,根据PolygonAPolygonB的形状和位置不同,其运算获得的结果可能为0个、1个或多个。
  • 所以我们需要判断运算结果(也就是返回的Array[PackedVector2Array])的元素数目来进行结果的显示:
    • 等于0代表交集为空,不需要绘制
    • 大于0代表至少有一个交集图形,可以进行绘制。
  • 因为可能有一个以上的布尔运算结果,所以遍历结果动态生成Polygon2D节点显示布尔运算结果

以下是各种可能性下的运算结果,其中:

  • 白色为PolygonA,海蓝色为PolygonB,布尔运算结果为橘红色

PolygonA和PolygonB有一个交集

PolygonB完全在PolygonA内部

PolygonA和PolygonB有一个以上的交集

PolygonB完全包围PolygonB

PolygonA和PolygonB有没有交集

:::color3
注意

这里有一个需要注意的点:PolygonAPolygonB的原点需要对齐,也就是坐标系原点一致,也就是没有发生相对偏移,否则会出现不准确的结果。对其他布尔操作也是一样。

:::

求并集

有了上面的polygon_boolean()函数,以及_ready中动态创建Polygon2D显示结果的代码。我们则只需要修改polygon_boolean()的布尔运算类型参数就可以查看其他布尔运算的结果了

var result = polygon_boolean(polygon_a.polygon,polygon_b.polygon)

以下是一些并集测试结果:

求差集

var result = polygon_boolean(polygon_a.polygon,polygon_b.polygon,Geometry2D.OPERATION_DIFFERENCE)

亦或

  • exclude_polygons相互排除由 polygon_a 和 polygon_b 的交集(参见 intersect_polygons())定义的公共区域,并返回一组排除的多边形。这会在多边形之间执行 OPERATION_XOR。换句话说,返回各多边形之间除公共区域之外的所有区域。
  • 该操作可能会产生一个外多边形(边界)和一个内多边形(孔),这可以通过调用 is_polygon_clockwise() 来区分。
var result = polygon_boolean(polygon_a.polygon,polygon_b.polygon,Geometry2D.OPERATION_XOR)

在第三个例子中,因为存在两个区域重叠的结果,所以修改部分代码如下:

func _ready() -> void:
	polygon_a.color = polygon_a_color
	polygon_b.color = polygon_b_color
	# 求PolygonAPolygonB交集
	var result = polygon_boolean(polygon_a.polygon,polygon_b.polygon,Geometry2D.OPERATION_XOR)
	# 显示信息
	lab.text = "result = %s\nresult.size = %d" % [str(result),result.size()]
	# 显示运算结果
	if result.size() > 0: # 运算结果不为空
		# 遍历结果动态生成Polygon2D节点显示布尔运算结果
		for i in range(result.size()):
			var polygon_node = Polygon2D.new()
			polygon_node.polygon = result[i]
			polygon_node.color = polygon_result_color.darkened(0.2 * i)
			add_child(polygon_node)

PolyLine和Polygon的布尔运算

自定义函数

同样我们自定义一个函数来封装Geometry2D对PolyLine和Polygon的布尔运算。

# ============================ 自定义函数 ============================
# 多边形与折线段布尔运算
func polygon_boolean_polyline(
	polyline:PackedVector2Array, # 多边形
	polygon:PackedVector2Array,  # 折线段
	operation:int = Geometry2D.PolyBooleanOperation.OPERATION_INTERSECTION  # 布尔运算形式
) -> Array[PackedVector2Array]:
	var result:Array[PackedVector2Array]
	# 根据运算类型调用不同的方法
	match operation:
		Geometry2D.OPERATION_INTERSECTION: # 交集
			result = Geometry2D.intersect_polyline_with_polygon(polyline,polygon)
		Geometry2D.OPERATION_DIFFERENCE:   # 差集
			result = Geometry2D.clip_polyline_with_polygon(polyline,polygon)
	return result

可以看到PolyLine和Polygon的布尔运算只有交集和差集两种。

布尔运算方法布尔操作
交集intersect_polyline_with_polygonOPERATION_INTERSECTION
差集clip_polyline_with_polygonOPERATION_DIFFERENCE

修改测试场景

我们更改场景节点如下:

然后简单绘制一段折线与一个多边形:

修改根节点代码如下:

# =============================================================
# Geometry2D 测试
# Godot v4.3.stable.steam [77dcf97d8]
# 20249400:17:00
# =============================================================
extends Node2D
# ============================ 参数 ============================
@export var polygon_color:Color = Color.WHITE
@export var polyline_color:Color = Color.AQUA
@export var result_color:Color = Color.ORANGE_RED
# ============================ 节点引用 ============================
@onready var polygon: Polygon2D = $Polygon
@onready var polyline: Line2D = $Polyline
@onready var lab: Label = $lab


# ============================ 测试代码 ============================
func _ready() -> void:
	polygon.color = polygon_color
	polyline.default_color = polyline_color
	
	var result = polygon_boolean_polyline(polyline.points,polygon.polygon)
	# 显示信息
	lab.text = "result = %s\nresult.size = %d" % [str(result),result.size()]
	# 显示运算结果
	if result.size() > 0: # 运算结果不为空
		# 遍历结果动态生成Line2D节点显示布尔运算结果
		for i in range(result.size()):
			var polyline_node = Line2D.new()
			polyline_node.points = result[i]
			polyline_node.default_color = result_color.darkened(0.2 * i)
			add_child(polyline_node)

求交集

var result = polygon_boolean_polyline(polyline.points,polygon.polygon)

注意

不要传错顺序,第一个参数传入Polyline的顶点数据,第二个参数传入Polygon的。






求差集

extends Node2D

@onready var path = $Path
@onready var polygon = $Polygon
@onready var polyline_result = $PolylineResult
@onready var polyline_result2 = $PolylineResult2



func _ready():
	var l = path.points
	var p = polygon.polygon
	
	var result := Geometry2D.clip_polyline_with_polygon(l,p)
	print(result)
	if result.size() == 1:
		polyline_result.points = result[0]
	elif result.size() == 2:
		polyline_result.points = result[0]
		polyline_result2.points = result[1]
	pass





2024年9月4日02:05:09 以下内容未修改


获取凸多边形

  • convex_hull()可以用于获取任意包围折线段或多边形的凸多边形

获取任意多边形的凸多边形

# =============================================================
# Geometry2D 测试
# Godot v4.3.stable.steam [77dcf97d8]
# 20249420:53:23
# =============================================================
extends Node2D
# ============================ 参数 ============================
@export var polygon_color:Color = Color.WHITE
@export var result_color:Color = Color.ORANGE_RED
# ============================ 节点引用 ============================
@onready var polygon: Polygon2D = $Polygon
@onready var polygon_result: Polygon2D = $PolygonResult
@onready var lab: Label = $lab


# ============================ 测试代码 ============================
func _ready() -> void:
	polygon.color = polygon_color
	var result = Geometry2D.convex_hull(polygon.polygon)
	# 显示信息
	lab.text = "result = %s\nresult.size = %d" % [str(result),result.size()]
	# 显示运算结果
	if result.size() > 0: # 运算结果不为空
		polygon_result.polygon = result
		polygon_result.color = result_color

获取任意折线段的凸多边形

分解为凸多边形

# =============================================================
# Geometry2D 测试
# Godot v4.3.stable.steam [77dcf97d8]
# 20249420:53:23
# =============================================================
extends Node2D
# ============================ 参数 ============================
@export var polygon_color:Color = Color.WHITE
@export var result_color:Color = Color.ORANGE_RED
# ============================ 节点引用 ============================
@onready var polygon: Polygon2D = $Polygon
@onready var marker2d: Marker2D = $Marker2D
@onready var lab: Label = $lab



# ============================ 测试代码 ============================
func _ready() -> void:
	polygon.color = polygon_color
	var result = Geometry2D.decompose_polygon_in_convex(polygon.polygon)
	# 显示信息
	lab.text = "result = %s\nresult.size = %d" % [str(result),result.size()]
	# 显示运算结果
	if result.size() > 0: # 运算结果不为空
		for i in range(result.size()):
			var polygon_node = Polygon2D.new()
			polygon_node.polygon = result[i]
			polygon_node.color = result_color.darkened(0.2 * i)
			polygon_node.position = marker2d.position
			add_child(polygon_node)

判断两条直线是否相交以及获得交点

@tool
extends Control

var line1 = [Vector2(0,0),Vector2(400,400)]
var line2 = [Vector2(350,150),Vector2(10,300)]

func _draw():
	draw_line(line1[0],line1[1],Color.GOLDENROD,2)
	draw_line(line2[0],line2[1],Color.GREEN_YELLOW,2)
	if Geometry2D.line_intersects_line(line1[0],line1[0].direction_to(line1[1]),line2[0],line2[0].direction_to(line2[1])):
		var j_point:Vector2 = Geometry2D.line_intersects_line(line1[0],line1[0].direction_to(line1[1]),line2[0],line2[0].direction_to(line2[1]))
		draw_circle(j_point,4,Color.BLUE_VIOLET)

判断并求取两条直线的交点

判断点是否在一个几何图形内

判断点是否在圆内

@tool
extends Control

var pos:Vector2

func _process(delta):
	pos = get_global_mouse_position()
	queue_redraw()

func _draw():
	var center = Vector2(400,200)
	var r = 50
	if Geometry2D.is_point_in_circle(pos,center,r): # 鼠标进入圆
		draw_circle(center,r,Color.AQUA)
	else: # 鼠标在圆外
		draw_circle(center,r,Color.AQUAMARINE)
	
	draw_circle(pos,4,Color.ORANGE) # 绘制鼠标位置

判断鼠标是否进入圆的区域

判断点是否在多边形内

@tool
extends Control

var polygon:PackedVector2Array = [
	Vector2(100,100),Vector2(300,100),
	Vector2(450,150),Vector2(300,300),
	Vector2(200,200),Vector2(100,100)
]
var pos:Vector2

func _process(delta):
	pos = get_global_mouse_position()
	queue_redraw()

func _draw():
	if Geometry2D.is_point_in_polygon(pos,polygon): # 鼠标进入圆
		draw_polygon(polygon,[Color.AQUA])
	else: # 鼠标在圆外
		draw_polygon(polygon,[Color.AQUAMARINE])
	
	draw_circle(pos,4,Color.ORANGE) # 绘制鼠标位置

判断鼠标是否进入多边形区域

判断点是否在三角形内

@tool
extends Control

var polygon:PackedVector2Array = [
	Vector2(100,100),Vector2(300,100),
	Vector2(250,350)
]
var pos:Vector2

func _process(delta):
	pos = get_global_mouse_position()
	queue_redraw()

func _draw():
	if Geometry2D.point_is_inside_triangle(pos,polygon[0],polygon[1],polygon[2]): # 鼠标进入三角形
		draw_polygon(polygon,[Color.AQUA])
	else: # 鼠标在三角形外
		draw_polygon(polygon,[Color.AQUAMARINE])
	
	draw_circle(pos,4,Color.ORANGE) # 绘制鼠标位置

判断鼠标点是否在三角形内

获取最近点

求取线段上离鼠标位置最近的点

@tool
extends Control

var line1 = [Vector2(0,0),Vector2(400,400)]
var pos:Vector2

func _process(delta):
	pos = get_global_mouse_position()
	queue_redraw()

func _draw():
	draw_line(line1[0],line1[1],Color.GOLDENROD,2)
	var c_point = Geometry2D.get_closest_point_to_segment(pos,line1[0],line1[1])
	draw_circle(c_point,4,Color.GREEN_YELLOW)
	draw_circle(pos,4,Color.BLUE)

求取线段上离鼠标位置最近的点

求取直线上离鼠标最近的点

@tool
extends Control

var line1 = [Vector2(0,0),Vector2(400,400)]
var pos:Vector2

func _process(delta):
	pos = get_global_mouse_position()
	queue_redraw()

func _draw():
	draw_line(line1[0],line1[1],Color.GOLDENROD,2)
	var c_point = Geometry2D.get_closest_point_to_segment_uncapped(pos,line1[0],line1[1])
	draw_circle(c_point,4,Color.GREEN_YELLOW)
	draw_circle(pos,4,Color.BLUE)

求取直线上离鼠标最近的点

求取两条线段之间最近的两个点

@tool
extends Control

var line1 = [Vector2(100,100),Vector2(400,100)]
var line2 = [Vector2(200,200),Vector2(500,500)]
var pos:Vector2

func _process(delta):
	line2[1] = get_global_mouse_position()
	queue_redraw()

func _draw():
	draw_line(line1[0],line1[1],Color.GOLDENROD,2)
	draw_line(line2[0],line2[1],Color.ORANGE_RED,2)
	var c_points:PackedVector2Array = Geometry2D.get_closest_points_between_segments(line1[0],line1[1],line2[0],line2[1])
	draw_circle(c_points[0],4,Color.GREEN_YELLOW)
	draw_circle(c_points[1],4,Color.GREEN_YELLOW)

求取两条线段之间最近的两个点

膨胀或缩小多边形

圆角化膨胀或缩小

@tool
extends Control

@export var offset:int = 0:
	set(val):
		offset = val
		queue_redraw()


var polygon:PackedVector2Array = ShapePoints.star(0,5,50,30,Vector2(400,200))
var pos:Vector2

func _draw():
	var off_polygon = Geometry2D.offset_polygon(polygon,offset,Geometry2D.JOIN_ROUND)[0]
	draw_polygon(off_polygon,[Color.AQUAMARINE])

最初的五角星

通过圆角化膨胀后的五角星

保持尖角的膨胀和缩小

@tool
extends Control

@export var offset:int = 0:
	set(val):
		offset = val
		queue_redraw()


var polygon:PackedVector2Array = ShapePoints.star(0,5,50,30,Vector2(400,200))
var pos:Vector2

func _draw():
	var off_polygon = Geometry2D.offset_polygon(polygon,offset,Geometry2D.JOIN_MITER)[0]
	draw_polygon(off_polygon,[Color.AQUAMARINE])

保持尖角膨胀后的五角星

切角化的膨胀或缩小

@tool
extends Control

@export var offset:int = 0:
	set(val):
		offset = val
		queue_redraw()


var polygon:PackedVector2Array = ShapePoints.star(0,5,50,30,Vector2(400,200))
var pos:Vector2

func _draw():
	var off_polygon = Geometry2D.offset_polygon(polygon,offset,Geometry2D.JOIN_SQUARE)[0]
	draw_polygon(off_polygon,[Color.AQUAMARINE])

切角化膨胀后的五角星

切角化收缩后的五角星

获取折线偏移一定距离后的多边形

offset_polyline()可以基础Polyline,经过一定的偏移,获取对应的多边形数据。

# 折线段转多边形测试
# 作者:巽星石
# 20249723:46:30

@tool
extends Node2D

# 折线顶点数据
var line:PackedVector2Array = [
	Vector2(20,20),
	Vector2(100,100),
	Vector2(200,150)
]

# ================== 参数 ==================
# 折线的绘制颜色
@export var line_color:=Color.WHITE:
	set(val):
		line_color = val
		queue_redraw()

# 折线的绘制颜色
@export_range(0.1,50.0,0.1) var offset:=1.0:
	set(val):
		offset = val
		queue_redraw()

# ================== 绘制 ==================
func _draw() -> void:
	var line_polygon = Geometry2D.offset_polyline(line,offset)
	if line_polygon.size() > 0:
		line_polygon[0].append(line_polygon[0][0]) # 闭合
		draw_polyline(line_polygon[0],line_color,1)
	draw_polyline(line,Color.RED,1)
	pass

通过设定连接处和端点处的样式,我们可以获取不同的效果:

  • JOIN_ROUND和END_ROUND分别代表连接处使用圆角或弧线形式
var line_polygon = Geometry2D.offset_polyline(line,offset,Geometry2D.JOIN_ROUND,Geometry2D.END_ROUND)

在偏移参数不变的情况下,得到的多边形如下:


http://www.kler.cn/a/582335.html

相关文章:

  • Kubernetes 中的 Secrets 配置管理
  • Django部署Filemanagement
  • 共享经济时代下,鲲鹏共享科技如何逆袭改命?
  • 操作系统知识总结(三)——内存
  • 【数据结构】3顺序表
  • 从零开始学机器学习——构建一个推荐web应用
  • 华为HCIE认证用处大吗?
  • 【A2DP】深入解析A2DP协议中的音频流处理
  • Redis实现高并发排行榜的功能
  • 侯捷 C++ 课程学习笔记:C++ 新标准11/14
  • 使用AI一步一步实现若依前端(8)
  • vue3 二次封装uni-ui中的组件,并且组件中有 v-model 的解决方法
  • Vue 实现AI对话和AI绘图(AIGC)人工智能
  • Excel多级联动下拉菜单设置
  • C盘清理技巧分享:释放空间,提升电脑性能
  • Networking Based ISAC Hardware Testbed and Performance Evaluation
  • [动手学习深度学习]13.丢弃法 Dropout
  • 修改jupyter notebook的工作空间
  • 二级Python通关秘籍:字符串操作符/函数/方法全解析与实战演练
  • Spike RISC-V ISA 模拟器