网站广告的图片怎么做做旅行攻略的网站好
概述
之前写过一篇总结:【Godot4.2】2D辅助类Geometry2D入门
本文是去年9月份新总结的。
Godot提供了一个名叫Geometry2D
的类,它提供了一些用于2D几何图形(包括Polygon、PolyLine等)相关的函数,可以实现诸如多边形与多边形或多边形与折线的布尔运算等。本篇就是简单研究和总结这部分内容的一个简要笔记。
多边形与多边形的布尔运算
多边形与多边形可以进行四种布尔运算:交集、差集、并集和亦或,返回的结果可能是0个,一个或多个多边形。
测试场景
我们创建一个2D测试场景,添加3个Polygon2D
节点,其中PolygonA
和PolygonB
是用于布尔运算的多边形。lab
用于显示布尔运算的结果的信息。
自定义多边形布尔运算函数
Geometry2D的布尔运算方法名起的优点随意,反倒是代表布尔运算的PolyBooleanOperation
枚举常量更符合原意,所以简单封装一个函数polygon_boolean()
,代表多边形的布尔运算,并用PolyBooleanOperation
枚举常量值,来进行运算的区分,根据运算类型调用不同的方法。
# ============================ 自定义函数 ============================
# 多边形布尔运算
func polygon_boolean(polygon_a:PackedVector2Array, # 多边形Apolygon_b:PackedVector2Array, # 多边形Boperation: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]
# 2024年9月4日00: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_colorpolygon_b.color = polygon_b_color# 求PolygonA和PolygonB交集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 = polygonpolygon_node.color = polygon_result_coloradd_child(polygon_node)
在上面的代码中:
polygon_boolean()
默认进行求交集运算,传入PolygonA
和PolygonB
的顶点数据后,返回的是一个Array[PackedVector2Array]
类型,根据PolygonA
和PolygonB
的形状和位置不同,其运算获得的结果可能为0个、1个或多个。- 所以我们需要判断运算结果(也就是返回的
Array[PackedVector2Array]
)的元素数目来进行结果的显示:- 等于0代表交集为空,不需要绘制
- 大于0代表至少有一个交集图形,可以进行绘制。
- 因为可能有一个以上的布尔运算结果,所以遍历结果动态生成
Polygon2D
节点显示布尔运算结果
以下是各种可能性下的运算结果,其中:
- 白色为PolygonA,海蓝色为PolygonB,布尔运算结果为橘红色
:::color3
注意
这里有一个需要注意的点:PolygonA
和PolygonB
的原点需要对齐,也就是坐标系原点一致,也就是没有发生相对偏移,否则会出现不准确的结果。对其他布尔操作也是一样。
:::
求并集
有了上面的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_colorpolygon_b.color = polygon_b_color# 求PolygonA和PolygonB交集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_polygon | OPERATION_INTERSECTION |
差集 | clip_polyline_with_polygon | OPERATION_DIFFERENCE |
修改测试场景
我们更改场景节点如下:
然后简单绘制一段折线与一个多边形:
修改根节点代码如下:
# =============================================================
# Geometry2D 测试
# Godot v4.3.stable.steam [77dcf97d8]
# 2024年9月4日00: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_colorpolyline.default_color = polyline_colorvar 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 = $PolylineResult2func _ready():var l = path.pointsvar p = polygon.polygonvar 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]
# 2024年9月4日20: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_colorvar result = Geometry2D.convex_hull(polygon.polygon)# 显示信息lab.text = "result = %s\nresult.size = %d" % [str(result),result.size()]# 显示运算结果if result.size() > 0: # 运算结果不为空polygon_result.polygon = resultpolygon_result.color = result_color
获取任意折线段的凸多边形
分解为凸多边形
# =============================================================
# Geometry2D 测试
# Godot v4.3.stable.steam [77dcf97d8]
# 2024年9月4日20: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_colorvar 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.positionadd_child(polygon_node)
判断两条直线是否相交以及获得交点
@tool
extends Controlvar 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 Controlvar pos:Vector2func _process(delta):pos = get_global_mouse_position()queue_redraw()func _draw():var center = Vector2(400,200)var r = 50if 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 Controlvar polygon:PackedVector2Array = [Vector2(100,100),Vector2(300,100),Vector2(450,150),Vector2(300,300),Vector2(200,200),Vector2(100,100)
]
var pos:Vector2func _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 Controlvar polygon:PackedVector2Array = [Vector2(100,100),Vector2(300,100),Vector2(250,350)
]
var pos:Vector2func _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 Controlvar line1 = [Vector2(0,0),Vector2(400,400)]
var pos:Vector2func _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 Controlvar line1 = [Vector2(0,0),Vector2(400,400)]
var pos:Vector2func _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 Controlvar line1 = [Vector2(100,100),Vector2(400,100)]
var line2 = [Vector2(200,200),Vector2(500,500)]
var pos:Vector2func _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 = valqueue_redraw()var polygon:PackedVector2Array = ShapePoints.star(0,5,50,30,Vector2(400,200))
var pos:Vector2func _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 = valqueue_redraw()var polygon:PackedVector2Array = ShapePoints.star(0,5,50,30,Vector2(400,200))
var pos:Vector2func _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 = valqueue_redraw()var polygon:PackedVector2Array = ShapePoints.star(0,5,50,30,Vector2(400,200))
var pos:Vector2func _draw():var off_polygon = Geometry2D.offset_polygon(polygon,offset,Geometry2D.JOIN_SQUARE)[0]draw_polygon(off_polygon,[Color.AQUAMARINE])
获取折线偏移一定距离后的多边形
offset_polyline()
可以基础Polyline,经过一定的偏移,获取对应的多边形数据。
# 折线段转多边形测试
# 作者:巽星石
# 2024年9月7日23: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 = valqueue_redraw()# 折线的绘制颜色
@export_range(0.1,50.0,0.1) var offset:=1.0:set(val):offset = valqueue_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)
在偏移参数不变的情况下,得到的多边形如下: