Add 2D navigation mesh baking

Adds 2D navigation mesh baking.
This commit is contained in:
smix8 2023-08-17 18:32:30 +02:00
parent 82f6e9be5e
commit 0ee7e3102b
44 changed files with 10706 additions and 537 deletions

View File

@ -171,6 +171,11 @@ Comment: CA certificates
Copyright: Mozilla Contributors Copyright: Mozilla Contributors
License: MPL-2.0 License: MPL-2.0
Files: ./thirdparty/clipper2/
Comment: Clipper2
Copyright: 2010-2013, Angus Johnson
License: BSL-1.0
Files: ./thirdparty/cvtt/ Files: ./thirdparty/cvtt/
Comment: Convection Texture Tools Stand-Alone Kernels Comment: Convection Texture Tools Stand-Alone Kernels
Copyright: 2018, Eric Lasota Copyright: 2018, Eric Lasota

View File

@ -226,6 +226,7 @@ opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determine
# Thirdparty libraries # Thirdparty libraries
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True)) opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundles", True)) opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundles", True))
opts.Add(BoolVariable("builtin_clipper2", "Use the built-in Clipper2 library", True))
opts.Add(BoolVariable("builtin_embree", "Use the built-in Embree library", True)) opts.Add(BoolVariable("builtin_embree", "Use the built-in Embree library", True))
opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True)) opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True))
opts.Add(BoolVariable("builtin_freetype", "Use the built-in FreeType library", True)) opts.Add(BoolVariable("builtin_freetype", "Use the built-in FreeType library", True))

View File

@ -89,6 +89,24 @@ if env["brotli"] and env["builtin_brotli"]:
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_brotli_sources) env_thirdparty.add_source_files(thirdparty_obj, thirdparty_brotli_sources)
# Clipper2 Thirdparty source files used for polygon and polyline boolean operations.
if env["builtin_clipper2"]:
thirdparty_clipper_dir = "#thirdparty/clipper2/"
thirdparty_clipper_sources = [
"src/clipper.engine.cpp",
"src/clipper.offset.cpp",
"src/clipper.rectclip.cpp",
]
thirdparty_clipper_sources = [thirdparty_clipper_dir + file for file in thirdparty_clipper_sources]
env_thirdparty.Prepend(CPPPATH=[thirdparty_clipper_dir + "include"])
env.Prepend(CPPPATH=[thirdparty_clipper_dir + "include"])
env_thirdparty.Append(CPPDEFINES=["CLIPPER2_ENABLED"])
env.Append(CPPDEFINES=["CLIPPER2_ENABLED"])
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_clipper_sources)
# Zlib library, can be unbundled # Zlib library, can be unbundled
if env["builtin_zlib"]: if env["builtin_zlib"]:
thirdparty_zlib_dir = "#thirdparty/zlib/" thirdparty_zlib_dir = "#thirdparty/zlib/"

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationMeshSourceGeometryData2D" inherits="Resource" is_experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Container for parsed source geometry data used in navigation mesh baking.
</brief_description>
<description>
Container for parsed source geometry data used in navigation mesh baking.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_obstruction_outline">
<return type="void" />
<param index="0" name="shape_outline" type="PackedVector2Array" />
<description>
Adds the outline points of a shape as obstructed area.
</description>
</method>
<method name="add_traversable_outline">
<return type="void" />
<param index="0" name="shape_outline" type="PackedVector2Array" />
<description>
Adds the outline points of a shape as traversable area.
</description>
</method>
<method name="clear">
<return type="void" />
<description>
Clears the internal data.
</description>
</method>
<method name="get_obstruction_outlines" qualifiers="const">
<return type="PackedVector2Array[]" />
<description>
Returns all the obstructed area outlines arrays.
</description>
</method>
<method name="get_traversable_outlines" qualifiers="const">
<return type="PackedVector2Array[]" />
<description>
Returns all the traversable area outlines arrays.
</description>
</method>
<method name="has_data">
<return type="bool" />
<description>
Returns [code]true[/code] when parsed source geometry data exists.
</description>
</method>
<method name="set_obstruction_outlines">
<return type="void" />
<param index="0" name="obstruction_outlines" type="PackedVector2Array[]" />
<description>
Sets all the obstructed area outlines arrays.
</description>
</method>
<method name="set_traversable_outlines">
<return type="void" />
<param index="0" name="traversable_outlines" type="PackedVector2Array[]" />
<description>
Sets all the traversable area outlines arrays.
</description>
</method>
</methods>
</class>

View File

@ -1,44 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationPolygon" inherits="Resource" is_experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <class name="NavigationPolygon" inherits="Resource" is_experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description> <brief_description>
A navigation polygon that defines traversable areas and obstacles. A 2D navigation mesh that describes a traversable surface for pathfinding.
</brief_description> </brief_description>
<description> <description>
There are two ways to create polygons. Either by using the [method add_outline] method, or using the [method add_polygon] method. A navigation mesh can be created either by baking it with the help of the [NavigationServer2D], or by adding vertices and convex polygon indices arrays manually.
Using [method add_outline]: To bake a navigation mesh at least one outline needs to be added that defines the outer bounds of the baked area.
[codeblocks] [codeblocks]
[gdscript] [gdscript]
var polygon = NavigationPolygon.new() var new_navigation_mesh = NavigationPolygon.new()
var outline = PackedVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)]) var bounding_outline = PackedVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)])
polygon.add_outline(outline) new_navigation_mesh.add_outline(bounding_outline)
polygon.make_polygons_from_outlines() NavigationServer2D.bake_from_source_geometry_data(new_navigation_mesh, NavigationMeshSourceGeometryData2D.new());
$NavigationRegion2D.navigation_polygon = polygon $NavigationRegion2D.navigation_polygon = new_navigation_mesh
[/gdscript] [/gdscript]
[csharp] [csharp]
var polygon = new NavigationPolygon(); var newNavigationMesh = new NavigationPolygon();
var outline = new Vector2[] { new Vector2(0, 0), new Vector2(0, 50), new Vector2(50, 50), new Vector2(50, 0) }; var boundingOutline = new Vector2[] { new Vector2(0, 0), new Vector2(0, 50), new Vector2(50, 50), new Vector2(50, 0) };
polygon.AddOutline(outline); newNavigationMesh.AddOutline(boundingOutline);
polygon.MakePolygonsFromOutlines(); NavigationServer2D.BakeFromSourceGeometryData(newNavigationMesh, new NavigationMeshSourceGeometryData2D());
GetNode&lt;NavigationRegion2D&gt;("NavigationRegion2D").NavigationPolygon = polygon; GetNode&lt;NavigationRegion2D&gt;("NavigationRegion2D").NavigationPolygon = newNavigationMesh;
[/csharp] [/csharp]
[/codeblocks] [/codeblocks]
Using [method add_polygon] and indices of the vertices array. Adding vertices and polygon indices manually.
[codeblocks] [codeblocks]
[gdscript] [gdscript]
var polygon = NavigationPolygon.new() var new_navigation_mesh = NavigationPolygon.new()
var vertices = PackedVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)]) var new_vertices = PackedVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)])
polygon.vertices = vertices new_navigation_mesh.vertices = new_vertices
var indices = PackedInt32Array([0, 1, 2, 3]) var new_polygon_indices = PackedInt32Array([0, 1, 2, 3])
polygon.add_polygon(indices) new_navigation_mesh.add_polygon(new_polygon_indices)
$NavigationRegion2D.navigation_polygon = polygon $NavigationRegion2D.navigation_polygon = new_navigation_mesh
[/gdscript] [/gdscript]
[csharp] [csharp]
var polygon = new NavigationPolygon(); var newNavigationMesh = new NavigationPolygon();
var vertices = new Vector2[] { new Vector2(0, 0), new Vector2(0, 50), new Vector2(50, 50), new Vector2(50, 0) }; var newVertices = new Vector2[] { new Vector2(0, 0), new Vector2(0, 50), new Vector2(50, 50), new Vector2(50, 0) };
polygon.Vertices = vertices; newNavigationMesh.Vertices = newVertices;
var indices = new int[] { 0, 1, 2, 3 }; var newPolygonIndices = new int[] { 0, 1, 2, 3 };
polygon.AddPolygon(indices); newNavigationMesh.AddPolygon(newPolygonIndices);
GetNode&lt;NavigationRegion2D&gt;("NavigationRegion2D").NavigationPolygon = polygon; GetNode&lt;NavigationRegion2D&gt;("NavigationRegion2D").NavigationPolygon = newNavigationMesh;
[/csharp] [/csharp]
[/codeblocks] [/codeblocks]
</description> </description>
@ -51,7 +51,7 @@
<return type="void" /> <return type="void" />
<param index="0" name="outline" type="PackedVector2Array" /> <param index="0" name="outline" type="PackedVector2Array" />
<description> <description>
Appends a [PackedVector2Array] that contains the vertices of an outline to the internal array that contains all the outlines. You have to call [method make_polygons_from_outlines] in order for this array to be converted to polygons that the engine will use. Appends a [PackedVector2Array] that contains the vertices of an outline to the internal array that contains all the outlines.
</description> </description>
</method> </method>
<method name="add_outline_at_index"> <method name="add_outline_at_index">
@ -59,7 +59,7 @@
<param index="0" name="outline" type="PackedVector2Array" /> <param index="0" name="outline" type="PackedVector2Array" />
<param index="1" name="index" type="int" /> <param index="1" name="index" type="int" />
<description> <description>
Adds a [PackedVector2Array] that contains the vertices of an outline to the internal array that contains all the outlines at a fixed position. You have to call [method make_polygons_from_outlines] in order for this array to be converted to polygons that the engine will use. Adds a [PackedVector2Array] that contains the vertices of an outline to the internal array that contains all the outlines at a fixed position.
</description> </description>
</method> </method>
<method name="add_polygon"> <method name="add_polygon">
@ -106,6 +106,13 @@
Returns the number of outlines that were created in the editor or by script. Returns the number of outlines that were created in the editor or by script.
</description> </description>
</method> </method>
<method name="get_parsed_collision_mask_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member parsed_collision_mask] is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_polygon"> <method name="get_polygon">
<return type="PackedInt32Array" /> <return type="PackedInt32Array" />
<param index="0" name="idx" type="int" /> <param index="0" name="idx" type="int" />
@ -125,10 +132,11 @@
Returns a [PackedVector2Array] containing all the vertices being used to create the polygons. Returns a [PackedVector2Array] containing all the vertices being used to create the polygons.
</description> </description>
</method> </method>
<method name="make_polygons_from_outlines"> <method name="make_polygons_from_outlines" is_deprecated="true">
<return type="void" /> <return type="void" />
<description> <description>
Creates polygons from the outlines added in the editor or by script. Creates polygons from the outlines added in the editor or by script.
[i]Deprecated.[/i] This function is deprecated, and might be removed in a future release. Use [method NavigationServer2D.parse_source_geometry_data] and [method NavigationServer2D.bake_from_source_geometry_data] instead.
</description> </description>
</method> </method>
<method name="remove_outline"> <method name="remove_outline">
@ -146,6 +154,14 @@
Changes an outline created in the editor or by script. You have to call [method make_polygons_from_outlines] for the polygons to update. Changes an outline created in the editor or by script. You have to call [method make_polygons_from_outlines] for the polygons to update.
</description> </description>
</method> </method>
<method name="set_parsed_collision_mask_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified layer in the [member parsed_collision_mask], given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_vertices"> <method name="set_vertices">
<return type="void" /> <return type="void" />
<param index="0" name="vertices" type="PackedVector2Array" /> <param index="0" name="vertices" type="PackedVector2Array" />
@ -155,8 +171,52 @@
</method> </method>
</methods> </methods>
<members> <members>
<member name="agent_radius" type="float" setter="set_agent_radius" getter="get_agent_radius" default="10.0">
The distance to erode/shrink the walkable surface when baking the navigation mesh.
</member>
<member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="1.0"> <member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="1.0">
The cell size used to rasterize the navigation mesh vertices. Must match with the cell size on the navigation map. The cell size used to rasterize the navigation mesh vertices. Must match with the cell size on the navigation map.
</member> </member>
<member name="parsed_collision_mask" type="int" setter="set_parsed_collision_mask" getter="get_parsed_collision_mask" default="4294967295">
The physics layers to scan for static colliders.
Only used when [member parsed_geometry_type] is [constant PARSED_GEOMETRY_STATIC_COLLIDERS] or [constant PARSED_GEOMETRY_BOTH].
</member>
<member name="parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" enum="NavigationPolygon.ParsedGeometryType" default="2">
Determines which type of nodes will be parsed as geometry. See [enum ParsedGeometryType] for possible values.
</member>
<member name="source_geometry_group_name" type="StringName" setter="set_source_geometry_group_name" getter="get_source_geometry_group_name" default="&amp;&quot;navigation_polygon_source_geometry_group&quot;">
The group name of nodes that should be parsed for baking source geometry.
Only used when [member source_geometry_mode] is [constant SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN] or [constant SOURCE_GEOMETRY_GROUPS_EXPLICIT].
</member>
<member name="source_geometry_mode" type="int" setter="set_source_geometry_mode" getter="get_source_geometry_mode" enum="NavigationPolygon.SourceGeometryMode" default="0">
The source of the geometry used when baking. See [enum SourceGeometryMode] for possible values.
</member>
</members> </members>
<constants>
<constant name="PARSED_GEOMETRY_MESH_INSTANCES" value="0" enum="ParsedGeometryType">
Parses mesh instances as obstruction geometry. This includes [Polygon2D], [MeshInstance2D], [MultiMeshInstance2D], and [TileMap] nodes.
Meshes are only parsed when they use a 2D vertices surface format.
</constant>
<constant name="PARSED_GEOMETRY_STATIC_COLLIDERS" value="1" enum="ParsedGeometryType">
Parses [StaticBody2D] and [TileMap] colliders as obstruction geometry. The collider should be in any of the layers specified by [member parsed_collision_mask].
</constant>
<constant name="PARSED_GEOMETRY_BOTH" value="2" enum="ParsedGeometryType">
Both [constant PARSED_GEOMETRY_MESH_INSTANCES] and [constant PARSED_GEOMETRY_STATIC_COLLIDERS].
</constant>
<constant name="PARSED_GEOMETRY_MAX" value="3" enum="ParsedGeometryType">
Represents the size of the [enum ParsedGeometryType] enum.
</constant>
<constant name="SOURCE_GEOMETRY_ROOT_NODE_CHILDREN" value="0" enum="SourceGeometryMode">
Scans the child nodes of the root node recursively for geometry.
</constant>
<constant name="SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN" value="1" enum="SourceGeometryMode">
Scans nodes in a group and their child nodes recursively for geometry. The group is specified by [member source_geometry_group_name].
</constant>
<constant name="SOURCE_GEOMETRY_GROUPS_EXPLICIT" value="2" enum="SourceGeometryMode">
Uses nodes in a group for geometry. The group is specified by [member source_geometry_group_name].
</constant>
<constant name="SOURCE_GEOMETRY_MAX" value="3" enum="SourceGeometryMode">
Represents the size of the [enum SourceGeometryMode] enum.
</constant>
</constants>
</class> </class>

View File

@ -16,6 +16,13 @@
<link title="Using NavigationRegions">$DOCS_URL/tutorials/navigation/navigation_using_navigationregions.html</link> <link title="Using NavigationRegions">$DOCS_URL/tutorials/navigation/navigation_using_navigationregions.html</link>
</tutorials> </tutorials>
<methods> <methods>
<method name="bake_navigation_polygon">
<return type="void" />
<param index="0" name="on_thread" type="bool" default="true" />
<description>
Bakes the [NavigationPolygon]. If [param on_thread] is set to [code]true[/code] (default), the baking is done on a separate thread.
</description>
</method>
<method name="get_avoidance_layer_value" qualifiers="const"> <method name="get_avoidance_layer_value" qualifiers="const">
<return type="bool" /> <return type="bool" />
<param index="0" name="layer_number" type="int" /> <param index="0" name="layer_number" type="int" />
@ -93,4 +100,16 @@
If enabled the navigation region will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. If enabled the navigation region will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</member> </member>
</members> </members>
<signals>
<signal name="bake_finished">
<description>
Emitted when a navigation polygon bake operation is completed.
</description>
</signal>
<signal name="navigation_polygon_changed">
<description>
Emitted when the used navigation polygon is replaced or changes to the internals of the current navigation polygon are committed.
</description>
</signal>
</signals>
</class> </class>

View File

@ -182,6 +182,24 @@
Replaces the internal velocity in the collision avoidance simulation with [param velocity] for the specified [param agent]. When an agent is teleported to a new position far away this function should be used in the same frame. If called frequently this function can get agents stuck. Replaces the internal velocity in the collision avoidance simulation with [param velocity] for the specified [param agent]. When an agent is teleported to a new position far away this function should be used in the same frame. If called frequently this function can get agents stuck.
</description> </description>
</method> </method>
<method name="bake_from_source_geometry_data">
<return type="void" />
<param index="0" name="navigation_polygon" type="NavigationPolygon" />
<param index="1" name="source_geometry_data" type="NavigationMeshSourceGeometryData2D" />
<param index="2" name="callback" type="Callable" default="Callable()" />
<description>
Bakes the provided [param navigation_polygon] with the data from the provided [param source_geometry_data]. After the process is finished the optional [param callback] will be called.
</description>
</method>
<method name="bake_from_source_geometry_data_async">
<return type="void" />
<param index="0" name="navigation_polygon" type="NavigationPolygon" />
<param index="1" name="source_geometry_data" type="NavigationMeshSourceGeometryData2D" />
<param index="2" name="callback" type="Callable" default="Callable()" />
<description>
Bakes the provided [param navigation_polygon] with the data from the provided [param source_geometry_data] as an async task running on a background thread. After the process is finished the optional [param callback] will be called.
</description>
</method>
<method name="free_rid"> <method name="free_rid">
<return type="void" /> <return type="void" />
<param index="0" name="rid" type="RID" /> <param index="0" name="rid" type="RID" />
@ -579,6 +597,18 @@
Sets the outline vertices for the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out. Sets the outline vertices for the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out.
</description> </description>
</method> </method>
<method name="parse_source_geometry_data">
<return type="void" />
<param index="0" name="navigation_polygon" type="NavigationPolygon" />
<param index="1" name="source_geometry_data" type="NavigationMeshSourceGeometryData2D" />
<param index="2" name="root_node" type="Node" />
<param index="3" name="callback" type="Callable" default="Callable()" />
<description>
Parses the [SceneTree] for source geometry according to the properties of [param navigation_polygon]. Updates the provided [param source_geometry_data] resource with the resulting data. The resource can then be used to bake a navigation mesh with [method bake_from_source_geometry_data]. After the process is finished the optional [param callback] will be called.
[b]Note:[/b] This function needs to run on the main thread or with a deferred call as the SceneTree is not thread-safe.
[b]Performance:[/b] While convenient, reading data arrays from [Mesh] resources can affect the frame rate negatively. The data needs to be received from the GPU, stalling the [RenderingServer] in the process. For performance prefer the use of e.g. collision shapes or creating the data arrays entirely in code.
</description>
</method>
<method name="query_path" qualifiers="const"> <method name="query_path" qualifiers="const">
<return type="void" /> <return type="void" />
<param index="0" name="parameters" type="NavigationPathQueryParameters2D" /> <param index="0" name="parameters" type="NavigationPathQueryParameters2D" />

View File

@ -32,6 +32,8 @@
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h" #include "editor/editor_undo_redo_manager.h"
#include "scene/2d/navigation_region_2d.h"
#include "scene/gui/dialogs.h"
Ref<NavigationPolygon> NavigationPolygonEditor::_ensure_navpoly() const { Ref<NavigationPolygon> NavigationPolygonEditor::_ensure_navpoly() const {
Ref<NavigationPolygon> navpoly = node->get_navigation_polygon(); Ref<NavigationPolygon> navpoly = node->get_navigation_polygon();
@ -71,7 +73,6 @@ Variant NavigationPolygonEditor::_get_polygon(int p_idx) const {
void NavigationPolygonEditor::_set_polygon(int p_idx, const Variant &p_polygon) const { void NavigationPolygonEditor::_set_polygon(int p_idx, const Variant &p_polygon) const {
Ref<NavigationPolygon> navpoly = _ensure_navpoly(); Ref<NavigationPolygon> navpoly = _ensure_navpoly();
navpoly->set_outline(p_idx, p_polygon); navpoly->set_outline(p_idx, p_polygon);
navpoly->make_polygons_from_outlines();
} }
void NavigationPolygonEditor::_action_add_polygon(const Variant &p_polygon) { void NavigationPolygonEditor::_action_add_polygon(const Variant &p_polygon) {
@ -79,8 +80,6 @@ void NavigationPolygonEditor::_action_add_polygon(const Variant &p_polygon) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(navpoly.ptr(), "add_outline", p_polygon); undo_redo->add_do_method(navpoly.ptr(), "add_outline", p_polygon);
undo_redo->add_undo_method(navpoly.ptr(), "remove_outline", navpoly->get_outline_count()); undo_redo->add_undo_method(navpoly.ptr(), "remove_outline", navpoly->get_outline_count());
undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines");
undo_redo->add_undo_method(navpoly.ptr(), "make_polygons_from_outlines");
} }
void NavigationPolygonEditor::_action_remove_polygon(int p_idx) { void NavigationPolygonEditor::_action_remove_polygon(int p_idx) {
@ -88,8 +87,6 @@ void NavigationPolygonEditor::_action_remove_polygon(int p_idx) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(navpoly.ptr(), "remove_outline", p_idx); undo_redo->add_do_method(navpoly.ptr(), "remove_outline", p_idx);
undo_redo->add_undo_method(navpoly.ptr(), "add_outline_at_index", navpoly->get_outline(p_idx), p_idx); undo_redo->add_undo_method(navpoly.ptr(), "add_outline_at_index", navpoly->get_outline(p_idx), p_idx);
undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines");
undo_redo->add_undo_method(navpoly.ptr(), "make_polygons_from_outlines");
} }
void NavigationPolygonEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) { void NavigationPolygonEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
@ -97,8 +94,6 @@ void NavigationPolygonEditor::_action_set_polygon(int p_idx, const Variant &p_pr
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(navpoly.ptr(), "set_outline", p_idx, p_polygon); undo_redo->add_do_method(navpoly.ptr(), "set_outline", p_idx, p_polygon);
undo_redo->add_undo_method(navpoly.ptr(), "set_outline", p_idx, p_previous); undo_redo->add_undo_method(navpoly.ptr(), "set_outline", p_idx, p_previous);
undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines");
undo_redo->add_undo_method(navpoly.ptr(), "make_polygons_from_outlines");
} }
bool NavigationPolygonEditor::_has_resource() const { bool NavigationPolygonEditor::_has_resource() const {
@ -119,7 +114,84 @@ void NavigationPolygonEditor::_create_resource() {
_menu_option(MODE_CREATE); _menu_option(MODE_CREATE);
} }
NavigationPolygonEditor::NavigationPolygonEditor() {} NavigationPolygonEditor::NavigationPolygonEditor() {
bake_hbox = memnew(HBoxContainer);
add_child(bake_hbox);
button_bake = memnew(Button);
button_bake->set_flat(true);
bake_hbox->add_child(button_bake);
button_bake->set_toggle_mode(true);
button_bake->set_text(TTR("Bake NavigationPolygon"));
button_bake->set_tooltip_text(TTR("Bakes the NavigationPolygon by first parsing the scene for source geometry and then creating the navigation polygon vertices and polygons."));
button_bake->connect("pressed", callable_mp(this, &NavigationPolygonEditor::_bake_pressed));
button_reset = memnew(Button);
button_reset->set_flat(true);
bake_hbox->add_child(button_reset);
button_reset->set_text(TTR("Clear NavigationPolygon"));
button_reset->set_tooltip_text(TTR("Clears the internal NavigationPolygon outlines, vertices and polygons."));
button_reset->connect("pressed", callable_mp(this, &NavigationPolygonEditor::_clear_pressed));
bake_info = memnew(Label);
bake_hbox->add_child(bake_info);
err_dialog = memnew(AcceptDialog);
add_child(err_dialog);
node = nullptr;
}
void NavigationPolygonEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
button_bake->set_icon(get_theme_icon(SNAME("Bake"), SNAME("EditorIcons")));
button_reset->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
} break;
}
}
void NavigationPolygonEditor::_bake_pressed() {
button_bake->set_pressed(false);
ERR_FAIL_NULL(node);
Ref<NavigationPolygon> navigation_polygon = node->get_navigation_polygon();
if (!navigation_polygon.is_valid()) {
err_dialog->set_text(TTR("A NavigationPolygon resource must be set or created for this node to work."));
err_dialog->popup_centered();
return;
}
node->bake_navigation_polygon(true);
node->queue_redraw();
}
void NavigationPolygonEditor::_clear_pressed() {
if (node) {
if (node->get_navigation_polygon().is_valid()) {
node->get_navigation_polygon()->clear();
}
}
button_bake->set_pressed(false);
bake_info->set_text("");
if (node) {
node->queue_redraw();
}
}
void NavigationPolygonEditor::_update_polygon_editing_state() {
if (!_get_node()) {
return;
}
if (node != nullptr && node->get_navigation_polygon().is_valid()) {
bake_hbox->show();
} else {
bake_hbox->hide();
}
}
NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin() : NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin() :
AbstractPolygon2DEditorPlugin(memnew(NavigationPolygonEditor), "NavigationRegion2D") { AbstractPolygon2DEditorPlugin(memnew(NavigationPolygonEditor), "NavigationRegion2D") {

View File

@ -32,16 +32,38 @@
#define NAVIGATION_POLYGON_EDITOR_PLUGIN_H #define NAVIGATION_POLYGON_EDITOR_PLUGIN_H
#include "editor/plugins/abstract_polygon_2d_editor.h" #include "editor/plugins/abstract_polygon_2d_editor.h"
#include "scene/2d/navigation_region_2d.h"
#include "editor/editor_plugin.h"
class AcceptDialog;
class HBoxContainer;
class NavigationPolygon;
class NavigationRegion2D;
class NavigationPolygonEditor : public AbstractPolygon2DEditor { class NavigationPolygonEditor : public AbstractPolygon2DEditor {
friend class NavigationPolygonEditorPlugin;
GDCLASS(NavigationPolygonEditor, AbstractPolygon2DEditor); GDCLASS(NavigationPolygonEditor, AbstractPolygon2DEditor);
NavigationRegion2D *node = nullptr; NavigationRegion2D *node = nullptr;
Ref<NavigationPolygon> _ensure_navpoly() const; Ref<NavigationPolygon> _ensure_navpoly() const;
AcceptDialog *err_dialog = nullptr;
HBoxContainer *bake_hbox = nullptr;
Button *button_bake = nullptr;
Button *button_reset = nullptr;
Label *bake_info = nullptr;
void _bake_pressed();
void _clear_pressed();
void _update_polygon_editing_state();
protected: protected:
void _notification(int p_what);
virtual Node2D *_get_node() const override; virtual Node2D *_get_node() const override;
virtual void _set_node(Node *p_polygon) override; virtual void _set_node(Node *p_polygon) override;
@ -63,6 +85,8 @@ public:
class NavigationPolygonEditorPlugin : public AbstractPolygon2DEditorPlugin { class NavigationPolygonEditorPlugin : public AbstractPolygon2DEditorPlugin {
GDCLASS(NavigationPolygonEditorPlugin, AbstractPolygon2DEditorPlugin); GDCLASS(NavigationPolygonEditorPlugin, AbstractPolygon2DEditorPlugin);
NavigationPolygonEditor *navigation_polygon_editor = nullptr;
public: public:
NavigationPolygonEditorPlugin(); NavigationPolygonEditorPlugin();
}; };

View File

@ -70,6 +70,7 @@
#include "servers/movie_writer/movie_writer.h" #include "servers/movie_writer/movie_writer.h"
#include "servers/movie_writer/movie_writer_mjpeg.h" #include "servers/movie_writer/movie_writer_mjpeg.h"
#include "servers/navigation_server_2d.h" #include "servers/navigation_server_2d.h"
#include "servers/navigation_server_2d_dummy.h"
#include "servers/navigation_server_3d.h" #include "servers/navigation_server_3d.h"
#include "servers/navigation_server_3d_dummy.h" #include "servers/navigation_server_3d_dummy.h"
#include "servers/physics_server_2d.h" #include "servers/physics_server_2d.h"
@ -339,8 +340,14 @@ void initialize_navigation_server() {
navigation_server_3d->init(); navigation_server_3d->init();
// Init 2D Navigation Server // Init 2D Navigation Server
navigation_server_2d = memnew(NavigationServer2D); navigation_server_2d = NavigationServer2DManager::new_default_server();
if (!navigation_server_2d) {
WARN_PRINT_ONCE("No NavigationServer2D implementation has been registered! Falling back to a dummy implementation: navigation features will be unavailable.");
navigation_server_2d = memnew(NavigationServer2DDummy);
}
ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D."); ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D.");
navigation_server_2d->init();
} }
void finalize_navigation_server() { void finalize_navigation_server() {
@ -350,6 +357,7 @@ void finalize_navigation_server() {
navigation_server_3d = nullptr; navigation_server_3d = nullptr;
ERR_FAIL_NULL(navigation_server_2d); ERR_FAIL_NULL(navigation_server_2d);
navigation_server_2d->finish();
memdelete(navigation_server_2d); memdelete(navigation_server_2d);
navigation_server_2d = nullptr; navigation_server_2d = nullptr;
} }
@ -3490,6 +3498,9 @@ bool Main::iteration() {
// process all our active interfaces // process all our active interfaces
XRServer::get_singleton()->_process(); XRServer::get_singleton()->_process();
NavigationServer2D::get_singleton()->sync();
NavigationServer3D::get_singleton()->sync();
for (int iters = 0; iters < advance.physics_steps; ++iters) { for (int iters = 0; iters < advance.physics_steps; ++iters) {
if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) { if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) {
Input::get_singleton()->flush_buffered_events(); Input::get_singleton()->flush_buffered_events();

View File

@ -1,5 +1,5 @@
def can_build(env, platform): def can_build(env, platform):
return True return not env["disable_3d"]
def configure(env): def configure(env):

View File

@ -1086,6 +1086,14 @@ void GodotNavigationServer::map_force_update(RID p_map) {
map->sync(); map->sync();
} }
void GodotNavigationServer::sync() {
#ifndef _3D_DISABLED
if (navmesh_generator_3d) {
navmesh_generator_3d->sync();
}
#endif // _3D_DISABLED
}
void GodotNavigationServer::process(real_t p_delta_time) { void GodotNavigationServer::process(real_t p_delta_time) {
flush_queries(); flush_queries();
@ -1093,16 +1101,6 @@ void GodotNavigationServer::process(real_t p_delta_time) {
return; return;
} }
#ifndef _3D_DISABLED
// Sync finished navmesh bakes before doing NavMap updates.
if (navmesh_generator_3d) {
navmesh_generator_3d->sync();
// Finished bakes emit callbacks and users might have reacted to those.
// Flush queue again so users do not have to wait for the next sync.
flush_queries();
}
#endif // _3D_DISABLED
int _new_pm_region_count = 0; int _new_pm_region_count = 0;
int _new_pm_agent_count = 0; int _new_pm_agent_count = 0;
int _new_pm_link_count = 0; int _new_pm_link_count = 0;

View File

@ -243,6 +243,7 @@ public:
void flush_queries(); void flush_queries();
virtual void process(real_t p_delta_time) override; virtual void process(real_t p_delta_time) override;
virtual void init() override; virtual void init() override;
virtual void sync() override;
virtual void finish() override; virtual void finish() override;
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override; virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override;

View File

@ -0,0 +1,369 @@
/**************************************************************************/
/* godot_navigation_server_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "godot_navigation_server_2d.h"
#ifdef CLIPPER2_ENABLED
#include "nav_mesh_generator_2d.h"
#endif // CLIPPER2_ENABLED
#include "servers/navigation_server_3d.h"
#define FORWARD_0(FUNC_NAME) \
GodotNavigationServer2D::FUNC_NAME() { \
return NavigationServer3D::get_singleton()->FUNC_NAME(); \
}
#define FORWARD_0_C(FUNC_NAME) \
GodotNavigationServer2D::FUNC_NAME() \
const { \
return NavigationServer3D::get_singleton()->FUNC_NAME(); \
}
#define FORWARD_1(FUNC_NAME, T_0, D_0, CONV_0) \
GodotNavigationServer2D::FUNC_NAME(T_0 D_0) { \
return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0)); \
}
#define FORWARD_1_C(FUNC_NAME, T_0, D_0, CONV_0) \
GodotNavigationServer2D::FUNC_NAME(T_0 D_0) \
const { \
return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0)); \
}
#define FORWARD_1_R_C(CONV_R, FUNC_NAME, T_0, D_0, CONV_0) \
GodotNavigationServer2D::FUNC_NAME(T_0 D_0) \
const { \
return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0))); \
}
#define FORWARD_2(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
GodotNavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) { \
return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1)); \
}
#define FORWARD_2_C(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
GodotNavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) \
const { \
return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1)); \
}
#define FORWARD_2_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
GodotNavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) \
const { \
return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1))); \
}
#define FORWARD_5_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, T_4, D_4, CONV_0, CONV_1, CONV_2, CONV_3, CONV_4) \
GodotNavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3, T_4 D_4) \
const { \
return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3), CONV_4(D_4))); \
}
static RID rid_to_rid(const RID d) {
return d;
}
static bool bool_to_bool(const bool d) {
return d;
}
static int int_to_int(const int d) {
return d;
}
static uint32_t uint32_to_uint32(const uint32_t d) {
return d;
}
static real_t real_to_real(const real_t d) {
return d;
}
static Vector3 v2_to_v3(const Vector2 d) {
return Vector3(d.x, 0.0, d.y);
}
static Vector2 v3_to_v2(const Vector3 &d) {
return Vector2(d.x, d.z);
}
static Vector<Vector3> vector_v2_to_v3(const Vector<Vector2> &d) {
Vector<Vector3> nd;
nd.resize(d.size());
for (int i(0); i < nd.size(); i++) {
nd.write[i] = v2_to_v3(d[i]);
}
return nd;
}
static Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) {
Vector<Vector2> nd;
nd.resize(d.size());
for (int i(0); i < nd.size(); i++) {
nd.write[i] = v3_to_v2(d[i]);
}
return nd;
}
static Transform3D trf2_to_trf3(const Transform2D &d) {
Vector3 o(v2_to_v3(d.get_origin()));
Basis b;
b.rotate(Vector3(0, -1, 0), d.get_rotation());
b.scale(v2_to_v3(d.get_scale()));
return Transform3D(b, o);
}
static ObjectID id_to_id(const ObjectID &id) {
return id;
}
static Callable callable_to_callable(const Callable &c) {
return c;
}
static Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) {
if (d.is_valid()) {
return d->get_navigation_mesh();
} else {
return Ref<NavigationMesh>();
}
}
void GodotNavigationServer2D::init() {
#ifdef CLIPPER2_ENABLED
navmesh_generator_2d = memnew(NavMeshGenerator2D);
ERR_FAIL_NULL_MSG(navmesh_generator_2d, "Failed to init NavMeshGenerator2D.");
#endif // CLIPPER2_ENABLED
}
void GodotNavigationServer2D::sync() {
#ifdef CLIPPER2_ENABLED
if (navmesh_generator_2d) {
navmesh_generator_2d->sync();
}
#endif // CLIPPER2_ENABLED
}
void GodotNavigationServer2D::finish() {
#ifdef CLIPPER2_ENABLED
if (navmesh_generator_2d) {
navmesh_generator_2d->finish();
memdelete(navmesh_generator_2d);
navmesh_generator_2d = nullptr;
}
#endif // CLIPPER2_ENABLED
}
void GodotNavigationServer2D::parse_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation polygon.");
ERR_FAIL_NULL_MSG(p_root_node, "No parsing root node specified.");
ERR_FAIL_COND_MSG(!p_root_node->is_inside_tree(), "The root node needs to be inside the SceneTree.");
#ifdef CLIPPER2_ENABLED
ERR_FAIL_NULL(NavMeshGenerator2D::get_singleton());
NavMeshGenerator2D::get_singleton()->parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node, p_callback);
#endif // CLIPPER2_ENABLED
}
void GodotNavigationServer2D::bake_from_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback) {
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation polygon.");
ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData2D.");
#ifdef CLIPPER2_ENABLED
ERR_FAIL_NULL(NavMeshGenerator2D::get_singleton());
NavMeshGenerator2D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
#endif // CLIPPER2_ENABLED
}
void GodotNavigationServer2D::bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback) {
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData2D.");
#ifdef CLIPPER2_ENABLED
ERR_FAIL_NULL(NavMeshGenerator2D::get_singleton());
NavMeshGenerator2D::get_singleton()->bake_from_source_geometry_data_async(p_navigation_mesh, p_source_geometry_data, p_callback);
#endif // CLIPPER2_ENABLED
}
GodotNavigationServer2D::GodotNavigationServer2D() {}
GodotNavigationServer2D::~GodotNavigationServer2D() {}
TypedArray<RID> FORWARD_0_C(get_maps);
TypedArray<RID> FORWARD_1_C(map_get_links, RID, p_map, rid_to_rid);
TypedArray<RID> FORWARD_1_C(map_get_regions, RID, p_map, rid_to_rid);
TypedArray<RID> FORWARD_1_C(map_get_agents, RID, p_map, rid_to_rid);
TypedArray<RID> FORWARD_1_C(map_get_obstacles, RID, p_map, rid_to_rid);
RID FORWARD_1_C(region_get_map, RID, p_region, rid_to_rid);
RID FORWARD_1_C(agent_get_map, RID, p_agent, rid_to_rid);
RID FORWARD_0(map_create);
void FORWARD_2(map_set_active, RID, p_map, bool, p_active, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(map_is_active, RID, p_map, rid_to_rid);
void GodotNavigationServer2D::map_force_update(RID p_map) {
NavigationServer3D::get_singleton()->map_force_update(p_map);
}
void FORWARD_2(map_set_cell_size, RID, p_map, real_t, p_cell_size, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_cell_size, RID, p_map, rid_to_rid);
void FORWARD_2(map_set_use_edge_connections, RID, p_map, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(map_get_use_edge_connections, RID, p_map, rid_to_rid);
void FORWARD_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid);
void FORWARD_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_link_connection_radius, RID, p_map, rid_to_rid);
Vector<Vector2> FORWARD_5_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, uint32_t, p_layers, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool, uint32_to_uint32);
Vector2 FORWARD_2_R_C(v3_to_v2, map_get_closest_point, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
RID FORWARD_0(region_create);
void FORWARD_2(region_set_enabled, RID, p_region, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(region_get_enabled, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_use_edge_connections, RID, p_region, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(region_get_use_edge_connections, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(region_get_enter_cost, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(region_get_travel_cost, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_owner_id, RID, p_region, ObjectID, p_owner_id, rid_to_rid, id_to_id);
ObjectID FORWARD_1_C(region_get_owner_id, RID, p_region, rid_to_rid);
bool FORWARD_2_C(region_owns_point, RID, p_region, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
void FORWARD_2(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
uint32_t FORWARD_1_C(region_get_navigation_layers, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3);
void GodotNavigationServer2D::region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) {
NavigationServer3D::get_singleton()->region_set_navigation_mesh(p_region, poly_to_mesh(p_navigation_polygon));
}
int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid);
Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_start, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int);
Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_end, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int);
RID FORWARD_0(link_create);
void FORWARD_2(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid);
RID FORWARD_1_C(link_get_map, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_enabled, RID, p_link, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(link_get_enabled, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(link_is_bidirectional, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
uint32_t FORWARD_1_C(link_get_navigation_layers, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_start_position, RID, p_link, Vector2, p_position, rid_to_rid, v2_to_v3);
Vector2 FORWARD_1_R_C(v3_to_v2, link_get_start_position, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_end_position, RID, p_link, Vector2, p_position, rid_to_rid, v2_to_v3);
Vector2 FORWARD_1_R_C(v3_to_v2, link_get_end_position, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(link_get_enter_cost, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(link_get_travel_cost, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_owner_id, RID, p_link, ObjectID, p_owner_id, rid_to_rid, id_to_id);
ObjectID FORWARD_1_C(link_get_owner_id, RID, p_link, rid_to_rid);
RID GodotNavigationServer2D::agent_create() {
RID agent = NavigationServer3D::get_singleton()->agent_create();
return agent;
}
void FORWARD_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(agent_get_avoidance_enabled, RID, p_agent, rid_to_rid);
void FORWARD_2(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int);
void FORWARD_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_velocity_forced, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3);
bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid);
void FORWARD_2(agent_set_paused, RID, p_agent, bool, p_paused, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(agent_get_paused, RID, p_agent, rid_to_rid);
void FORWARD_1(free, RID, p_object, rid_to_rid);
void FORWARD_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback, rid_to_rid, callable_to_callable);
void FORWARD_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
void FORWARD_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask, rid_to_rid, uint32_to_uint32);
void FORWARD_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority, rid_to_rid, real_to_real);
RID GodotNavigationServer2D::obstacle_create() {
RID obstacle = NavigationServer3D::get_singleton()->obstacle_create();
return obstacle;
}
void FORWARD_2(obstacle_set_avoidance_enabled, RID, p_obstacle, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(obstacle_get_avoidance_enabled, RID, p_obstacle, rid_to_rid);
void FORWARD_2(obstacle_set_map, RID, p_obstacle, RID, p_map, rid_to_rid, rid_to_rid);
RID FORWARD_1_C(obstacle_get_map, RID, p_obstacle, rid_to_rid);
void FORWARD_2(obstacle_set_paused, RID, p_obstacle, bool, p_paused, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(obstacle_get_paused, RID, p_obstacle, rid_to_rid);
void FORWARD_2(obstacle_set_radius, RID, p_obstacle, real_t, p_radius, rid_to_rid, real_to_real);
void FORWARD_2(obstacle_set_velocity, RID, p_obstacle, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(obstacle_set_position, RID, p_obstacle, Vector2, p_position, rid_to_rid, v2_to_v3);
void FORWARD_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
void GodotNavigationServer2D::obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) {
NavigationServer3D::get_singleton()->obstacle_set_vertices(p_obstacle, vector_v2_to_v3(p_vertices));
}
void GodotNavigationServer2D::query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const {
ERR_FAIL_COND(!p_query_parameters.is_valid());
ERR_FAIL_COND(!p_query_result.is_valid());
const NavigationUtilities::PathQueryResult _query_result = NavigationServer3D::get_singleton()->_query_path(p_query_parameters->get_parameters());
p_query_result->set_path(vector_v3_to_v2(_query_result.path));
p_query_result->set_path_types(_query_result.path_types);
p_query_result->set_path_rids(_query_result.path_rids);
p_query_result->set_path_owner_ids(_query_result.path_owner_ids);
}

View File

@ -0,0 +1,234 @@
/**************************************************************************/
/* godot_navigation_server_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GODOT_NAVIGATION_SERVER_2D_H
#define GODOT_NAVIGATION_SERVER_2D_H
#include "nav_agent.h"
#include "nav_link.h"
#include "nav_map.h"
#include "nav_obstacle.h"
#include "nav_region.h"
#include "servers/navigation_server_2d.h"
#ifdef CLIPPER2_ENABLED
class NavMeshGenerator2D;
#endif // CLIPPER2_ENABLED
// This server exposes the `NavigationServer3D` features in the 2D world.
class GodotNavigationServer2D : public NavigationServer2D {
GDCLASS(GodotNavigationServer2D, NavigationServer2D);
#ifdef CLIPPER2_ENABLED
NavMeshGenerator2D *navmesh_generator_2d = nullptr;
#endif // CLIPPER2_ENABLED
public:
GodotNavigationServer2D();
virtual ~GodotNavigationServer2D();
virtual TypedArray<RID> get_maps() const override;
virtual RID map_create() override;
virtual void map_set_active(RID p_map, bool p_active) override;
virtual bool map_is_active(RID p_map) const override;
virtual void map_set_cell_size(RID p_map, real_t p_cell_size) override;
virtual real_t map_get_cell_size(RID p_map) const override;
virtual void map_set_use_edge_connections(RID p_map, bool p_enabled) override;
virtual bool map_get_use_edge_connections(RID p_map) const override;
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) override;
virtual real_t map_get_edge_connection_margin(RID p_map) const override;
virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) override;
virtual real_t map_get_link_connection_radius(RID p_map) const override;
virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override;
virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const override;
virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const override;
virtual TypedArray<RID> map_get_links(RID p_map) const override;
virtual TypedArray<RID> map_get_regions(RID p_map) const override;
virtual TypedArray<RID> map_get_agents(RID p_map) const override;
virtual TypedArray<RID> map_get_obstacles(RID p_map) const override;
virtual void map_force_update(RID p_map) override;
virtual RID region_create() override;
virtual void region_set_enabled(RID p_region, bool p_enabled) override;
virtual bool region_get_enabled(RID p_region) const override;
virtual void region_set_use_edge_connections(RID p_region, bool p_enabled) override;
virtual bool region_get_use_edge_connections(RID p_region) const override;
virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) override;
virtual real_t region_get_enter_cost(RID p_region) const override;
virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) override;
virtual real_t region_get_travel_cost(RID p_region) const override;
virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id) override;
virtual ObjectID region_get_owner_id(RID p_region) const override;
virtual bool region_owns_point(RID p_region, const Vector2 &p_point) const override;
virtual void region_set_map(RID p_region, RID p_map) override;
virtual RID region_get_map(RID p_region) const override;
virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override;
virtual uint32_t region_get_navigation_layers(RID p_region) const override;
virtual void region_set_transform(RID p_region, Transform2D p_transform) override;
virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) override;
virtual int region_get_connections_count(RID p_region) const override;
virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override;
virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override;
virtual RID link_create() override;
/// Set the map of this link.
virtual void link_set_map(RID p_link, RID p_map) override;
virtual RID link_get_map(RID p_link) const override;
virtual void link_set_enabled(RID p_link, bool p_enabled) override;
virtual bool link_get_enabled(RID p_link) const override;
/// Set whether this link travels in both directions.
virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) override;
virtual bool link_is_bidirectional(RID p_link) const override;
/// Set the link's layers.
virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) override;
virtual uint32_t link_get_navigation_layers(RID p_link) const override;
/// Set the start position of the link.
virtual void link_set_start_position(RID p_link, Vector2 p_position) override;
virtual Vector2 link_get_start_position(RID p_link) const override;
/// Set the end position of the link.
virtual void link_set_end_position(RID p_link, Vector2 p_position) override;
virtual Vector2 link_get_end_position(RID p_link) const override;
/// Set the enter cost of the link.
virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) override;
virtual real_t link_get_enter_cost(RID p_link) const override;
/// Set the travel cost of the link.
virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) override;
virtual real_t link_get_travel_cost(RID p_link) const override;
/// Set the node which manages this link.
virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id) override;
virtual ObjectID link_get_owner_id(RID p_link) const override;
/// Creates the agent.
virtual RID agent_create() override;
/// Put the agent in the map.
virtual void agent_set_map(RID p_agent, RID p_map) override;
virtual RID agent_get_map(RID p_agent) const override;
virtual void agent_set_paused(RID p_agent, bool p_paused) override;
virtual bool agent_get_paused(RID p_agent) const override;
virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) override;
virtual bool agent_get_avoidance_enabled(RID p_agent) const override;
/// The maximum distance (center point to
/// center point) to other agents this agent
/// takes into account in the navigation. The
/// larger this number, the longer the running
/// time of the simulation. If the number is too
/// low, the simulation will not be safe.
/// Must be non-negative.
virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override;
/// The maximum number of other agents this
/// agent takes into account in the navigation.
/// The larger this number, the longer the
/// running time of the simulation. If the
/// number is too low, the simulation will not
/// be safe.
virtual void agent_set_max_neighbors(RID p_agent, int p_count) override;
/// The minimal amount of time for which this
/// agent's velocities that are computed by the
/// simulation are safe with respect to other
/// agents. The larger this number, the sooner
/// this agent will respond to the presence of
/// other agents, but the less freedom this
/// agent has in choosing its velocities.
/// Must be positive.
virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override;
virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override;
/// The radius of this agent.
/// Must be non-negative.
virtual void agent_set_radius(RID p_agent, real_t p_radius) override;
/// The maximum speed of this agent.
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) override;
/// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) override;
/// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
/// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) override;
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector2 p_position) override;
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const override;
/// Callback called at the end of the RVO process
virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override;
virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override;
virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override;
virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override;
virtual RID obstacle_create() override;
virtual void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) override;
virtual bool obstacle_get_avoidance_enabled(RID p_obstacle) const override;
virtual void obstacle_set_map(RID p_obstacle, RID p_map) override;
virtual RID obstacle_get_map(RID p_obstacle) const override;
virtual void obstacle_set_paused(RID p_obstacle, bool p_paused) override;
virtual bool obstacle_get_paused(RID p_obstacle) const override;
virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) override;
virtual void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) override;
virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) override;
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) override;
virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override;
virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const override;
virtual void init() override;
virtual void sync() override;
virtual void finish() override;
virtual void free(RID p_object) override;
virtual void parse_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
virtual void bake_from_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
virtual void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
};
#endif // GODOT_NAVIGATION_SERVER_2D_H

View File

@ -0,0 +1,830 @@
/**************************************************************************/
/* nav_mesh_generator_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "nav_mesh_generator_2d.h"
#include "core/config/project_settings.h"
#include "scene/2d/mesh_instance_2d.h"
#include "scene/2d/multimesh_instance_2d.h"
#include "scene/2d/physics_body_2d.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/tile_map.h"
#include "scene/resources/capsule_shape_2d.h"
#include "scene/resources/circle_shape_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/navigation_polygon.h"
#include "scene/resources/rectangle_shape_2d.h"
#include "thirdparty/clipper2/include/clipper2/clipper.h"
#include "thirdparty/misc/polypartition.h"
NavMeshGenerator2D *NavMeshGenerator2D::singleton = nullptr;
Mutex NavMeshGenerator2D::baking_navmesh_mutex;
Mutex NavMeshGenerator2D::generator_task_mutex;
bool NavMeshGenerator2D::use_threads = true;
bool NavMeshGenerator2D::baking_use_multiple_threads = true;
bool NavMeshGenerator2D::baking_use_high_priority_threads = true;
HashSet<Ref<NavigationPolygon>> NavMeshGenerator2D::baking_navmeshes;
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator2D::NavMeshGeneratorTask2D *> NavMeshGenerator2D::generator_tasks;
NavMeshGenerator2D *NavMeshGenerator2D::get_singleton() {
return singleton;
}
NavMeshGenerator2D::NavMeshGenerator2D() {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
baking_use_multiple_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_multiple_threads");
baking_use_high_priority_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_high_priority_threads");
// Using threads might cause problems on certain exports or with the Editor on certain devices.
// This is the main switch to turn threaded navmesh baking off should the need arise.
use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint();
}
NavMeshGenerator2D::~NavMeshGenerator2D() {
cleanup();
}
void NavMeshGenerator2D::sync() {
if (generator_tasks.size() == 0) {
return;
}
baking_navmesh_mutex.lock();
generator_task_mutex.lock();
LocalVector<WorkerThreadPool::TaskID> finished_task_ids;
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask2D *> &E : generator_tasks) {
if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) {
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
finished_task_ids.push_back(E.key);
NavMeshGeneratorTask2D *generator_task = E.value;
DEV_ASSERT(generator_task->status = NavMeshGeneratorTask2D::TaskStatus::BAKING_FINISHED);
baking_navmeshes.erase(generator_task->navigation_mesh);
if (generator_task->callback.is_valid()) {
generator_emit_callback(generator_task->callback);
}
memdelete(generator_task);
}
}
for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) {
generator_tasks.erase(finished_task_id);
}
generator_task_mutex.unlock();
baking_navmesh_mutex.unlock();
}
void NavMeshGenerator2D::cleanup() {
baking_navmesh_mutex.lock();
generator_task_mutex.lock();
baking_navmeshes.clear();
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask2D *> &E : generator_tasks) {
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
NavMeshGeneratorTask2D *generator_task = E.value;
memdelete(generator_task);
}
generator_tasks.clear();
generator_task_mutex.unlock();
baking_navmesh_mutex.unlock();
}
void NavMeshGenerator2D::finish() {
cleanup();
}
void NavMeshGenerator2D::parse_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
ERR_FAIL_COND(!Thread::is_main_thread());
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
ERR_FAIL_NULL(p_root_node);
ERR_FAIL_COND(!p_root_node->is_inside_tree());
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
generator_parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node);
if (p_callback.is_valid()) {
generator_emit_callback(p_callback);
}
}
void NavMeshGenerator2D::bake_from_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, const Callable &p_callback) {
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
if (p_navigation_mesh->get_outline_count() == 0 && !p_source_geometry_data->has_data()) {
p_navigation_mesh->clear();
if (p_callback.is_valid()) {
generator_emit_callback(p_callback);
}
return;
}
baking_navmesh_mutex.lock();
if (baking_navmeshes.has(p_navigation_mesh)) {
baking_navmesh_mutex.unlock();
ERR_FAIL_MSG("NavigationPolygon is already baking. Wait for current bake to finish.");
}
baking_navmeshes.insert(p_navigation_mesh);
baking_navmesh_mutex.unlock();
generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data);
baking_navmesh_mutex.lock();
baking_navmeshes.erase(p_navigation_mesh);
baking_navmesh_mutex.unlock();
if (p_callback.is_valid()) {
generator_emit_callback(p_callback);
}
}
void NavMeshGenerator2D::bake_from_source_geometry_data_async(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, const Callable &p_callback) {
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
if (p_navigation_mesh->get_outline_count() == 0 && !p_source_geometry_data->has_data()) {
p_navigation_mesh->clear();
if (p_callback.is_valid()) {
generator_emit_callback(p_callback);
}
return;
}
if (!use_threads) {
bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
return;
}
baking_navmesh_mutex.lock();
if (baking_navmeshes.has(p_navigation_mesh)) {
baking_navmesh_mutex.unlock();
ERR_FAIL_MSG("NavigationPolygon is already baking. Wait for current bake to finish.");
}
baking_navmeshes.insert(p_navigation_mesh);
baking_navmesh_mutex.unlock();
generator_task_mutex.lock();
NavMeshGeneratorTask2D *generator_task = memnew(NavMeshGeneratorTask2D);
generator_task->navigation_mesh = p_navigation_mesh;
generator_task->source_geometry_data = p_source_geometry_data;
generator_task->callback = p_callback;
generator_task->status = NavMeshGeneratorTask2D::TaskStatus::BAKING_STARTED;
generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator2D::generator_thread_bake, generator_task, NavMeshGenerator2D::baking_use_high_priority_threads, "NavMeshGeneratorBake2D");
generator_tasks.insert(generator_task->thread_task_id, generator_task);
generator_task_mutex.unlock();
}
void NavMeshGenerator2D::generator_thread_bake(void *p_arg) {
NavMeshGeneratorTask2D *generator_task = static_cast<NavMeshGeneratorTask2D *>(p_arg);
generator_bake_from_source_geometry_data(generator_task->navigation_mesh, generator_task->source_geometry_data);
generator_task->status = NavMeshGeneratorTask2D::TaskStatus::BAKING_FINISHED;
}
void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node, bool p_recurse_children) {
generator_parse_meshinstance2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_multimeshinstance2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_polygon2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_staticbody2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_tilemap_node(p_navigation_mesh, p_source_geometry_data, p_node);
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children);
}
}
}
void NavMeshGenerator2D::generator_parse_meshinstance2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
MeshInstance2D *mesh_instance = Object::cast_to<MeshInstance2D>(p_node);
if (mesh_instance == nullptr) {
return;
}
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
if (!(parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH)) {
return;
}
Ref<Mesh> mesh = mesh_instance->get_mesh();
if (!mesh.is_valid()) {
return;
}
const Transform2D mesh_instance_xform = p_source_geometry_data->root_node_transform * mesh_instance->get_global_transform();
using namespace Clipper2Lib;
Paths64 subject_paths, dummy_clip_paths;
for (int i = 0; i < mesh->get_surface_count(); i++) {
if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
continue;
}
if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FLAG_USE_2D_VERTICES)) {
continue;
}
Path64 subject_path;
int index_count = 0;
if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
index_count = mesh->surface_get_array_index_len(i);
} else {
index_count = mesh->surface_get_array_len(i);
}
ERR_CONTINUE((index_count == 0 || (index_count % 3) != 0));
Array a = mesh->surface_get_arrays(i);
Vector<Vector2> mesh_vertices = a[Mesh::ARRAY_VERTEX];
if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
Vector<int> mesh_indices = a[Mesh::ARRAY_INDEX];
for (int vertex_index : mesh_indices) {
const Vector2 &vertex = mesh_vertices[vertex_index];
const Point64 &point = Point64(vertex.x, vertex.y);
subject_path.push_back(point);
}
} else {
for (const Vector2 &vertex : mesh_vertices) {
const Point64 &point = Point64(vertex.x, vertex.y);
subject_path.push_back(point);
}
}
subject_paths.push_back(subject_path);
}
Paths64 path_solution;
path_solution = Union(subject_paths, dummy_clip_paths, FillRule::NonZero);
//path_solution = RamerDouglasPeucker(path_solution, 0.025);
Vector<Vector<Vector2>> polypaths;
for (const Path64 &scaled_path : path_solution) {
Vector<Vector2> shape_outline;
for (const Point64 &scaled_point : scaled_path) {
shape_outline.push_back(Point2(static_cast<real_t>(scaled_point.x), static_cast<real_t>(scaled_point.y)));
}
for (int i = 0; i < shape_outline.size(); i++) {
shape_outline.write[i] = mesh_instance_xform.xform(shape_outline[i]);
}
p_source_geometry_data->add_obstruction_outline(shape_outline);
}
}
void NavMeshGenerator2D::generator_parse_multimeshinstance2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
MultiMeshInstance2D *multimesh_instance = Object::cast_to<MultiMeshInstance2D>(p_node);
if (multimesh_instance == nullptr) {
return;
}
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
if (!(parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH)) {
return;
}
Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh();
if (!(multimesh.is_valid() && multimesh->get_transform_format() == MultiMesh::TRANSFORM_2D)) {
return;
}
Ref<Mesh> mesh = multimesh->get_mesh();
if (!mesh.is_valid()) {
return;
}
using namespace Clipper2Lib;
Paths64 mesh_subject_paths, dummy_clip_paths;
for (int i = 0; i < mesh->get_surface_count(); i++) {
if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
continue;
}
if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FLAG_USE_2D_VERTICES)) {
continue;
}
Path64 subject_path;
int index_count = 0;
if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
index_count = mesh->surface_get_array_index_len(i);
} else {
index_count = mesh->surface_get_array_len(i);
}
ERR_CONTINUE((index_count == 0 || (index_count % 3) != 0));
Array a = mesh->surface_get_arrays(i);
Vector<Vector2> mesh_vertices = a[Mesh::ARRAY_VERTEX];
if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
Vector<int> mesh_indices = a[Mesh::ARRAY_INDEX];
for (int vertex_index : mesh_indices) {
const Vector2 &vertex = mesh_vertices[vertex_index];
const Point64 &point = Point64(vertex.x, vertex.y);
subject_path.push_back(point);
}
} else {
for (const Vector2 &vertex : mesh_vertices) {
const Point64 &point = Point64(vertex.x, vertex.y);
subject_path.push_back(point);
}
}
mesh_subject_paths.push_back(subject_path);
}
Paths64 mesh_path_solution = Union(mesh_subject_paths, dummy_clip_paths, FillRule::NonZero);
//path_solution = RamerDouglasPeucker(path_solution, 0.025);
int multimesh_instance_count = multimesh->get_visible_instance_count();
if (multimesh_instance_count == -1) {
multimesh_instance_count = multimesh->get_instance_count();
}
const Transform2D multimesh_instance_xform = p_source_geometry_data->root_node_transform * multimesh_instance->get_global_transform();
for (int i = 0; i < multimesh_instance_count; i++) {
const Transform2D multimesh_instance_mesh_instance_xform = multimesh_instance_xform * multimesh->get_instance_transform_2d(i);
for (const Path64 &mesh_path : mesh_path_solution) {
Vector<Vector2> shape_outline;
for (const Point64 &mesh_path_point : mesh_path) {
shape_outline.push_back(Point2(static_cast<real_t>(mesh_path_point.x), static_cast<real_t>(mesh_path_point.y)));
}
for (int j = 0; j < shape_outline.size(); j++) {
shape_outline.write[j] = multimesh_instance_mesh_instance_xform.xform(shape_outline[j]);
}
p_source_geometry_data->add_obstruction_outline(shape_outline);
}
}
}
void NavMeshGenerator2D::generator_parse_polygon2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
Polygon2D *polygon_2d = Object::cast_to<Polygon2D>(p_node);
if (polygon_2d == nullptr) {
return;
}
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
if (parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) {
const Transform2D polygon_2d_xform = p_source_geometry_data->root_node_transform * polygon_2d->get_global_transform();
Vector<Vector2> shape_outline = polygon_2d->get_polygon();
for (int i = 0; i < shape_outline.size(); i++) {
shape_outline.write[i] = polygon_2d_xform.xform(shape_outline[i]);
}
p_source_geometry_data->add_obstruction_outline(shape_outline);
}
}
void NavMeshGenerator2D::generator_parse_staticbody2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
StaticBody2D *static_body = Object::cast_to<StaticBody2D>(p_node);
if (static_body == nullptr) {
return;
}
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
if (!(parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH)) {
return;
}
uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask();
if (!(static_body->get_collision_layer() & parsed_collision_mask)) {
return;
}
List<uint32_t> shape_owners;
static_body->get_shape_owners(&shape_owners);
for (uint32_t shape_owner : shape_owners) {
if (static_body->is_shape_owner_disabled(shape_owner)) {
continue;
}
const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
for (int shape_index = 0; shape_index < shape_count; shape_index++) {
Ref<Shape2D> s = static_body->shape_owner_get_shape(shape_owner, shape_index);
if (s.is_null()) {
continue;
}
const Transform2D static_body_xform = p_source_geometry_data->root_node_transform * static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner);
RectangleShape2D *rectangle_shape = Object::cast_to<RectangleShape2D>(*s);
if (rectangle_shape) {
Vector<Vector2> shape_outline;
const Vector2 &rectangle_size = rectangle_shape->get_size();
shape_outline.resize(5);
shape_outline.write[0] = static_body_xform.xform(-rectangle_size * 0.5);
shape_outline.write[1] = static_body_xform.xform(Vector2(rectangle_size.x, -rectangle_size.y) * 0.5);
shape_outline.write[2] = static_body_xform.xform(rectangle_size * 0.5);
shape_outline.write[3] = static_body_xform.xform(Vector2(-rectangle_size.x, rectangle_size.y) * 0.5);
shape_outline.write[4] = static_body_xform.xform(-rectangle_size * 0.5);
p_source_geometry_data->add_obstruction_outline(shape_outline);
}
CapsuleShape2D *capsule_shape = Object::cast_to<CapsuleShape2D>(*s);
if (capsule_shape) {
const real_t capsule_height = capsule_shape->get_height();
const real_t capsule_radius = capsule_shape->get_radius();
Vector<Vector2> shape_outline;
const real_t turn_step = Math_TAU / 12.0;
shape_outline.resize(14);
int shape_outline_inx = 0;
for (int i = 0; i < 12; i++) {
Vector2 ofs = Vector2(0, (i > 3 && i <= 9) ? -capsule_height * 0.5 + capsule_radius : capsule_height * 0.5 - capsule_radius);
shape_outline.write[shape_outline_inx] = static_body_xform.xform(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * capsule_radius + ofs);
shape_outline_inx += 1;
if (i == 3 || i == 9) {
shape_outline.write[shape_outline_inx] = static_body_xform.xform(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * capsule_radius - ofs);
shape_outline_inx += 1;
}
}
p_source_geometry_data->add_obstruction_outline(shape_outline);
}
CircleShape2D *circle_shape = Object::cast_to<CircleShape2D>(*s);
if (circle_shape) {
const real_t circle_radius = circle_shape->get_radius();
Vector<Vector2> shape_outline;
int circle_edge_count = 12;
shape_outline.resize(circle_edge_count);
const real_t turn_step = Math_TAU / real_t(circle_edge_count);
for (int i = 0; i < circle_edge_count; i++) {
shape_outline.write[i] = static_body_xform.xform(Vector2(Math::cos(i * turn_step), Math::sin(i * turn_step)) * circle_radius);
}
p_source_geometry_data->add_obstruction_outline(shape_outline);
}
ConcavePolygonShape2D *concave_polygon_shape = Object::cast_to<ConcavePolygonShape2D>(*s);
if (concave_polygon_shape) {
Vector<Vector2> shape_outline = concave_polygon_shape->get_segments();
for (int i = 0; i < shape_outline.size(); i++) {
shape_outline.write[i] = static_body_xform.xform(shape_outline[i]);
}
p_source_geometry_data->add_obstruction_outline(shape_outline);
}
ConvexPolygonShape2D *convex_polygon_shape = Object::cast_to<ConvexPolygonShape2D>(*s);
if (convex_polygon_shape) {
Vector<Vector2> shape_outline = convex_polygon_shape->get_points();
for (int i = 0; i < shape_outline.size(); i++) {
shape_outline.write[i] = static_body_xform.xform(shape_outline[i]);
}
p_source_geometry_data->add_obstruction_outline(shape_outline);
}
}
}
}
void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
TileMap *tilemap = Object::cast_to<TileMap>(p_node);
if (tilemap == nullptr) {
return;
}
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask();
if (tilemap->get_layers_count() <= 0) {
return;
}
int tilemap_layer = 0; // only main tile map layer is supported
Ref<TileSet> tile_set = tilemap->get_tileset();
if (!tile_set.is_valid()) {
return;
}
int physics_layers_count = tile_set->get_physics_layers_count();
int navigation_layers_count = tile_set->get_navigation_layers_count();
if (physics_layers_count <= 0 && navigation_layers_count <= 0) {
return;
}
const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tilemap->get_global_transform();
TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer);
for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
const Vector2i &cell = used_cells[used_cell_index];
const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false);
Transform2D tile_transform;
tile_transform.set_origin(tilemap->map_to_local(cell));
const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
if (navigation_layers_count > 0) {
Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer);
if (navigation_polygon.is_valid()) {
for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
Vector<Vector2> traversable_outline = navigation_polygon->get_outline(outline_index);
for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
traversable_outline.write[traversable_outline_index] = tile_transform_offset.xform(traversable_outline[traversable_outline_index]);
}
p_source_geometry_data->_add_traversable_outline(traversable_outline);
}
}
}
if (physics_layers_count > 0 && (parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) && (tile_set->get_physics_layer_collision_layer(tilemap_layer) & parsed_collision_mask)) {
for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(tilemap_layer); collision_polygon_index++) {
Vector<Vector2> obstruction_outline = tile_data->get_collision_polygon_points(tilemap_layer, collision_polygon_index);
for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
obstruction_outline.write[obstruction_outline_index] = tile_transform_offset.xform(obstruction_outline[obstruction_outline_index]);
}
p_source_geometry_data->_add_obstruction_outline(obstruction_outline);
}
}
}
}
void NavMeshGenerator2D::generator_parse_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_root_node) {
List<Node *> parse_nodes;
if (p_navigation_mesh->get_source_geometry_mode() == NavigationPolygon::SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) {
parse_nodes.push_back(p_root_node);
} else {
p_root_node->get_tree()->get_nodes_in_group(p_navigation_mesh->get_source_geometry_group_name(), &parse_nodes);
}
Transform2D root_node_transform = Transform2D();
if (Object::cast_to<Node2D>(p_root_node)) {
root_node_transform = Object::cast_to<Node2D>(p_root_node)->get_global_transform().affine_inverse();
}
p_source_geometry_data->clear();
p_source_geometry_data->root_node_transform = root_node_transform;
bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationPolygon::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
for (Node *E : parse_nodes) {
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, E, recurse_children);
}
};
static void generator_recursive_process_polytree_items(List<TPPLPoly> &p_tppl_in_polygon, const Clipper2Lib::PolyPath64 *p_polypath_item) {
using namespace Clipper2Lib;
Vector<Vector2> polygon_vertices;
for (const Point64 &polypath_point : p_polypath_item->Polygon()) {
polygon_vertices.push_back(Vector2(static_cast<real_t>(polypath_point.x), static_cast<real_t>(polypath_point.y)));
}
TPPLPoly tp;
tp.Init(polygon_vertices.size());
for (int j = 0; j < polygon_vertices.size(); j++) {
tp[j] = polygon_vertices[j];
}
if (p_polypath_item->IsHole()) {
tp.SetOrientation(TPPL_ORIENTATION_CW);
tp.SetHole(true);
} else {
tp.SetOrientation(TPPL_ORIENTATION_CCW);
}
p_tppl_in_polygon.push_back(tp);
for (size_t i = 0; i < p_polypath_item->Count(); i++) {
const PolyPath64 *polypath_item = p_polypath_item->Child(i);
generator_recursive_process_polytree_items(p_tppl_in_polygon, polypath_item);
}
}
bool NavMeshGenerator2D::generator_emit_callback(const Callable &p_callback) {
ERR_FAIL_COND_V(!p_callback.is_valid(), false);
Callable::CallError ce;
Variant result;
p_callback.callp(nullptr, 0, result, ce);
return ce.error == Callable::CallError::CALL_OK;
}
void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data) {
if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) {
return;
}
if (p_navigation_mesh->get_outline_count() == 0 && !p_source_geometry_data->has_data()) {
return;
}
int outline_count = p_navigation_mesh->get_outline_count();
const Vector<Vector<Vector2>> &traversable_outlines = p_source_geometry_data->_get_traversable_outlines();
const Vector<Vector<Vector2>> &obstruction_outlines = p_source_geometry_data->_get_obstruction_outlines();
if (outline_count == 0 && traversable_outlines.size() == 0) {
return;
}
using namespace Clipper2Lib;
Paths64 traversable_polygon_paths;
Paths64 obstruction_polygon_paths;
for (int i = 0; i < outline_count; i++) {
const Vector<Vector2> &traversable_outline = p_navigation_mesh->get_outline(i);
Path64 subject_path;
for (const Vector2 &traversable_point : traversable_outline) {
const Point64 &point = Point64(traversable_point.x, traversable_point.y);
subject_path.push_back(point);
}
traversable_polygon_paths.push_back(subject_path);
}
for (const Vector<Vector2> &traversable_outline : traversable_outlines) {
Path64 subject_path;
for (const Vector2 &traversable_point : traversable_outline) {
const Point64 &point = Point64(traversable_point.x, traversable_point.y);
subject_path.push_back(point);
}
traversable_polygon_paths.push_back(subject_path);
}
for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) {
Path64 clip_path;
for (const Vector2 &obstruction_point : obstruction_outline) {
const Point64 &point = Point64(obstruction_point.x, obstruction_point.y);
clip_path.push_back(point);
}
obstruction_polygon_paths.push_back(clip_path);
}
Paths64 path_solution;
// first merge all traversable polygons according to user specified fill rule
Paths64 dummy_clip_path;
traversable_polygon_paths = Union(traversable_polygon_paths, dummy_clip_path, FillRule::NonZero);
// merge all obstruction polygons, don't allow holes for what is considered "solid" 2D geometry
obstruction_polygon_paths = Union(obstruction_polygon_paths, dummy_clip_path, FillRule::NonZero);
path_solution = Difference(traversable_polygon_paths, obstruction_polygon_paths, FillRule::NonZero);
real_t agent_radius_offset = p_navigation_mesh->get_agent_radius();
if (agent_radius_offset > 0.0) {
path_solution = InflatePaths(path_solution, -agent_radius_offset, JoinType::Miter, EndType::Polygon);
}
//path_solution = RamerDouglasPeucker(path_solution, 0.025); //
Vector<Vector<Vector2>> new_baked_outlines;
for (const Path64 &scaled_path : path_solution) {
Vector<Vector2> polypath;
for (const Point64 &scaled_point : scaled_path) {
polypath.push_back(Vector2(static_cast<real_t>(scaled_point.x), static_cast<real_t>(scaled_point.y)));
}
new_baked_outlines.push_back(polypath);
}
if (new_baked_outlines.size() == 0) {
p_navigation_mesh->set_vertices(Vector<Vector2>());
p_navigation_mesh->clear_polygons();
return;
}
Paths64 polygon_paths;
for (const Vector<Vector2> &baked_outline : new_baked_outlines) {
Path64 polygon_path;
for (const Vector2 &baked_outline_point : baked_outline) {
const Point64 &point = Point64(baked_outline_point.x, baked_outline_point.y);
polygon_path.push_back(point);
}
polygon_paths.push_back(polygon_path);
}
ClipType clipper_cliptype = ClipType::Union;
List<TPPLPoly> tppl_in_polygon, tppl_out_polygon;
PolyTree64 polytree;
Clipper64 clipper_64;
clipper_64.AddSubject(polygon_paths);
clipper_64.Execute(clipper_cliptype, FillRule::NonZero, polytree);
for (size_t i = 0; i < polytree.Count(); i++) {
const PolyPath64 *polypath_item = polytree[i];
generator_recursive_process_polytree_items(tppl_in_polygon, polypath_item);
}
TPPLPartition tpart;
if (tpart.ConvexPartition_HM(&tppl_in_polygon, &tppl_out_polygon) == 0) { //failed!
ERR_PRINT("NavigationPolygon Convex partition failed. Unable to create a valid NavigationMesh from defined polygon outline paths.");
p_navigation_mesh->set_vertices(Vector<Vector2>());
p_navigation_mesh->clear_polygons();
return;
}
Vector<Vector2> new_vertices;
Vector<Vector<int>> new_polygons;
HashMap<Vector2, int> points;
for (List<TPPLPoly>::Element *I = tppl_out_polygon.front(); I; I = I->next()) {
TPPLPoly &tp = I->get();
Vector<int> new_polygon;
for (int64_t i = 0; i < tp.GetNumPoints(); i++) {
HashMap<Vector2, int>::Iterator E = points.find(tp[i]);
if (!E) {
E = points.insert(tp[i], new_vertices.size());
new_vertices.push_back(tp[i]);
}
new_polygon.push_back(E->value);
}
new_polygons.push_back(new_polygon);
}
p_navigation_mesh->set_vertices(new_vertices);
p_navigation_mesh->clear_polygons();
for (int i = 0; i < new_polygons.size(); i++) {
p_navigation_mesh->add_polygon(new_polygons[i]);
}
}

View File

@ -0,0 +1,100 @@
/**************************************************************************/
/* nav_mesh_generator_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NAV_MESH_GENERATOR_2D_H
#define NAV_MESH_GENERATOR_2D_H
#include "core/object/class_db.h"
#include "core/object/worker_thread_pool.h"
class Node;
class NavigationPolygon;
class NavigationMeshSourceGeometryData2D;
class NavMeshGenerator2D : public Object {
static NavMeshGenerator2D *singleton;
static Mutex baking_navmesh_mutex;
static Mutex generator_task_mutex;
static bool use_threads;
static bool baking_use_multiple_threads;
static bool baking_use_high_priority_threads;
struct NavMeshGeneratorTask2D {
enum TaskStatus {
BAKING_STARTED,
BAKING_FINISHED,
BAKING_FAILED,
CALLBACK_DISPATCHED,
CALLBACK_FAILED,
};
Ref<NavigationPolygon> navigation_mesh;
Ref<NavigationMeshSourceGeometryData2D> source_geometry_data;
Callable callback;
WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
NavMeshGeneratorTask2D::TaskStatus status = NavMeshGeneratorTask2D::TaskStatus::BAKING_STARTED;
};
static HashMap<WorkerThreadPool::TaskID, NavMeshGeneratorTask2D *> generator_tasks;
static void generator_thread_bake(void *p_arg);
static HashSet<Ref<NavigationPolygon>> baking_navmeshes;
static void generator_parse_geometry_node(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node, bool p_recurse_children);
static void generator_parse_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_root_node);
static void generator_bake_from_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data);
static void generator_parse_meshinstance2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_multimeshinstance2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_polygon2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_staticbody2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static bool generator_emit_callback(const Callable &p_callback);
public:
static NavMeshGenerator2D *get_singleton();
static void sync();
static void cleanup();
static void finish();
static void parse_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
static void bake_from_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, const Callable &p_callback = Callable());
static void bake_from_source_geometry_data_async(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, const Callable &p_callback = Callable());
NavMeshGenerator2D();
~NavMeshGenerator2D();
};
#endif // NAV_MESH_GENERATOR_2D_H

View File

@ -31,6 +31,7 @@
#include "register_types.h" #include "register_types.h"
#include "godot_navigation_server.h" #include "godot_navigation_server.h"
#include "godot_navigation_server_2d.h"
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
@ -43,6 +44,7 @@
#endif #endif
#include "core/config/engine.h" #include "core/config/engine.h"
#include "servers/navigation_server_2d.h"
#include "servers/navigation_server_3d.h" #include "servers/navigation_server_3d.h"
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
@ -55,9 +57,14 @@ NavigationServer3D *new_server() {
return memnew(GodotNavigationServer); return memnew(GodotNavigationServer);
} }
NavigationServer2D *new_navigation_server_2d() {
return memnew(GodotNavigationServer2D);
}
void initialize_navigation_module(ModuleInitializationLevel p_level) { void initialize_navigation_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
NavigationServer3DManager::set_default_server(new_server); NavigationServer3DManager::set_default_server(new_server);
NavigationServer2DManager::set_default_server(new_navigation_server_2d);
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
#ifndef _3D_DISABLED #ifndef _3D_DISABLED

View File

@ -179,10 +179,6 @@ void NavigationRegion2D::_notification(int p_what) {
} }
void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon) { void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon) {
if (p_navigation_polygon == navigation_polygon) {
return;
}
if (navigation_polygon.is_valid()) { if (navigation_polygon.is_valid()) {
navigation_polygon->disconnect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); navigation_polygon->disconnect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed));
} }
@ -226,6 +222,32 @@ RID NavigationRegion2D::get_navigation_map() const {
return RID(); return RID();
} }
void NavigationRegion2D::bake_navigation_polygon(bool p_on_thread) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
ERR_FAIL_COND_MSG(!navigation_polygon.is_valid(), "Baking the navigation polygon requires a valid `NavigationPolygon` resource.");
Ref<NavigationMeshSourceGeometryData2D> source_geometry_data;
source_geometry_data.instantiate();
NavigationServer2D::get_singleton()->parse_source_geometry_data(navigation_polygon, source_geometry_data, this);
if (p_on_thread) {
NavigationServer2D::get_singleton()->bake_from_source_geometry_data_async(navigation_polygon, source_geometry_data, callable_mp(this, &NavigationRegion2D::_bake_finished).bind(navigation_polygon));
} else {
NavigationServer2D::get_singleton()->bake_from_source_geometry_data(navigation_polygon, source_geometry_data, callable_mp(this, &NavigationRegion2D::_bake_finished).bind(navigation_polygon));
}
}
void NavigationRegion2D::_bake_finished(Ref<NavigationPolygon> p_navigation_polygon) {
if (!Thread::is_main_thread()) {
call_deferred(SNAME("_bake_finished"), p_navigation_polygon);
return;
}
set_navigation_polygon(p_navigation_polygon);
emit_signal(SNAME("bake_finished"));
}
void NavigationRegion2D::_navigation_polygon_changed() { void NavigationRegion2D::_navigation_polygon_changed() {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) { if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
queue_redraw(); queue_redraw();
@ -290,6 +312,8 @@ void NavigationRegion2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationRegion2D::set_travel_cost); ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationRegion2D::set_travel_cost);
ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationRegion2D::get_travel_cost); ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationRegion2D::get_travel_cost);
ClassDB::bind_method(D_METHOD("bake_navigation_polygon", "on_thread"), &NavigationRegion2D::bake_navigation_polygon, DEFVAL(true));
ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationRegion2D::_navigation_polygon_changed); ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationRegion2D::_navigation_polygon_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon");
@ -300,6 +324,9 @@ void NavigationRegion2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance"), "set_constrain_avoidance", "get_constrain_avoidance"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance"), "set_constrain_avoidance", "get_constrain_avoidance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
ADD_SIGNAL(MethodInfo("navigation_polygon_changed"));
ADD_SIGNAL(MethodInfo("bake_finished"));
} }
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED

View File

@ -114,6 +114,9 @@ public:
PackedStringArray get_configuration_warnings() const override; PackedStringArray get_configuration_warnings() const override;
void bake_navigation_polygon(bool p_on_thread);
void _bake_finished(Ref<NavigationPolygon> p_navigation_polygon);
NavigationRegion2D(); NavigationRegion2D();
~NavigationRegion2D(); ~NavigationRegion2D();

View File

@ -172,6 +172,7 @@
#include "scene/resources/mesh_texture.h" #include "scene/resources/mesh_texture.h"
#include "scene/resources/multimesh.h" #include "scene/resources/multimesh.h"
#include "scene/resources/navigation_mesh.h" #include "scene/resources/navigation_mesh.h"
#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/navigation_mesh_source_geometry_data_3d.h" #include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/navigation_polygon.h" #include "scene/resources/navigation_polygon.h"
#include "scene/resources/packed_scene.h" #include "scene/resources/packed_scene.h"
@ -956,6 +957,7 @@ void register_scene_types() {
GDREGISTER_CLASS(PathFollow2D); GDREGISTER_CLASS(PathFollow2D);
GDREGISTER_CLASS(NavigationMesh); GDREGISTER_CLASS(NavigationMesh);
GDREGISTER_CLASS(NavigationMeshSourceGeometryData2D);
GDREGISTER_CLASS(NavigationMeshSourceGeometryData3D); GDREGISTER_CLASS(NavigationMeshSourceGeometryData3D);
GDREGISTER_CLASS(NavigationPolygon); GDREGISTER_CLASS(NavigationPolygon);
GDREGISTER_CLASS(NavigationRegion2D); GDREGISTER_CLASS(NavigationRegion2D);

View File

@ -0,0 +1,138 @@
/**************************************************************************/
/* navigation_mesh_source_geometry_data_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/mesh.h"
void NavigationMeshSourceGeometryData2D::clear() {
traversable_outlines.clear();
obstruction_outlines.clear();
}
void NavigationMeshSourceGeometryData2D::_set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines) {
traversable_outlines = p_traversable_outlines;
}
void NavigationMeshSourceGeometryData2D::_set_obstruction_outlines(const Vector<Vector<Vector2>> &p_obstruction_outlines) {
obstruction_outlines = p_obstruction_outlines;
}
void NavigationMeshSourceGeometryData2D::_add_traversable_outline(const Vector<Vector2> &p_shape_outline) {
if (p_shape_outline.size() > 1) {
traversable_outlines.push_back(p_shape_outline);
}
}
void NavigationMeshSourceGeometryData2D::_add_obstruction_outline(const Vector<Vector2> &p_shape_outline) {
if (p_shape_outline.size() > 1) {
obstruction_outlines.push_back(p_shape_outline);
}
}
void NavigationMeshSourceGeometryData2D::set_traversable_outlines(const TypedArray<Vector<Vector2>> &p_traversable_outlines) {
traversable_outlines.resize(p_traversable_outlines.size());
for (int i = 0; i < p_traversable_outlines.size(); i++) {
traversable_outlines.write[i] = p_traversable_outlines[i];
}
}
TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_traversable_outlines() const {
TypedArray<Vector<Vector2>> typed_array_traversable_outlines;
typed_array_traversable_outlines.resize(traversable_outlines.size());
for (int i = 0; i < typed_array_traversable_outlines.size(); i++) {
typed_array_traversable_outlines[i] = traversable_outlines[i];
}
return typed_array_traversable_outlines;
}
void NavigationMeshSourceGeometryData2D::set_obstruction_outlines(const TypedArray<Vector<Vector2>> &p_obstruction_outlines) {
obstruction_outlines.resize(p_obstruction_outlines.size());
for (int i = 0; i < p_obstruction_outlines.size(); i++) {
obstruction_outlines.write[i] = p_obstruction_outlines[i];
}
}
TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_obstruction_outlines() const {
TypedArray<Vector<Vector2>> typed_array_obstruction_outlines;
typed_array_obstruction_outlines.resize(obstruction_outlines.size());
for (int i = 0; i < typed_array_obstruction_outlines.size(); i++) {
typed_array_obstruction_outlines[i] = obstruction_outlines[i];
}
return typed_array_obstruction_outlines;
}
void NavigationMeshSourceGeometryData2D::add_traversable_outline(const PackedVector2Array &p_shape_outline) {
if (p_shape_outline.size() > 1) {
Vector<Vector2> traversable_outline;
traversable_outline.resize(p_shape_outline.size());
for (int i = 0; i < p_shape_outline.size(); i++) {
traversable_outline.write[i] = p_shape_outline[i];
}
traversable_outlines.push_back(traversable_outline);
}
}
void NavigationMeshSourceGeometryData2D::add_obstruction_outline(const PackedVector2Array &p_shape_outline) {
if (p_shape_outline.size() > 1) {
Vector<Vector2> obstruction_outline;
obstruction_outline.resize(p_shape_outline.size());
for (int i = 0; i < p_shape_outline.size(); i++) {
obstruction_outline.write[i] = p_shape_outline[i];
}
obstruction_outlines.push_back(obstruction_outline);
}
}
void NavigationMeshSourceGeometryData2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &NavigationMeshSourceGeometryData2D::clear);
ClassDB::bind_method(D_METHOD("has_data"), &NavigationMeshSourceGeometryData2D::has_data);
ClassDB::bind_method(D_METHOD("set_traversable_outlines", "traversable_outlines"), &NavigationMeshSourceGeometryData2D::set_traversable_outlines);
ClassDB::bind_method(D_METHOD("get_traversable_outlines"), &NavigationMeshSourceGeometryData2D::get_traversable_outlines);
ClassDB::bind_method(D_METHOD("set_obstruction_outlines", "obstruction_outlines"), &NavigationMeshSourceGeometryData2D::set_obstruction_outlines);
ClassDB::bind_method(D_METHOD("get_obstruction_outlines"), &NavigationMeshSourceGeometryData2D::get_obstruction_outlines);
ClassDB::bind_method(D_METHOD("add_traversable_outline", "shape_outline"), &NavigationMeshSourceGeometryData2D::add_traversable_outline);
ClassDB::bind_method(D_METHOD("add_obstruction_outline", "shape_outline"), &NavigationMeshSourceGeometryData2D::add_obstruction_outline);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "traversable_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_traversable_outlines", "get_traversable_outlines");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "obstruction_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_obstruction_outlines", "get_obstruction_outlines");
}
NavigationMeshSourceGeometryData2D::NavigationMeshSourceGeometryData2D() {
}
NavigationMeshSourceGeometryData2D::~NavigationMeshSourceGeometryData2D() {
clear();
}

View File

@ -0,0 +1,78 @@
/**************************************************************************/
/* navigation_mesh_source_geometry_data_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
#define NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
#include "scene/2d/node_2d.h"
#include "scene/resources/navigation_polygon.h"
class NavigationMeshSourceGeometryData2D : public Resource {
GDCLASS(NavigationMeshSourceGeometryData2D, Resource);
Vector<Vector<Vector2>> traversable_outlines;
Vector<Vector<Vector2>> obstruction_outlines;
protected:
static void _bind_methods();
public:
void _set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines);
const Vector<Vector<Vector2>> &_get_traversable_outlines() const { return traversable_outlines; }
void _set_obstruction_outlines(const Vector<Vector<Vector2>> &p_obstruction_outlines);
const Vector<Vector<Vector2>> &_get_obstruction_outlines() const { return obstruction_outlines; }
void _add_traversable_outline(const Vector<Vector2> &p_shape_outline);
void _add_obstruction_outline(const Vector<Vector2> &p_shape_outline);
// kept root node transform here on the geometry data
// if we add this transform to all exposed functions we need to break comp on all functions later
// when navmesh changes from global transform to relative to navregion
// but if it stays here we can just remove it and change the internal functions only
Transform2D root_node_transform;
void set_traversable_outlines(const TypedArray<Vector<Vector2>> &p_traversable_outlines);
TypedArray<Vector<Vector2>> get_traversable_outlines() const;
void set_obstruction_outlines(const TypedArray<Vector<Vector2>> &p_obstruction_outlines);
TypedArray<Vector<Vector2>> get_obstruction_outlines() const;
void add_traversable_outline(const PackedVector2Array &p_shape_outline);
void add_obstruction_outline(const PackedVector2Array &p_shape_outline);
bool has_data() { return traversable_outlines.size(); };
void clear();
NavigationMeshSourceGeometryData2D();
~NavigationMeshSourceGeometryData2D();
};
#endif // NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H

View File

@ -32,6 +32,7 @@
#include "core/math/geometry_2d.h" #include "core/math/geometry_2d.h"
#include "core/os/mutex.h" #include "core/os/mutex.h"
#include "servers/navigation_server_2d.h"
#include "thirdparty/misc/polypartition.h" #include "thirdparty/misc/polypartition.h"
@ -229,7 +230,11 @@ void NavigationPolygon::clear_outlines() {
rect_cache_dirty = true; rect_cache_dirty = true;
} }
#ifndef DISABLE_DEPRECATED
void NavigationPolygon::make_polygons_from_outlines() { void NavigationPolygon::make_polygons_from_outlines() {
WARN_PRINT("Function make_polygons_from_outlines() is deprecated."
"\nUse NavigationServer2D.parse_source_geometry_data() and NavigationServer2D.bake_from_source_geometry_data() instead.");
{ {
MutexLock lock(navigation_mesh_generation); MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref(); navigation_mesh.unref();
@ -331,6 +336,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
emit_changed(); emit_changed();
} }
#endif // DISABLE_DEPRECATED
void NavigationPolygon::set_cell_size(real_t p_cell_size) { void NavigationPolygon::set_cell_size(real_t p_cell_size) {
cell_size = p_cell_size; cell_size = p_cell_size;
@ -341,6 +347,69 @@ real_t NavigationPolygon::get_cell_size() const {
return cell_size; return cell_size;
} }
void NavigationPolygon::set_parsed_geometry_type(ParsedGeometryType p_geometry_type) {
ERR_FAIL_INDEX(p_geometry_type, PARSED_GEOMETRY_MAX);
parsed_geometry_type = p_geometry_type;
notify_property_list_changed();
}
NavigationPolygon::ParsedGeometryType NavigationPolygon::get_parsed_geometry_type() const {
return parsed_geometry_type;
}
void NavigationPolygon::set_parsed_collision_mask(uint32_t p_mask) {
parsed_collision_mask = p_mask;
}
uint32_t NavigationPolygon::get_parsed_collision_mask() const {
return parsed_collision_mask;
}
void NavigationPolygon::set_parsed_collision_mask_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
uint32_t mask = get_parsed_collision_mask();
if (p_value) {
mask |= 1 << (p_layer_number - 1);
} else {
mask &= ~(1 << (p_layer_number - 1));
}
set_parsed_collision_mask(mask);
}
bool NavigationPolygon::get_parsed_collision_mask_value(int p_layer_number) const {
ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
return get_parsed_collision_mask() & (1 << (p_layer_number - 1));
}
void NavigationPolygon::set_source_geometry_mode(SourceGeometryMode p_geometry_mode) {
ERR_FAIL_INDEX(p_geometry_mode, SOURCE_GEOMETRY_MAX);
source_geometry_mode = p_geometry_mode;
notify_property_list_changed();
}
NavigationPolygon::SourceGeometryMode NavigationPolygon::get_source_geometry_mode() const {
return source_geometry_mode;
}
void NavigationPolygon::set_source_geometry_group_name(StringName p_group_name) {
source_geometry_group_name = p_group_name;
}
StringName NavigationPolygon::get_source_geometry_group_name() const {
return source_geometry_group_name;
}
void NavigationPolygon::set_agent_radius(real_t p_value) {
ERR_FAIL_COND(p_value < 0);
agent_radius = p_value;
}
real_t NavigationPolygon::get_agent_radius() const {
return agent_radius;
}
void NavigationPolygon::_bind_methods() { void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices); ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices);
ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices); ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices);
@ -358,7 +427,9 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_outline", "idx"), &NavigationPolygon::get_outline); ClassDB::bind_method(D_METHOD("get_outline", "idx"), &NavigationPolygon::get_outline);
ClassDB::bind_method(D_METHOD("remove_outline", "idx"), &NavigationPolygon::remove_outline); ClassDB::bind_method(D_METHOD("remove_outline", "idx"), &NavigationPolygon::remove_outline);
ClassDB::bind_method(D_METHOD("clear_outlines"), &NavigationPolygon::clear_outlines); ClassDB::bind_method(D_METHOD("clear_outlines"), &NavigationPolygon::clear_outlines);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("make_polygons_from_outlines"), &NavigationPolygon::make_polygons_from_outlines); ClassDB::bind_method(D_METHOD("make_polygons_from_outlines"), &NavigationPolygon::make_polygons_from_outlines);
#endif // DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationPolygon::_set_polygons); ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationPolygon::_set_polygons);
ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationPolygon::_get_polygons); ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationPolygon::_get_polygons);
@ -369,10 +440,69 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationPolygon::set_cell_size); ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationPolygon::set_cell_size);
ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationPolygon::get_cell_size); ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationPolygon::get_cell_size);
ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationPolygon::set_parsed_geometry_type);
ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationPolygon::get_parsed_geometry_type);
ClassDB::bind_method(D_METHOD("set_parsed_collision_mask", "mask"), &NavigationPolygon::set_parsed_collision_mask);
ClassDB::bind_method(D_METHOD("get_parsed_collision_mask"), &NavigationPolygon::get_parsed_collision_mask);
ClassDB::bind_method(D_METHOD("set_parsed_collision_mask_value", "layer_number", "value"), &NavigationPolygon::set_parsed_collision_mask_value);
ClassDB::bind_method(D_METHOD("get_parsed_collision_mask_value", "layer_number"), &NavigationPolygon::get_parsed_collision_mask_value);
ClassDB::bind_method(D_METHOD("set_source_geometry_mode", "geometry_mode"), &NavigationPolygon::set_source_geometry_mode);
ClassDB::bind_method(D_METHOD("get_source_geometry_mode"), &NavigationPolygon::get_source_geometry_mode);
ClassDB::bind_method(D_METHOD("set_source_geometry_group_name", "group_name"), &NavigationPolygon::set_source_geometry_group_name);
ClassDB::bind_method(D_METHOD("get_source_geometry_group_name"), &NavigationPolygon::get_source_geometry_group_name);
ClassDB::bind_method(D_METHOD("set_agent_radius", "agent_radius"), &NavigationPolygon::set_agent_radius);
ClassDB::bind_method(D_METHOD("get_agent_radius"), &NavigationPolygon::get_agent_radius);
ClassDB::bind_method(D_METHOD("clear"), &NavigationPolygon::clear); ClassDB::bind_method(D_METHOD("clear"), &NavigationPolygon::clear);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:px"), "set_cell_size", "get_cell_size");
ADD_GROUP("Geometry", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Meshes and Static Colliders"), "set_parsed_geometry_type", "get_parsed_geometry_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_parsed_collision_mask", "get_parsed_collision_mask");
ADD_PROPERTY_DEFAULT("parsed_collision_mask", 0xFFFFFFFF);
ADD_PROPERTY(PropertyInfo(Variant::INT, "source_geometry_mode", PROPERTY_HINT_ENUM, "Root Node Children,Group With Children,Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_geometry_group_name"), "set_source_geometry_group_name", "get_source_geometry_group_name");
ADD_PROPERTY_DEFAULT("source_geometry_group_name", StringName("navigation_polygon_source_geometry_group"));
ADD_GROUP("Cells", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "1.0,50.0,1.0,or_greater,suffix:px"), "set_cell_size", "get_cell_size");
ADD_GROUP("Agents", "agent_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_radius", PROPERTY_HINT_RANGE, "0.0,500.0,0.01,or_greater,suffix:px"), "set_agent_radius", "get_agent_radius");
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MAX);
BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_ROOT_NODE_CHILDREN);
BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN);
BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_EXPLICIT);
BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_MAX);
}
void NavigationPolygon::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "parsed_collision_mask") {
if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) {
p_property.usage = PROPERTY_USAGE_NONE;
return;
}
}
if (p_property.name == "parsed_source_group_name") {
if (source_geometry_mode == SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) {
p_property.usage = PROPERTY_USAGE_NONE;
return;
}
}
}
NavigationPolygon::NavigationPolygon() {
navigation_mesh.instantiate();
} }

View File

@ -43,6 +43,7 @@ class NavigationPolygon : public Resource {
}; };
Vector<Polygon> polygons; Vector<Polygon> polygons;
Vector<Vector<Vector2>> outlines; Vector<Vector<Vector2>> outlines;
Vector<Vector<Vector2>> baked_outlines;
mutable Rect2 item_rect; mutable Rect2 item_rect;
mutable bool rect_cache_dirty = true; mutable bool rect_cache_dirty = true;
@ -55,6 +56,7 @@ class NavigationPolygon : public Resource {
protected: protected:
static void _bind_methods(); static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
void _set_polygons(const TypedArray<Vector<int32_t>> &p_array); void _set_polygons(const TypedArray<Vector<int32_t>> &p_array);
TypedArray<Vector<int32_t>> _get_polygons() const; TypedArray<Vector<int32_t>> _get_polygons() const;
@ -68,6 +70,28 @@ public:
bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
#endif #endif
enum ParsedGeometryType {
PARSED_GEOMETRY_MESH_INSTANCES = 0,
PARSED_GEOMETRY_STATIC_COLLIDERS,
PARSED_GEOMETRY_BOTH,
PARSED_GEOMETRY_MAX
};
enum SourceGeometryMode {
SOURCE_GEOMETRY_ROOT_NODE_CHILDREN = 0,
SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN,
SOURCE_GEOMETRY_GROUPS_EXPLICIT,
SOURCE_GEOMETRY_MAX
};
real_t agent_radius = 10.0f;
ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_BOTH;
uint32_t parsed_collision_mask = 0xFFFFFFFF;
SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_ROOT_NODE_CHILDREN;
StringName source_geometry_group_name = "navigation_polygon_source_group";
void set_vertices(const Vector<Vector2> &p_vertices); void set_vertices(const Vector<Vector2> &p_vertices);
Vector<Vector2> get_vertices() const; Vector<Vector2> get_vertices() const;
@ -82,11 +106,33 @@ public:
int get_outline_count() const; int get_outline_count() const;
void clear_outlines(); void clear_outlines();
#ifndef DISABLE_DEPRECATED
void make_polygons_from_outlines(); void make_polygons_from_outlines();
#endif // DISABLE_DEPRECATED
void set_polygons(const Vector<Vector<int>> &p_polygons);
const Vector<Vector<int>> &get_polygons() const;
Vector<int> get_polygon(int p_idx); Vector<int> get_polygon(int p_idx);
void clear_polygons(); void clear_polygons();
void set_parsed_geometry_type(ParsedGeometryType p_geometry_type);
ParsedGeometryType get_parsed_geometry_type() const;
void set_parsed_collision_mask(uint32_t p_mask);
uint32_t get_parsed_collision_mask() const;
void set_parsed_collision_mask_value(int p_layer_number, bool p_value);
bool get_parsed_collision_mask_value(int p_layer_number) const;
void set_source_geometry_mode(SourceGeometryMode p_geometry_mode);
SourceGeometryMode get_source_geometry_mode() const;
void set_source_geometry_group_name(StringName p_group_name);
StringName get_source_geometry_group_name() const;
void set_agent_radius(real_t p_value);
real_t get_agent_radius() const;
Ref<NavigationMesh> get_navigation_mesh(); Ref<NavigationMesh> get_navigation_mesh();
void set_cell_size(real_t p_cell_size); void set_cell_size(real_t p_cell_size);
@ -94,8 +140,11 @@ public:
void clear(); void clear();
NavigationPolygon() {} NavigationPolygon();
~NavigationPolygon() {} ~NavigationPolygon() {}
}; };
VARIANT_ENUM_CAST(NavigationPolygon::ParsedGeometryType);
VARIANT_ENUM_CAST(NavigationPolygon::SourceGeometryMode);
#endif // NAVIGATION_POLYGON_H #endif // NAVIGATION_POLYGON_H

View File

@ -28,133 +28,153 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/ /**************************************************************************/
#include "servers/navigation_server_2d.h" #include "navigation_server_2d.h"
#include "core/math/transform_2d.h"
#include "core/math/transform_3d.h"
#include "servers/navigation_server_3d.h" #include "servers/navigation_server_3d.h"
NavigationServer2D *NavigationServer2D::singleton = nullptr; NavigationServer2D *NavigationServer2D::singleton = nullptr;
#define FORWARD_0(FUNC_NAME) \ void NavigationServer2D::_bind_methods() {
NavigationServer2D::FUNC_NAME() { \ ClassDB::bind_method(D_METHOD("get_maps"), &NavigationServer2D::get_maps);
return NavigationServer3D::get_singleton()->FUNC_NAME(); \
ClassDB::bind_method(D_METHOD("map_create"), &NavigationServer2D::map_create);
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &NavigationServer2D::map_set_active);
ClassDB::bind_method(D_METHOD("map_is_active", "map"), &NavigationServer2D::map_is_active);
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer2D::map_set_cell_size);
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer2D::map_get_cell_size);
ClassDB::bind_method(D_METHOD("map_set_use_edge_connections", "map", "enabled"), &NavigationServer2D::map_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_get_use_edge_connections", "map"), &NavigationServer2D::map_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer2D::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer2D::map_get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_set_link_connection_radius", "map", "radius"), &NavigationServer2D::map_set_link_connection_radius);
ClassDB::bind_method(D_METHOD("map_get_link_connection_radius", "map"), &NavigationServer2D::map_get_link_connection_radius);
ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer2D::map_get_path, DEFVAL(1));
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer2D::map_get_closest_point);
ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &NavigationServer2D::map_get_closest_point_owner);
ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer2D::map_get_links);
ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer2D::map_get_regions);
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer2D::map_get_agents);
ClassDB::bind_method(D_METHOD("map_get_obstacles", "map"), &NavigationServer2D::map_get_obstacles);
ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer2D::map_force_update);
ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::query_path);
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create);
ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer2D::region_set_enabled);
ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer2D::region_get_enabled);
ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer2D::region_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_get_use_edge_connections", "region"), &NavigationServer2D::region_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_set_enter_cost", "region", "enter_cost"), &NavigationServer2D::region_set_enter_cost);
ClassDB::bind_method(D_METHOD("region_get_enter_cost", "region"), &NavigationServer2D::region_get_enter_cost);
ClassDB::bind_method(D_METHOD("region_set_travel_cost", "region", "travel_cost"), &NavigationServer2D::region_set_travel_cost);
ClassDB::bind_method(D_METHOD("region_get_travel_cost", "region"), &NavigationServer2D::region_get_travel_cost);
ClassDB::bind_method(D_METHOD("region_set_owner_id", "region", "owner_id"), &NavigationServer2D::region_set_owner_id);
ClassDB::bind_method(D_METHOD("region_get_owner_id", "region"), &NavigationServer2D::region_get_owner_id);
ClassDB::bind_method(D_METHOD("region_owns_point", "region", "point"), &NavigationServer2D::region_owns_point);
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer2D::region_set_map);
ClassDB::bind_method(D_METHOD("region_get_map", "region"), &NavigationServer2D::region_get_map);
ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer2D::region_set_navigation_layers);
ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer2D::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer2D::region_set_transform);
ClassDB::bind_method(D_METHOD("region_set_navigation_polygon", "region", "navigation_polygon"), &NavigationServer2D::region_set_navigation_polygon);
ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end);
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create);
ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map);
ClassDB::bind_method(D_METHOD("link_get_map", "link"), &NavigationServer2D::link_get_map);
ClassDB::bind_method(D_METHOD("link_set_enabled", "link", "enabled"), &NavigationServer2D::link_set_enabled);
ClassDB::bind_method(D_METHOD("link_get_enabled", "link"), &NavigationServer2D::link_get_enabled);
ClassDB::bind_method(D_METHOD("link_set_bidirectional", "link", "bidirectional"), &NavigationServer2D::link_set_bidirectional);
ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer2D::link_is_bidirectional);
ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer2D::link_set_navigation_layers);
ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer2D::link_get_navigation_layers);
ClassDB::bind_method(D_METHOD("link_set_start_position", "link", "position"), &NavigationServer2D::link_set_start_position);
ClassDB::bind_method(D_METHOD("link_get_start_position", "link"), &NavigationServer2D::link_get_start_position);
ClassDB::bind_method(D_METHOD("link_set_end_position", "link", "position"), &NavigationServer2D::link_set_end_position);
ClassDB::bind_method(D_METHOD("link_get_end_position", "link"), &NavigationServer2D::link_get_end_position);
ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer2D::link_set_enter_cost);
ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer2D::link_get_enter_cost);
ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer2D::link_set_travel_cost);
ClassDB::bind_method(D_METHOD("link_get_travel_cost", "link"), &NavigationServer2D::link_get_travel_cost);
ClassDB::bind_method(D_METHOD("link_set_owner_id", "link", "owner_id"), &NavigationServer2D::link_set_owner_id);
ClassDB::bind_method(D_METHOD("link_get_owner_id", "link"), &NavigationServer2D::link_get_owner_id);
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer2D::agent_create);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_enabled", "agent", "enabled"), &NavigationServer2D::agent_set_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_enabled", "agent"), &NavigationServer2D::agent_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer2D::agent_set_map);
ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer2D::agent_get_map);
ClassDB::bind_method(D_METHOD("agent_set_paused", "agent", "paused"), &NavigationServer2D::agent_set_paused);
ClassDB::bind_method(D_METHOD("agent_get_paused", "agent"), &NavigationServer2D::agent_get_paused);
ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer2D::agent_set_neighbor_distance);
ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer2D::agent_set_max_neighbors);
ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_agents);
ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer2D::agent_set_radius);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer2D::agent_set_max_speed);
ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer2D::agent_set_velocity_forced);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer2D::agent_set_velocity);
ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer2D::agent_set_position);
ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer2D::agent_is_map_changed);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer2D::agent_set_avoidance_callback);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer2D::agent_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer2D::agent_set_avoidance_mask);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer2D::agent_set_avoidance_priority);
ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer2D::obstacle_create);
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_enabled", "obstacle", "enabled"), &NavigationServer2D::obstacle_set_avoidance_enabled);
ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_enabled", "obstacle"), &NavigationServer2D::obstacle_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("obstacle_set_map", "obstacle", "map"), &NavigationServer2D::obstacle_set_map);
ClassDB::bind_method(D_METHOD("obstacle_get_map", "obstacle"), &NavigationServer2D::obstacle_get_map);
ClassDB::bind_method(D_METHOD("obstacle_set_paused", "obstacle", "paused"), &NavigationServer2D::obstacle_set_paused);
ClassDB::bind_method(D_METHOD("obstacle_get_paused", "obstacle"), &NavigationServer2D::obstacle_get_paused);
ClassDB::bind_method(D_METHOD("obstacle_set_radius", "obstacle", "radius"), &NavigationServer2D::obstacle_set_radius);
ClassDB::bind_method(D_METHOD("obstacle_set_velocity", "obstacle", "velocity"), &NavigationServer2D::obstacle_set_velocity);
ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer2D::obstacle_set_position);
ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer2D::obstacle_set_vertices);
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer2D::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_polygon", "source_geometry_data", "root_node", "callback"), &NavigationServer2D::parse_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_polygon", "source_geometry_data", "callback"), &NavigationServer2D::bake_from_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_polygon", "source_geometry_data", "callback"), &NavigationServer2D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer2D::free);
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationServer2D::set_debug_enabled);
ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationServer2D::get_debug_enabled);
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
ADD_SIGNAL(MethodInfo("navigation_debug_changed"));
} }
#define FORWARD_0_C(FUNC_NAME) \ NavigationServer2D *NavigationServer2D::get_singleton() {
NavigationServer2D::FUNC_NAME() \ return singleton;
const { \
return NavigationServer3D::get_singleton()->FUNC_NAME(); \
} }
#define FORWARD_1(FUNC_NAME, T_0, D_0, CONV_0) \ NavigationServer2D::NavigationServer2D() {
NavigationServer2D::FUNC_NAME(T_0 D_0) { \ ERR_FAIL_COND(singleton != nullptr);
return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0)); \ singleton = this;
ERR_FAIL_NULL_MSG(NavigationServer3D::get_singleton(), "The Navigation3D singleton should be initialized before the 2D one.");
NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationServer2D::_emit_map_changed));
#ifdef DEBUG_ENABLED
NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationServer2D::_emit_navigation_debug_changed_signal));
#endif // DEBUG_ENABLED
} }
#define FORWARD_1_C(FUNC_NAME, T_0, D_0, CONV_0) \ #ifdef DEBUG_ENABLED
NavigationServer2D::FUNC_NAME(T_0 D_0) \ void NavigationServer2D::_emit_navigation_debug_changed_signal() {
const { \ emit_signal(SNAME("navigation_debug_changed"));
return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0)); \
} }
#endif // DEBUG_ENABLED
#define FORWARD_1_R_C(CONV_R, FUNC_NAME, T_0, D_0, CONV_0) \ NavigationServer2D::~NavigationServer2D() {
NavigationServer2D::FUNC_NAME(T_0 D_0) \ singleton = nullptr;
const { \
return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0))); \
}
#define FORWARD_2(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) { \
return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1)); \
}
#define FORWARD_2_C(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) \
const { \
return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1)); \
}
#define FORWARD_2_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) \
const { \
return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1))); \
}
#define FORWARD_5_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, T_4, D_4, CONV_0, CONV_1, CONV_2, CONV_3, CONV_4) \
NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3, T_4 D_4) \
const { \
return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3), CONV_4(D_4))); \
}
static RID rid_to_rid(const RID d) {
return d;
}
static bool bool_to_bool(const bool d) {
return d;
}
static int int_to_int(const int d) {
return d;
}
static uint32_t uint32_to_uint32(const uint32_t d) {
return d;
}
static real_t real_to_real(const real_t d) {
return d;
}
static Vector3 v2_to_v3(const Vector2 d) {
return Vector3(d.x, 0.0, d.y);
}
static Vector2 v3_to_v2(const Vector3 &d) {
return Vector2(d.x, d.z);
}
static Vector<Vector3> vector_v2_to_v3(const Vector<Vector2> &d) {
Vector<Vector3> nd;
nd.resize(d.size());
for (int i(0); i < nd.size(); i++) {
nd.write[i] = v2_to_v3(d[i]);
}
return nd;
}
static Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) {
Vector<Vector2> nd;
nd.resize(d.size());
for (int i(0); i < nd.size(); i++) {
nd.write[i] = v3_to_v2(d[i]);
}
return nd;
}
static Transform3D trf2_to_trf3(const Transform2D &d) {
Vector3 o(v2_to_v3(d.get_origin()));
Basis b;
b.rotate(Vector3(0, -1, 0), d.get_rotation());
b.scale(v2_to_v3(d.get_scale()));
return Transform3D(b, o);
}
static ObjectID id_to_id(const ObjectID &id) {
return id;
}
static Callable callable_to_callable(const Callable &c) {
return c;
}
static Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) {
if (d.is_valid()) {
return d->get_navigation_mesh();
} else {
return Ref<NavigationMesh>();
}
} }
void NavigationServer2D::_emit_map_changed(RID p_map) { void NavigationServer2D::_emit_map_changed(RID p_map) {
@ -363,286 +383,18 @@ bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_static(
} }
#endif // DEBUG_ENABLED #endif // DEBUG_ENABLED
void NavigationServer2D::_bind_methods() { ///////////////////////////////////////////////////////
ClassDB::bind_method(D_METHOD("get_maps"), &NavigationServer2D::get_maps);
ClassDB::bind_method(D_METHOD("map_create"), &NavigationServer2D::map_create); NavigationServer2DCallback NavigationServer2DManager::create_callback = nullptr;
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &NavigationServer2D::map_set_active);
ClassDB::bind_method(D_METHOD("map_is_active", "map"), &NavigationServer2D::map_is_active);
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer2D::map_set_cell_size);
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer2D::map_get_cell_size);
ClassDB::bind_method(D_METHOD("map_set_use_edge_connections", "map", "enabled"), &NavigationServer2D::map_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_get_use_edge_connections", "map"), &NavigationServer2D::map_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer2D::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer2D::map_get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_set_link_connection_radius", "map", "radius"), &NavigationServer2D::map_set_link_connection_radius);
ClassDB::bind_method(D_METHOD("map_get_link_connection_radius", "map"), &NavigationServer2D::map_get_link_connection_radius);
ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer2D::map_get_path, DEFVAL(1));
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer2D::map_get_closest_point);
ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &NavigationServer2D::map_get_closest_point_owner);
ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer2D::map_get_links); void NavigationServer2DManager::set_default_server(NavigationServer2DCallback p_callback) {
ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer2D::map_get_regions); create_callback = p_callback;
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer2D::map_get_agents);
ClassDB::bind_method(D_METHOD("map_get_obstacles", "map"), &NavigationServer2D::map_get_obstacles);
ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer2D::map_force_update);
ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::query_path);
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create);
ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer2D::region_set_enabled);
ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer2D::region_get_enabled);
ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer2D::region_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_get_use_edge_connections", "region"), &NavigationServer2D::region_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_set_enter_cost", "region", "enter_cost"), &NavigationServer2D::region_set_enter_cost);
ClassDB::bind_method(D_METHOD("region_get_enter_cost", "region"), &NavigationServer2D::region_get_enter_cost);
ClassDB::bind_method(D_METHOD("region_set_travel_cost", "region", "travel_cost"), &NavigationServer2D::region_set_travel_cost);
ClassDB::bind_method(D_METHOD("region_get_travel_cost", "region"), &NavigationServer2D::region_get_travel_cost);
ClassDB::bind_method(D_METHOD("region_set_owner_id", "region", "owner_id"), &NavigationServer2D::region_set_owner_id);
ClassDB::bind_method(D_METHOD("region_get_owner_id", "region"), &NavigationServer2D::region_get_owner_id);
ClassDB::bind_method(D_METHOD("region_owns_point", "region", "point"), &NavigationServer2D::region_owns_point);
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer2D::region_set_map);
ClassDB::bind_method(D_METHOD("region_get_map", "region"), &NavigationServer2D::region_get_map);
ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer2D::region_set_navigation_layers);
ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer2D::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer2D::region_set_transform);
ClassDB::bind_method(D_METHOD("region_set_navigation_polygon", "region", "navigation_polygon"), &NavigationServer2D::region_set_navigation_polygon);
ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end);
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create);
ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map);
ClassDB::bind_method(D_METHOD("link_get_map", "link"), &NavigationServer2D::link_get_map);
ClassDB::bind_method(D_METHOD("link_set_enabled", "link", "enabled"), &NavigationServer2D::link_set_enabled);
ClassDB::bind_method(D_METHOD("link_get_enabled", "link"), &NavigationServer2D::link_get_enabled);
ClassDB::bind_method(D_METHOD("link_set_bidirectional", "link", "bidirectional"), &NavigationServer2D::link_set_bidirectional);
ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer2D::link_is_bidirectional);
ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer2D::link_set_navigation_layers);
ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer2D::link_get_navigation_layers);
ClassDB::bind_method(D_METHOD("link_set_start_position", "link", "position"), &NavigationServer2D::link_set_start_position);
ClassDB::bind_method(D_METHOD("link_get_start_position", "link"), &NavigationServer2D::link_get_start_position);
ClassDB::bind_method(D_METHOD("link_set_end_position", "link", "position"), &NavigationServer2D::link_set_end_position);
ClassDB::bind_method(D_METHOD("link_get_end_position", "link"), &NavigationServer2D::link_get_end_position);
ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer2D::link_set_enter_cost);
ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer2D::link_get_enter_cost);
ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer2D::link_set_travel_cost);
ClassDB::bind_method(D_METHOD("link_get_travel_cost", "link"), &NavigationServer2D::link_get_travel_cost);
ClassDB::bind_method(D_METHOD("link_set_owner_id", "link", "owner_id"), &NavigationServer2D::link_set_owner_id);
ClassDB::bind_method(D_METHOD("link_get_owner_id", "link"), &NavigationServer2D::link_get_owner_id);
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer2D::agent_create);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_enabled", "agent", "enabled"), &NavigationServer2D::agent_set_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_get_avoidance_enabled", "agent"), &NavigationServer2D::agent_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer2D::agent_set_map);
ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer2D::agent_get_map);
ClassDB::bind_method(D_METHOD("agent_set_paused", "agent", "paused"), &NavigationServer2D::agent_set_paused);
ClassDB::bind_method(D_METHOD("agent_get_paused", "agent"), &NavigationServer2D::agent_get_paused);
ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer2D::agent_set_neighbor_distance);
ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer2D::agent_set_max_neighbors);
ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_agents);
ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer2D::agent_set_radius);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer2D::agent_set_max_speed);
ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer2D::agent_set_velocity_forced);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer2D::agent_set_velocity);
ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer2D::agent_set_position);
ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer2D::agent_is_map_changed);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer2D::agent_set_avoidance_callback);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer2D::agent_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer2D::agent_set_avoidance_mask);
ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer2D::agent_set_avoidance_priority);
ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer2D::obstacle_create);
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_enabled", "obstacle", "enabled"), &NavigationServer2D::obstacle_set_avoidance_enabled);
ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_enabled", "obstacle"), &NavigationServer2D::obstacle_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("obstacle_set_map", "obstacle", "map"), &NavigationServer2D::obstacle_set_map);
ClassDB::bind_method(D_METHOD("obstacle_get_map", "obstacle"), &NavigationServer2D::obstacle_get_map);
ClassDB::bind_method(D_METHOD("obstacle_set_paused", "obstacle", "paused"), &NavigationServer2D::obstacle_set_paused);
ClassDB::bind_method(D_METHOD("obstacle_get_paused", "obstacle"), &NavigationServer2D::obstacle_get_paused);
ClassDB::bind_method(D_METHOD("obstacle_set_radius", "obstacle", "radius"), &NavigationServer2D::obstacle_set_radius);
ClassDB::bind_method(D_METHOD("obstacle_set_velocity", "obstacle", "velocity"), &NavigationServer2D::obstacle_set_velocity);
ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer2D::obstacle_set_position);
ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer2D::obstacle_set_vertices);
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer2D::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer2D::free);
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationServer2D::set_debug_enabled);
ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationServer2D::get_debug_enabled);
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
ADD_SIGNAL(MethodInfo("navigation_debug_changed"));
} }
NavigationServer2D::NavigationServer2D() { NavigationServer2D *NavigationServer2DManager::new_default_server() {
singleton = this; if (create_callback == nullptr) {
ERR_FAIL_COND_MSG(!NavigationServer3D::get_singleton(), "The Navigation3D singleton should be initialized before the 2D one."); return nullptr;
NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationServer2D::_emit_map_changed));
#ifdef DEBUG_ENABLED
NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationServer2D::_emit_navigation_debug_changed_signal));
#endif // DEBUG_ENABLED
} }
#ifdef DEBUG_ENABLED return create_callback();
void NavigationServer2D::_emit_navigation_debug_changed_signal() {
emit_signal(SNAME("navigation_debug_changed"));
}
#endif // DEBUG_ENABLED
NavigationServer2D::~NavigationServer2D() {
singleton = nullptr;
}
TypedArray<RID> FORWARD_0_C(get_maps);
TypedArray<RID> FORWARD_1_C(map_get_links, RID, p_map, rid_to_rid);
TypedArray<RID> FORWARD_1_C(map_get_regions, RID, p_map, rid_to_rid);
TypedArray<RID> FORWARD_1_C(map_get_agents, RID, p_map, rid_to_rid);
TypedArray<RID> FORWARD_1_C(map_get_obstacles, RID, p_map, rid_to_rid);
RID FORWARD_1_C(region_get_map, RID, p_region, rid_to_rid);
RID FORWARD_1_C(agent_get_map, RID, p_agent, rid_to_rid);
RID FORWARD_0(map_create);
void FORWARD_2(map_set_active, RID, p_map, bool, p_active, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(map_is_active, RID, p_map, rid_to_rid);
void NavigationServer2D::map_force_update(RID p_map) {
NavigationServer3D::get_singleton()->map_force_update(p_map);
}
void FORWARD_2(map_set_cell_size, RID, p_map, real_t, p_cell_size, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_cell_size, RID, p_map, rid_to_rid);
void FORWARD_2(map_set_use_edge_connections, RID, p_map, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(map_get_use_edge_connections, RID, p_map, rid_to_rid);
void FORWARD_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid);
void FORWARD_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_link_connection_radius, RID, p_map, rid_to_rid);
Vector<Vector2> FORWARD_5_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, uint32_t, p_layers, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool, uint32_to_uint32);
Vector2 FORWARD_2_R_C(v3_to_v2, map_get_closest_point, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
RID FORWARD_0(region_create);
void FORWARD_2(region_set_enabled, RID, p_region, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(region_get_enabled, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_use_edge_connections, RID, p_region, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(region_get_use_edge_connections, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(region_get_enter_cost, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(region_get_travel_cost, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_owner_id, RID, p_region, ObjectID, p_owner_id, rid_to_rid, id_to_id);
ObjectID FORWARD_1_C(region_get_owner_id, RID, p_region, rid_to_rid);
bool FORWARD_2_C(region_owns_point, RID, p_region, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
void FORWARD_2(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
uint32_t FORWARD_1_C(region_get_navigation_layers, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3);
void NavigationServer2D::region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) {
NavigationServer3D::get_singleton()->region_set_navigation_mesh(p_region, poly_to_mesh(p_navigation_polygon));
}
int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid);
Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_start, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int);
Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_end, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int);
RID FORWARD_0(link_create);
void FORWARD_2(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid);
RID FORWARD_1_C(link_get_map, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_enabled, RID, p_link, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(link_get_enabled, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(link_is_bidirectional, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
uint32_t FORWARD_1_C(link_get_navigation_layers, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_start_position, RID, p_link, Vector2, p_position, rid_to_rid, v2_to_v3);
Vector2 FORWARD_1_R_C(v3_to_v2, link_get_start_position, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_end_position, RID, p_link, Vector2, p_position, rid_to_rid, v2_to_v3);
Vector2 FORWARD_1_R_C(v3_to_v2, link_get_end_position, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(link_get_enter_cost, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(link_get_travel_cost, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_owner_id, RID, p_link, ObjectID, p_owner_id, rid_to_rid, id_to_id);
ObjectID FORWARD_1_C(link_get_owner_id, RID, p_link, rid_to_rid);
RID NavigationServer2D::agent_create() {
RID agent = NavigationServer3D::get_singleton()->agent_create();
return agent;
}
void FORWARD_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(agent_get_avoidance_enabled, RID, p_agent, rid_to_rid);
void FORWARD_2(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int);
void FORWARD_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_velocity_forced, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3);
bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid);
void FORWARD_2(agent_set_paused, RID, p_agent, bool, p_paused, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(agent_get_paused, RID, p_agent, rid_to_rid);
void FORWARD_1(free, RID, p_object, rid_to_rid);
void FORWARD_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback, rid_to_rid, callable_to_callable);
void FORWARD_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
void FORWARD_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask, rid_to_rid, uint32_to_uint32);
void FORWARD_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority, rid_to_rid, real_to_real);
RID NavigationServer2D::obstacle_create() {
RID obstacle = NavigationServer3D::get_singleton()->obstacle_create();
return obstacle;
}
void FORWARD_2(obstacle_set_avoidance_enabled, RID, p_obstacle, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(obstacle_get_avoidance_enabled, RID, p_obstacle, rid_to_rid);
void FORWARD_2(obstacle_set_map, RID, p_obstacle, RID, p_map, rid_to_rid, rid_to_rid);
RID FORWARD_1_C(obstacle_get_map, RID, p_obstacle, rid_to_rid);
void FORWARD_2(obstacle_set_paused, RID, p_obstacle, bool, p_paused, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(obstacle_get_paused, RID, p_obstacle, rid_to_rid);
void FORWARD_2(obstacle_set_radius, RID, p_obstacle, real_t, p_radius, rid_to_rid, real_to_real);
void FORWARD_2(obstacle_set_velocity, RID, p_obstacle, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(obstacle_set_position, RID, p_obstacle, Vector2, p_position, rid_to_rid, v2_to_v3);
void FORWARD_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
void NavigationServer2D::obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) {
NavigationServer3D::get_singleton()->obstacle_set_vertices(p_obstacle, vector_v2_to_v3(p_vertices));
}
void NavigationServer2D::query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const {
ERR_FAIL_COND(!p_query_parameters.is_valid());
ERR_FAIL_COND(!p_query_result.is_valid());
const NavigationUtilities::PathQueryResult _query_result = NavigationServer3D::get_singleton()->_query_path(p_query_parameters->get_parameters());
p_query_result->set_path(vector_v3_to_v2(_query_result.path));
p_query_result->set_path_types(_query_result.path_types);
p_query_result->set_path_rids(_query_result.path_rids);
p_query_result->set_path_owner_ids(_query_result.path_owner_ids);
} }

View File

@ -34,10 +34,15 @@
#include "core/object/class_db.h" #include "core/object/class_db.h"
#include "core/templates/rid.h" #include "core/templates/rid.h"
#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/navigation_polygon.h" #include "scene/resources/navigation_polygon.h"
#include "servers/navigation/navigation_path_query_parameters_2d.h" #include "servers/navigation/navigation_path_query_parameters_2d.h"
#include "servers/navigation/navigation_path_query_result_2d.h" #include "servers/navigation/navigation_path_query_result_2d.h"
#ifdef CLIPPER2_ENABLED
class NavMeshGenerator2D;
#endif // CLIPPER2_ENABLED
// This server exposes the `NavigationServer3D` features in the 2D world. // This server exposes the `NavigationServer3D` features in the 2D world.
class NavigationServer2D : public Object { class NavigationServer2D : public Object {
GDCLASS(NavigationServer2D, Object); GDCLASS(NavigationServer2D, Object);
@ -51,145 +56,145 @@ protected:
public: public:
/// Thread safe, can be used across many threads. /// Thread safe, can be used across many threads.
static NavigationServer2D *get_singleton() { return singleton; } static NavigationServer2D *get_singleton();
virtual TypedArray<RID> get_maps() const; virtual TypedArray<RID> get_maps() const = 0;
/// Create a new map. /// Create a new map.
virtual RID map_create(); virtual RID map_create() = 0;
/// Set map active. /// Set map active.
virtual void map_set_active(RID p_map, bool p_active); virtual void map_set_active(RID p_map, bool p_active) = 0;
/// Returns true if the map is active. /// Returns true if the map is active.
virtual bool map_is_active(RID p_map) const; virtual bool map_is_active(RID p_map) const = 0;
/// Set the map cell size used to weld the navigation mesh polygons. /// Set the map cell size used to weld the navigation mesh polygons.
virtual void map_set_cell_size(RID p_map, real_t p_cell_size); virtual void map_set_cell_size(RID p_map, real_t p_cell_size) = 0;
/// Returns the map cell size. /// Returns the map cell size.
virtual real_t map_get_cell_size(RID p_map) const; virtual real_t map_get_cell_size(RID p_map) const = 0;
virtual void map_set_use_edge_connections(RID p_map, bool p_enabled); virtual void map_set_use_edge_connections(RID p_map, bool p_enabled) = 0;
virtual bool map_get_use_edge_connections(RID p_map) const; virtual bool map_get_use_edge_connections(RID p_map) const = 0;
/// Set the map edge connection margin used to weld the compatible region edges. /// Set the map edge connection margin used to weld the compatible region edges.
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin); virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) = 0;
/// Returns the edge connection margin of this map. /// Returns the edge connection margin of this map.
virtual real_t map_get_edge_connection_margin(RID p_map) const; virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
/// Set the map link connection radius used to attach links to the nav mesh. /// Set the map link connection radius used to attach links to the nav mesh.
virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius); virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) = 0;
/// Returns the link connection radius of this map. /// Returns the link connection radius of this map.
virtual real_t map_get_link_connection_radius(RID p_map) const; virtual real_t map_get_link_connection_radius(RID p_map) const = 0;
/// Returns the navigation path to reach the destination from the origin. /// Returns the navigation path to reach the destination from the origin.
virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const; virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const = 0;
virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const; virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const = 0;
virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const; virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const = 0;
virtual TypedArray<RID> map_get_links(RID p_map) const; virtual TypedArray<RID> map_get_links(RID p_map) const = 0;
virtual TypedArray<RID> map_get_regions(RID p_map) const; virtual TypedArray<RID> map_get_regions(RID p_map) const = 0;
virtual TypedArray<RID> map_get_agents(RID p_map) const; virtual TypedArray<RID> map_get_agents(RID p_map) const = 0;
virtual TypedArray<RID> map_get_obstacles(RID p_map) const; virtual TypedArray<RID> map_get_obstacles(RID p_map) const = 0;
virtual void map_force_update(RID p_map); virtual void map_force_update(RID p_map) = 0;
/// Creates a new region. /// Creates a new region.
virtual RID region_create(); virtual RID region_create() = 0;
virtual void region_set_enabled(RID p_region, bool p_enabled); virtual void region_set_enabled(RID p_region, bool p_enabled) = 0;
virtual bool region_get_enabled(RID p_region) const; virtual bool region_get_enabled(RID p_region) const = 0;
virtual void region_set_use_edge_connections(RID p_region, bool p_enabled); virtual void region_set_use_edge_connections(RID p_region, bool p_enabled) = 0;
virtual bool region_get_use_edge_connections(RID p_region) const; virtual bool region_get_use_edge_connections(RID p_region) const = 0;
/// Set the enter_cost of a region /// Set the enter_cost of a region
virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost); virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) = 0;
virtual real_t region_get_enter_cost(RID p_region) const; virtual real_t region_get_enter_cost(RID p_region) const = 0;
/// Set the travel_cost of a region /// Set the travel_cost of a region
virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost); virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) = 0;
virtual real_t region_get_travel_cost(RID p_region) const; virtual real_t region_get_travel_cost(RID p_region) const = 0;
/// Set the node which manages this region. /// Set the node which manages this region.
virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id); virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id) = 0;
virtual ObjectID region_get_owner_id(RID p_region) const; virtual ObjectID region_get_owner_id(RID p_region) const = 0;
virtual bool region_owns_point(RID p_region, const Vector2 &p_point) const; virtual bool region_owns_point(RID p_region, const Vector2 &p_point) const = 0;
/// Set the map of this region. /// Set the map of this region.
virtual void region_set_map(RID p_region, RID p_map); virtual void region_set_map(RID p_region, RID p_map) = 0;
virtual RID region_get_map(RID p_region) const; virtual RID region_get_map(RID p_region) const = 0;
/// Set the region's layers /// Set the region's layers
virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers); virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) = 0;
virtual uint32_t region_get_navigation_layers(RID p_region) const; virtual uint32_t region_get_navigation_layers(RID p_region) const = 0;
/// Set the global transformation of this region. /// Set the global transformation of this region.
virtual void region_set_transform(RID p_region, Transform2D p_transform); virtual void region_set_transform(RID p_region, Transform2D p_transform) = 0;
/// Set the navigation poly of this region. /// Set the navigation poly of this region.
virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon); virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) = 0;
/// Get a list of a region's connection to other regions. /// Get a list of a region's connection to other regions.
virtual int region_get_connections_count(RID p_region) const; virtual int region_get_connections_count(RID p_region) const = 0;
virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const; virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0;
virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0;
/// Creates a new link between positions in the nav map. /// Creates a new link between positions in the nav map.
virtual RID link_create(); virtual RID link_create() = 0;
/// Set the map of this link. /// Set the map of this link.
virtual void link_set_map(RID p_link, RID p_map); virtual void link_set_map(RID p_link, RID p_map) = 0;
virtual RID link_get_map(RID p_link) const; virtual RID link_get_map(RID p_link) const = 0;
virtual void link_set_enabled(RID p_link, bool p_enabled); virtual void link_set_enabled(RID p_link, bool p_enabled) = 0;
virtual bool link_get_enabled(RID p_link) const; virtual bool link_get_enabled(RID p_link) const = 0;
/// Set whether this link travels in both directions. /// Set whether this link travels in both directions.
virtual void link_set_bidirectional(RID p_link, bool p_bidirectional); virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) = 0;
virtual bool link_is_bidirectional(RID p_link) const; virtual bool link_is_bidirectional(RID p_link) const = 0;
/// Set the link's layers. /// Set the link's layers.
virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers); virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) = 0;
virtual uint32_t link_get_navigation_layers(RID p_link) const; virtual uint32_t link_get_navigation_layers(RID p_link) const = 0;
/// Set the start position of the link. /// Set the start position of the link.
virtual void link_set_start_position(RID p_link, Vector2 p_position); virtual void link_set_start_position(RID p_link, Vector2 p_position) = 0;
virtual Vector2 link_get_start_position(RID p_link) const; virtual Vector2 link_get_start_position(RID p_link) const = 0;
/// Set the end position of the link. /// Set the end position of the link.
virtual void link_set_end_position(RID p_link, Vector2 p_position); virtual void link_set_end_position(RID p_link, Vector2 p_position) = 0;
virtual Vector2 link_get_end_position(RID p_link) const; virtual Vector2 link_get_end_position(RID p_link) const = 0;
/// Set the enter cost of the link. /// Set the enter cost of the link.
virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost); virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) = 0;
virtual real_t link_get_enter_cost(RID p_link) const; virtual real_t link_get_enter_cost(RID p_link) const = 0;
/// Set the travel cost of the link. /// Set the travel cost of the link.
virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost); virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) = 0;
virtual real_t link_get_travel_cost(RID p_link) const; virtual real_t link_get_travel_cost(RID p_link) const = 0;
/// Set the node which manages this link. /// Set the node which manages this link.
virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id); virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id) = 0;
virtual ObjectID link_get_owner_id(RID p_link) const; virtual ObjectID link_get_owner_id(RID p_link) const = 0;
/// Creates the agent. /// Creates the agent.
virtual RID agent_create(); virtual RID agent_create() = 0;
/// Put the agent in the map. /// Put the agent in the map.
virtual void agent_set_map(RID p_agent, RID p_map); virtual void agent_set_map(RID p_agent, RID p_map) = 0;
virtual RID agent_get_map(RID p_agent) const; virtual RID agent_get_map(RID p_agent) const = 0;
virtual void agent_set_paused(RID p_agent, bool p_paused); virtual void agent_set_paused(RID p_agent, bool p_paused) = 0;
virtual bool agent_get_paused(RID p_agent) const; virtual bool agent_get_paused(RID p_agent) const = 0;
virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled); virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) = 0;
virtual bool agent_get_avoidance_enabled(RID p_agent) const; virtual bool agent_get_avoidance_enabled(RID p_agent) const = 0;
/// The maximum distance (center point to /// The maximum distance (center point to
/// center point) to other agents this agent /// center point) to other agents this agent
@ -198,7 +203,7 @@ public:
/// time of the simulation. If the number is too /// time of the simulation. If the number is too
/// low, the simulation will not be safe. /// low, the simulation will not be safe.
/// Must be non-negative. /// Must be non-negative.
virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance); virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) = 0;
/// The maximum number of other agents this /// The maximum number of other agents this
/// agent takes into account in the navigation. /// agent takes into account in the navigation.
@ -206,7 +211,7 @@ public:
/// running time of the simulation. If the /// running time of the simulation. If the
/// number is too low, the simulation will not /// number is too low, the simulation will not
/// be safe. /// be safe.
virtual void agent_set_max_neighbors(RID p_agent, int p_count); virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0;
/// The minimal amount of time for which this /// The minimal amount of time for which this
/// agent's velocities that are computed by the /// agent's velocities that are computed by the
@ -217,59 +222,67 @@ public:
/// agent has in choosing its velocities. /// agent has in choosing its velocities.
/// Must be positive. /// Must be positive.
virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon); virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0;
virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon); virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0;
/// The radius of this agent. /// The radius of this agent.
/// Must be non-negative. /// Must be non-negative.
virtual void agent_set_radius(RID p_agent, real_t p_radius); virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0;
/// The maximum speed of this agent. /// The maximum speed of this agent.
/// Must be non-negative. /// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed); virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0;
/// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity); virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) = 0;
/// The wanted velocity for the agent as a "suggestion" to the avoidance simulation. /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
/// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'. /// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity); virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) = 0;
/// Position of the agent in world space. /// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector2 p_position); virtual void agent_set_position(RID p_agent, Vector2 p_position) = 0;
/// Returns true if the map got changed the previous frame. /// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const; virtual bool agent_is_map_changed(RID p_agent) const = 0;
/// Callback called at the end of the RVO process /// Callback called at the end of the RVO process
virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback); virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) = 0;
virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers); virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0;
virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask); virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0;
virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority); virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0;
/// Creates the obstacle. /// Creates the obstacle.
virtual RID obstacle_create(); virtual RID obstacle_create() = 0;
virtual void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled); virtual void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) = 0;
virtual bool obstacle_get_avoidance_enabled(RID p_obstacle) const; virtual bool obstacle_get_avoidance_enabled(RID p_obstacle) const = 0;
virtual void obstacle_set_map(RID p_obstacle, RID p_map); virtual void obstacle_set_map(RID p_obstacle, RID p_map) = 0;
virtual RID obstacle_get_map(RID p_obstacle) const; virtual RID obstacle_get_map(RID p_obstacle) const = 0;
virtual void obstacle_set_paused(RID p_obstacle, bool p_paused); virtual void obstacle_set_paused(RID p_obstacle, bool p_paused) = 0;
virtual bool obstacle_get_paused(RID p_obstacle) const; virtual bool obstacle_get_paused(RID p_obstacle) const = 0;
virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius); virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) = 0;
virtual void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity); virtual void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) = 0;
virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position); virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) = 0;
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices); virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) = 0;
virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers); virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0;
/// Returns a customized navigation path using a query parameters object /// Returns a customized navigation path using a query parameters object
virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const; virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const = 0;
virtual void init() = 0;
virtual void sync() = 0;
virtual void finish() = 0;
/// Destroy the `RID` /// Destroy the `RID`
virtual void free(RID p_object); virtual void free(RID p_object) = 0;
virtual void parse_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
NavigationServer2D(); NavigationServer2D();
virtual ~NavigationServer2D(); ~NavigationServer2D() override;
void set_debug_enabled(bool p_enabled); void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const; bool get_debug_enabled() const;
@ -354,4 +367,15 @@ private:
#endif // DEBUG_ENABLED #endif // DEBUG_ENABLED
}; };
typedef NavigationServer2D *(*NavigationServer2DCallback)();
/// Manager used for the server singleton registration
class NavigationServer2DManager {
static NavigationServer2DCallback create_callback;
public:
static void set_default_server(NavigationServer2DCallback p_callback);
static NavigationServer2D *new_default_server();
};
#endif // NAVIGATION_SERVER_2D_H #endif // NAVIGATION_SERVER_2D_H

View File

@ -0,0 +1,155 @@
/**************************************************************************/
/* navigation_server_2d_dummy.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NAVIGATION_SERVER_2D_DUMMY_H
#define NAVIGATION_SERVER_2D_DUMMY_H
#include "servers/navigation_server_2d.h"
class NavigationServer2DDummy : public NavigationServer2D {
GDCLASS(NavigationServer2DDummy, NavigationServer2D);
public:
TypedArray<RID> get_maps() const override { return TypedArray<RID>(); }
RID map_create() override { return RID(); }
void map_set_active(RID p_map, bool p_active) override {}
bool map_is_active(RID p_map) const override { return false; }
void map_set_cell_size(RID p_map, real_t p_cell_size) override {}
real_t map_get_cell_size(RID p_map) const override { return 0; }
void map_set_use_edge_connections(RID p_map, bool p_enabled) override {}
bool map_get_use_edge_connections(RID p_map) const override { return false; }
void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) override {}
real_t map_get_edge_connection_margin(RID p_map) const override { return 0; }
void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) override {}
real_t map_get_link_connection_radius(RID p_map) const override { return 0; }
Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override { return Vector<Vector2>(); }
Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const override { return Vector2(); }
RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const override { return RID(); }
TypedArray<RID> map_get_links(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_regions(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_agents(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_obstacles(RID p_map) const override { return TypedArray<RID>(); }
void map_force_update(RID p_map) override {}
RID region_create() override { return RID(); }
void region_set_enabled(RID p_region, bool p_enabled) override {}
bool region_get_enabled(RID p_region) const override { return false; }
void region_set_use_edge_connections(RID p_region, bool p_enabled) override {}
bool region_get_use_edge_connections(RID p_region) const override { return false; }
void region_set_enter_cost(RID p_region, real_t p_enter_cost) override {}
real_t region_get_enter_cost(RID p_region) const override { return 0; }
void region_set_travel_cost(RID p_region, real_t p_travel_cost) override {}
real_t region_get_travel_cost(RID p_region) const override { return 0; }
void region_set_owner_id(RID p_region, ObjectID p_owner_id) override {}
ObjectID region_get_owner_id(RID p_region) const override { return ObjectID(); }
bool region_owns_point(RID p_region, const Vector2 &p_point) const override { return false; }
void region_set_map(RID p_region, RID p_map) override {}
RID region_get_map(RID p_region) const override { return RID(); }
void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override {}
uint32_t region_get_navigation_layers(RID p_region) const override { return 0; }
void region_set_transform(RID p_region, Transform2D p_transform) override {}
void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) override {}
int region_get_connections_count(RID p_region) const override { return 0; }
Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector2(); }
Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector2(); }
RID link_create() override { return RID(); }
void link_set_map(RID p_link, RID p_map) override {}
RID link_get_map(RID p_link) const override { return RID(); }
void link_set_enabled(RID p_link, bool p_enabled) override {}
bool link_get_enabled(RID p_link) const override { return false; }
void link_set_bidirectional(RID p_link, bool p_bidirectional) override {}
bool link_is_bidirectional(RID p_link) const override { return false; }
void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) override {}
uint32_t link_get_navigation_layers(RID p_link) const override { return 0; }
void link_set_start_position(RID p_link, Vector2 p_position) override {}
Vector2 link_get_start_position(RID p_link) const override { return Vector2(); }
void link_set_end_position(RID p_link, Vector2 p_position) override {}
Vector2 link_get_end_position(RID p_link) const override { return Vector2(); }
void link_set_enter_cost(RID p_link, real_t p_enter_cost) override {}
real_t link_get_enter_cost(RID p_link) const override { return 0; }
void link_set_travel_cost(RID p_link, real_t p_travel_cost) override {}
real_t link_get_travel_cost(RID p_link) const override { return 0; }
void link_set_owner_id(RID p_link, ObjectID p_owner_id) override {}
ObjectID link_get_owner_id(RID p_link) const override { return ObjectID(); }
RID agent_create() override { return RID(); }
void agent_set_map(RID p_agent, RID p_map) override {}
RID agent_get_map(RID p_agent) const override { return RID(); }
void agent_set_paused(RID p_agent, bool p_paused) override {}
bool agent_get_paused(RID p_agent) const override { return false; }
void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) override {}
bool agent_get_avoidance_enabled(RID p_agent) const override { return false; }
void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override {}
void agent_set_max_neighbors(RID p_agent, int p_count) override {}
void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override {}
void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override {}
void agent_set_radius(RID p_agent, real_t p_radius) override {}
void agent_set_max_speed(RID p_agent, real_t p_max_speed) override {}
void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) override {}
void agent_set_velocity(RID p_agent, Vector2 p_velocity) override {}
void agent_set_position(RID p_agent, Vector2 p_position) override {}
bool agent_is_map_changed(RID p_agent) const override { return false; }
void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override {}
void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override {}
void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override {}
void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override {}
RID obstacle_create() override { return RID(); }
void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) override {}
bool obstacle_get_avoidance_enabled(RID p_obstacle) const override { return false; }
void obstacle_set_map(RID p_obstacle, RID p_map) override {}
RID obstacle_get_map(RID p_obstacle) const override { return RID(); }
void obstacle_set_paused(RID p_obstacle, bool p_paused) override {}
bool obstacle_get_paused(RID p_obstacle) const override { return false; }
void obstacle_set_radius(RID p_obstacle, real_t p_radius) override {}
void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) override {}
void obstacle_set_position(RID p_obstacle, Vector2 p_position) override {}
void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) override {}
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const override {}
void init() override {}
void sync() override {}
void finish() override {}
void free(RID p_object) override {}
void parse_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
void set_debug_enabled(bool p_enabled) {}
bool get_debug_enabled() const { return false; }
};
#endif // NAVIGATION_SERVER_2D_DUMMY_H

View File

@ -302,6 +302,7 @@ public:
/// Note: This function is not thread safe. /// Note: This function is not thread safe.
virtual void process(real_t delta_time) = 0; virtual void process(real_t delta_time) = 0;
virtual void init() = 0; virtual void init() = 0;
virtual void sync() = 0;
virtual void finish() = 0; virtual void finish() = 0;
/// Returns a customized navigation path using a query parameters object /// Returns a customized navigation path using a query parameters object

View File

@ -152,6 +152,7 @@ public:
void set_active(bool p_active) override {} void set_active(bool p_active) override {}
void process(real_t delta_time) override {} void process(real_t delta_time) override {}
void init() override {} void init() override {}
void sync() override {}
void finish() override {} void finish() override {}
NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override { return NavigationUtilities::PathQueryResult(); } NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override { return NavigationUtilities::PathQueryResult(); }
int get_process_info(ProcessInfo p_info) const override { return 0; } int get_process_info(ProcessInfo p_info) const override { return 0; }

View File

@ -252,7 +252,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
ERR_PRINT_OFF; ERR_PRINT_OFF;
navigation_server_3d = NavigationServer3DManager::new_default_server(); navigation_server_3d = NavigationServer3DManager::new_default_server();
navigation_server_2d = memnew(NavigationServer2D); navigation_server_2d = NavigationServer2DManager::new_default_server();
ERR_PRINT_ON; ERR_PRINT_ON;
memnew(InputMap); memnew(InputMap);
@ -278,7 +278,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) { if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) {
ERR_PRINT_OFF; ERR_PRINT_OFF;
navigation_server_3d = NavigationServer3DManager::new_default_server(); navigation_server_3d = NavigationServer3DManager::new_default_server();
navigation_server_2d = memnew(NavigationServer2D); navigation_server_2d = NavigationServer2DManager::new_default_server();
ERR_PRINT_ON; ERR_PRINT_ON;
return; return;
} }

12
thirdparty/README.md vendored
View File

@ -91,6 +91,18 @@ Files extracted from upstream source:
- License: MPL 2.0 - License: MPL 2.0
## clipper2
- Upstream: https://github.com/AngusJohnson/Clipper2
- Version: 1.2.2 (756c5079aacab5837e812a143c59dc48a09f22e7, 2023)
- License: Boost Software License 1.0
Files extracted from upstream source:
- `CPP/Clipper2Lib` folder
- `LICENSE`
## cvtt ## cvtt
- Upstream: https://github.com/elasota/ConvectionKernels - Upstream: https://github.com/elasota/ConvectionKernels

23
thirdparty/clipper2/LICENSE vendored Normal file
View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,35 @@
diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
index c7522cb900..086d1b659c 100644
--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
@@ -20,6 +20,8 @@
#include <climits>
#include <numeric>
+#define CLIPPER2_THROW(exception) std::abort()
+
namespace Clipper2Lib
{
@@ -65,16 +67,16 @@ namespace Clipper2Lib
switch (error_code)
{
case precision_error_i:
- throw Clipper2Exception(precision_error);
+ CLIPPER2_THROW(Clipper2Exception(precision_error));
case scale_error_i:
- throw Clipper2Exception(scale_error);
+ CLIPPER2_THROW(Clipper2Exception(scale_error));
case non_pair_error_i:
- throw Clipper2Exception(non_pair_error);
+ CLIPPER2_THROW(Clipper2Exception(non_pair_error));
case range_error_i:
- throw Clipper2Exception(range_error);
+ CLIPPER2_THROW(Clipper2Exception(range_error));
}
#else
- ++error_code; // only to stop compiler warning
+ if(error_code) {}; // only to stop compiler 'parameter not used' warning
#endif
}

View File

@ -0,0 +1,846 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 March 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Core Clipper Library structures and functions *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_CORE_H
#define CLIPPER_CORE_H
#include <cstdint>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <climits>
#include <numeric>
#define CLIPPER2_THROW(exception) std::abort()
namespace Clipper2Lib
{
#if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
class Clipper2Exception : public std::exception {
public:
explicit Clipper2Exception(const char* description) :
m_descr(description) {}
virtual const char* what() const throw() override { return m_descr.c_str(); }
private:
std::string m_descr;
};
static const char* precision_error =
"Precision exceeds the permitted range";
static const char* range_error =
"Values exceed permitted range";
static const char* scale_error =
"Invalid scale (either 0 or too large)";
static const char* non_pair_error =
"There must be 2 values for each coordinate";
#endif
// error codes (2^n)
const int precision_error_i = 1; // non-fatal
const int scale_error_i = 2; // non-fatal
const int non_pair_error_i = 4; // non-fatal
const int range_error_i = 64;
static const double PI = 3.141592653589793238;
static const int64_t MAX_COORD = INT64_MAX >> 2;
static const int64_t MIN_COORD = -MAX_COORD;
static const int64_t INVALID = INT64_MAX;
const double max_coord = static_cast<double>(MAX_COORD);
const double min_coord = static_cast<double>(MIN_COORD);
static const double MAX_DBL = (std::numeric_limits<double>::max)();
static void DoError(int error_code)
{
#if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
switch (error_code)
{
case precision_error_i:
CLIPPER2_THROW(Clipper2Exception(precision_error));
case scale_error_i:
CLIPPER2_THROW(Clipper2Exception(scale_error));
case non_pair_error_i:
CLIPPER2_THROW(Clipper2Exception(non_pair_error));
case range_error_i:
CLIPPER2_THROW(Clipper2Exception(range_error));
}
#else
if(error_code) {}; // only to stop compiler 'parameter not used' warning
#endif
}
//By far the most widely used filling rules for polygons are EvenOdd
//and NonZero, sometimes called Alternate and Winding respectively.
//https://en.wikipedia.org/wiki/Nonzero-rule
enum class FillRule { EvenOdd, NonZero, Positive, Negative };
// Point ------------------------------------------------------------------------
template <typename T>
struct Point {
T x;
T y;
#ifdef USINGZ
int64_t z;
template <typename T2>
inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0)
{
if constexpr (std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T2>::is_integer)
{
x = static_cast<T>(std::round(x_));
y = static_cast<T>(std::round(y_));
z = z_;
}
else
{
x = static_cast<T>(x_);
y = static_cast<T>(y_);
z = z_;
}
}
explicit Point() : x(0), y(0), z(0) {};
template <typename T2>
Point(const T2 x_, const T2 y_, const int64_t z_ = 0)
{
Init(x_, y_);
z = z_;
}
template <typename T2>
explicit Point<T>(const Point<T2>& p)
{
Init(p.x, p.y, p.z);
}
Point operator * (const double scale) const
{
return Point(x * scale, y * scale, z);
}
friend std::ostream& operator<<(std::ostream& os, const Point& point)
{
os << point.x << "," << point.y << "," << point.z << " ";
return os;
}
#else
template <typename T2>
inline void Init(const T2 x_ = 0, const T2 y_ = 0)
{
if constexpr (std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T2>::is_integer)
{
x = static_cast<T>(std::round(x_));
y = static_cast<T>(std::round(y_));
}
else
{
x = static_cast<T>(x_);
y = static_cast<T>(y_);
}
}
explicit Point() : x(0), y(0) {};
template <typename T2>
Point(const T2 x_, const T2 y_) { Init(x_, y_); }
template <typename T2>
explicit Point<T>(const Point<T2>& p) { Init(p.x, p.y); }
Point operator * (const double scale) const
{
return Point(x * scale, y * scale);
}
friend std::ostream& operator<<(std::ostream& os, const Point& point)
{
os << point.x << "," << point.y << " ";
return os;
}
#endif
friend bool operator==(const Point& a, const Point& b)
{
return a.x == b.x && a.y == b.y;
}
friend bool operator!=(const Point& a, const Point& b)
{
return !(a == b);
}
inline Point<T> operator-() const
{
return Point<T>(-x, -y);
}
inline Point operator+(const Point& b) const
{
return Point(x + b.x, y + b.y);
}
inline Point operator-(const Point& b) const
{
return Point(x - b.x, y - b.y);
}
inline void Negate() { x = -x; y = -y; }
};
//nb: using 'using' here (instead of typedef) as they can be used in templates
using Point64 = Point<int64_t>;
using PointD = Point<double>;
template <typename T>
using Path = std::vector<Point<T>>;
template <typename T>
using Paths = std::vector<Path<T>>;
using Path64 = Path<int64_t>;
using PathD = Path<double>;
using Paths64 = std::vector< Path64>;
using PathsD = std::vector< PathD>;
// Rect ------------------------------------------------------------------------
template <typename T>
struct Rect;
using Rect64 = Rect<int64_t>;
using RectD = Rect<double>;
template <typename T>
struct Rect {
T left;
T top;
T right;
T bottom;
Rect() :
left(0),
top(0),
right(0),
bottom(0) {}
Rect(T l, T t, T r, T b) :
left(l),
top(t),
right(r),
bottom(b) {}
Rect(bool is_valid)
{
if (is_valid)
{
left = right = top = bottom = 0;
}
else
{
left = top = std::numeric_limits<T>::max();
right = bottom = -std::numeric_limits<int64_t>::max();
}
}
T Width() const { return right - left; }
T Height() const { return bottom - top; }
void Width(T width) { right = left + width; }
void Height(T height) { bottom = top + height; }
Point<T> MidPoint() const
{
return Point<T>((left + right) / 2, (top + bottom) / 2);
}
Path<T> AsPath() const
{
Path<T> result;
result.reserve(4);
result.push_back(Point<T>(left, top));
result.push_back(Point<T>(right, top));
result.push_back(Point<T>(right, bottom));
result.push_back(Point<T>(left, bottom));
return result;
}
bool Contains(const Point<T>& pt) const
{
return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom;
}
bool Contains(const Rect<T>& rec) const
{
return rec.left >= left && rec.right <= right &&
rec.top >= top && rec.bottom <= bottom;
}
void Scale(double scale) {
left *= scale;
top *= scale;
right *= scale;
bottom *= scale;
}
bool IsEmpty() const { return bottom <= top || right <= left; };
bool Intersects(const Rect<T>& rec) const
{
return ((std::max)(left, rec.left) <= (std::min)(right, rec.right)) &&
((std::max)(top, rec.top) <= (std::min)(bottom, rec.bottom));
};
friend std::ostream& operator<<(std::ostream& os, const Rect<T>& rect) {
os << "("
<< rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom
<< ")";
return os;
}
};
template <typename T1, typename T2>
inline Rect<T1> ScaleRect(const Rect<T2>& rect, double scale)
{
Rect<T1> result;
if constexpr (std::numeric_limits<T1>::is_integer &&
!std::numeric_limits<T2>::is_integer)
{
result.left = static_cast<T1>(std::round(rect.left * scale));
result.top = static_cast<T1>(std::round(rect.top * scale));
result.right = static_cast<T1>(std::round(rect.right * scale));
result.bottom = static_cast<T1>(std::round(rect.bottom * scale));
}
else
{
result.left = rect.left * scale;
result.top = rect.top * scale;
result.right = rect.right * scale;
result.bottom = rect.bottom * scale;
}
return result;
}
static const Rect64 MaxInvalidRect64 = Rect64(
INT64_MAX, INT64_MAX, INT64_MIN, INT64_MIN);
static const RectD MaxInvalidRectD = RectD(
MAX_DBL, MAX_DBL, -MAX_DBL, -MAX_DBL);
template <typename T>
Rect<T> GetBounds(const Path<T>& path)
{
auto xmin = std::numeric_limits<T>::max();
auto ymin = std::numeric_limits<T>::max();
auto xmax = std::numeric_limits<T>::lowest();
auto ymax = std::numeric_limits<T>::lowest();
for (const auto& p : path)
{
if (p.x < xmin) xmin = p.x;
if (p.x > xmax) xmax = p.x;
if (p.y < ymin) ymin = p.y;
if (p.y > ymax) ymax = p.y;
}
return Rect<T>(xmin, ymin, xmax, ymax);
}
template <typename T>
Rect<T> GetBounds(const Paths<T>& paths)
{
auto xmin = std::numeric_limits<T>::max();
auto ymin = std::numeric_limits<T>::max();
auto xmax = std::numeric_limits<T>::lowest();
auto ymax = std::numeric_limits<T>::lowest();
for (const Path<T>& path : paths)
for (const Point<T>& p : path)
{
if (p.x < xmin) xmin = p.x;
if (p.x > xmax) xmax = p.x;
if (p.y < ymin) ymin = p.y;
if (p.y > ymax) ymax = p.y;
}
return Rect<T>(xmin, ymin, xmax, ymax);
}
template <typename T>
std::ostream& operator << (std::ostream& outstream, const Path<T>& path)
{
if (!path.empty())
{
auto pt = path.cbegin(), last = path.cend() - 1;
while (pt != last)
outstream << *pt++ << ", ";
outstream << *last << std::endl;
}
return outstream;
}
template <typename T>
std::ostream& operator << (std::ostream& outstream, const Paths<T>& paths)
{
for (auto p : paths)
outstream << p;
return outstream;
}
template <typename T1, typename T2>
inline Path<T1> ScalePath(const Path<T2>& path,
double scale_x, double scale_y, int& error_code)
{
Path<T1> result;
if (scale_x == 0 || scale_y == 0)
{
error_code |= scale_error_i;
DoError(scale_error_i);
// if no exception, treat as non-fatal error
if (scale_x == 0) scale_x = 1.0;
if (scale_y == 0) scale_y = 1.0;
}
result.reserve(path.size());
#ifdef USINGZ
std::transform(path.begin(), path.end(), back_inserter(result),
[scale_x, scale_y](const auto& pt)
{ return Point<T1>(pt.x * scale_x, pt.y * scale_y, pt.z); });
#else
std::transform(path.begin(), path.end(), back_inserter(result),
[scale_x, scale_y](const auto& pt)
{ return Point<T1>(pt.x * scale_x, pt.y * scale_y); });
#endif
return result;
}
template <typename T1, typename T2>
inline Path<T1> ScalePath(const Path<T2>& path,
double scale, int& error_code)
{
return ScalePath<T1, T2>(path, scale, scale, error_code);
}
template <typename T1, typename T2>
inline Paths<T1> ScalePaths(const Paths<T2>& paths,
double scale_x, double scale_y, int& error_code)
{
Paths<T1> result;
if constexpr (std::numeric_limits<T1>::is_integer &&
!std::numeric_limits<T2>::is_integer)
{
RectD r = GetBounds(paths);
if ((r.left * scale_x) < min_coord ||
(r.right * scale_x) > max_coord ||
(r.top * scale_y) < min_coord ||
(r.bottom * scale_y) > max_coord)
{
error_code |= range_error_i;
DoError(range_error_i);
return result; // empty path
}
}
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[=, &error_code](const auto& path)
{ return ScalePath<T1, T2>(path, scale_x, scale_y, error_code); });
return result;
}
template <typename T1, typename T2>
inline Paths<T1> ScalePaths(const Paths<T2>& paths,
double scale, int& error_code)
{
return ScalePaths<T1, T2>(paths, scale, scale, error_code);
}
template <typename T1, typename T2>
inline Path<T1> TransformPath(const Path<T2>& path)
{
Path<T1> result;
result.reserve(path.size());
std::transform(path.cbegin(), path.cend(), std::back_inserter(result),
[](const Point<T2>& pt) {return Point<T1>(pt); });
return result;
}
template <typename T1, typename T2>
inline Paths<T1> TransformPaths(const Paths<T2>& paths)
{
Paths<T1> result;
std::transform(paths.cbegin(), paths.cend(), std::back_inserter(result),
[](const Path<T2>& path) {return TransformPath<T1, T2>(path); });
return result;
}
inline PathD Path64ToPathD(const Path64& path)
{
return TransformPath<double, int64_t>(path);
}
inline PathsD Paths64ToPathsD(const Paths64& paths)
{
return TransformPaths<double, int64_t>(paths);
}
inline Path64 PathDToPath64(const PathD& path)
{
return TransformPath<int64_t, double>(path);
}
inline Paths64 PathsDToPaths64(const PathsD& paths)
{
return TransformPaths<int64_t, double>(paths);
}
template<typename T>
inline double Sqr(T val)
{
return static_cast<double>(val) * static_cast<double>(val);
}
template<typename T>
inline bool NearEqual(const Point<T>& p1,
const Point<T>& p2, double max_dist_sqrd)
{
return Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y) < max_dist_sqrd;
}
template<typename T>
inline Path<T> StripNearEqual(const Path<T>& path,
double max_dist_sqrd, bool is_closed_path)
{
if (path.size() == 0) return Path<T>();
Path<T> result;
result.reserve(path.size());
typename Path<T>::const_iterator path_iter = path.cbegin();
Point<T> first_pt = *path_iter++, last_pt = first_pt;
result.push_back(first_pt);
for (; path_iter != path.cend(); ++path_iter)
{
if (!NearEqual(*path_iter, last_pt, max_dist_sqrd))
{
last_pt = *path_iter;
result.push_back(last_pt);
}
}
if (!is_closed_path) return result;
while (result.size() > 1 &&
NearEqual(result.back(), first_pt, max_dist_sqrd)) result.pop_back();
return result;
}
template<typename T>
inline Paths<T> StripNearEqual(const Paths<T>& paths,
double max_dist_sqrd, bool is_closed_path)
{
Paths<T> result;
result.reserve(paths.size());
for (typename Paths<T>::const_iterator paths_citer = paths.cbegin();
paths_citer != paths.cend(); ++paths_citer)
{
result.push_back(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path));
}
return result;
}
template<typename T>
inline Path<T> StripDuplicates(const Path<T>& path, bool is_closed_path)
{
if (path.size() == 0) return Path<T>();
Path<T> result;
result.reserve(path.size());
typename Path<T>::const_iterator path_iter = path.cbegin();
Point<T> first_pt = *path_iter++, last_pt = first_pt;
result.push_back(first_pt);
for (; path_iter != path.cend(); ++path_iter)
{
if (*path_iter != last_pt)
{
last_pt = *path_iter;
result.push_back(last_pt);
}
}
if (!is_closed_path) return result;
while (result.size() > 1 && result.back() == first_pt) result.pop_back();
return result;
}
template<typename T>
inline Paths<T> StripDuplicates(const Paths<T>& paths, bool is_closed_path)
{
Paths<T> result;
result.reserve(paths.size());
for (typename Paths<T>::const_iterator paths_citer = paths.cbegin();
paths_citer != paths.cend(); ++paths_citer)
{
result.push_back(StripDuplicates(*paths_citer, is_closed_path));
}
return result;
}
// Miscellaneous ------------------------------------------------------------
inline void CheckPrecision(int& precision, int& error_code)
{
if (precision >= -8 && precision <= 8) return;
error_code |= precision_error_i; // non-fatal error
DoError(precision_error_i); // unless exceptions enabled
precision = precision > 8 ? 8 : -8;
}
inline void CheckPrecision(int& precision)
{
int error_code = 0;
CheckPrecision(precision, error_code);
}
template <typename T>
inline double CrossProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.y -
pt2.y) - static_cast<double>(pt2.y - pt1.y) * static_cast<double>(pt3.x - pt2.x));
}
template <typename T>
inline double CrossProduct(const Point<T>& vec1, const Point<T>& vec2)
{
return static_cast<double>(vec1.y * vec2.x) - static_cast<double>(vec2.y * vec1.x);
}
template <typename T>
inline double DotProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.x - pt2.x) +
static_cast<double>(pt2.y - pt1.y) * static_cast<double>(pt3.y - pt2.y));
}
template <typename T>
inline double DotProduct(const Point<T>& vec1, const Point<T>& vec2)
{
return static_cast<double>(vec1.x * vec2.x) + static_cast<double>(vec1.y * vec2.y);
}
template <typename T>
inline double DistanceSqr(const Point<T> pt1, const Point<T> pt2)
{
return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y);
}
template <typename T>
inline double DistanceFromLineSqrd(const Point<T>& pt, const Point<T>& ln1, const Point<T>& ln2)
{
//perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
//see http://en.wikipedia.org/wiki/Perpendicular_distance
double A = static_cast<double>(ln1.y - ln2.y);
double B = static_cast<double>(ln2.x - ln1.x);
double C = A * ln1.x + B * ln1.y;
C = A * pt.x + B * pt.y - C;
return (C * C) / (A * A + B * B);
}
template <typename T>
inline double Area(const Path<T>& path)
{
size_t cnt = path.size();
if (cnt < 3) return 0.0;
double a = 0.0;
typename Path<T>::const_iterator it1, it2 = path.cend() - 1, stop = it2;
if (!(cnt & 1)) ++stop;
for (it1 = path.cbegin(); it1 != stop;)
{
a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
it2 = it1 + 1;
a += static_cast<double>(it1->y + it2->y) * (it1->x - it2->x);
it1 += 2;
}
if (cnt & 1)
a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
return a * 0.5;
}
template <typename T>
inline double Area(const Paths<T>& paths)
{
double a = 0.0;
for (typename Paths<T>::const_iterator paths_iter = paths.cbegin();
paths_iter != paths.cend(); ++paths_iter)
{
a += Area<T>(*paths_iter);
}
return a;
}
template <typename T>
inline bool IsPositive(const Path<T>& poly)
{
// A curve has positive orientation [and area] if a region 'R'
// is on the left when traveling around the outside of 'R'.
//https://mathworld.wolfram.com/CurveOrientation.html
//nb: This statement is premised on using Cartesian coordinates
return Area<T>(poly) >= 0;
}
inline int64_t CheckCastInt64(double val)
{
if ((val >= max_coord) || (val <= min_coord)) return INVALID;
else return static_cast<int64_t>(val);
}
inline bool GetIntersectPoint(const Point64& ln1a, const Point64& ln1b,
const Point64& ln2a, const Point64& ln2b, Point64& ip)
{
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
double dx1 = static_cast<double>(ln1b.x - ln1a.x);
double dy1 = static_cast<double>(ln1b.y - ln1a.y);
double dx2 = static_cast<double>(ln2b.x - ln2a.x);
double dy2 = static_cast<double>(ln2b.y - ln2a.y);
double det = dy1 * dx2 - dy2 * dx1;
if (det == 0.0) return 0;
double qx = dx1 * ln1a.y - dy1 * ln1a.x;
double qy = dx2 * ln2a.y - dy2 * ln2a.x;
ip.x = CheckCastInt64((dx1 * qy - dx2 * qx) / det);
ip.y = CheckCastInt64((dy1 * qy - dy2 * qx) / det);
return (ip.x != INVALID && ip.y != INVALID);
}
inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
{
if (inclusive)
{
double res1 = CrossProduct(seg1a, seg2a, seg2b);
double res2 = CrossProduct(seg1b, seg2a, seg2b);
if (res1 * res2 > 0) return false;
double res3 = CrossProduct(seg2a, seg1a, seg1b);
double res4 = CrossProduct(seg2b, seg1a, seg1b);
if (res3 * res4 > 0) return false;
return (res1 || res2 || res3 || res4); // ensures not collinear
}
else {
return (CrossProduct(seg1a, seg2a, seg2b) *
CrossProduct(seg1b, seg2a, seg2b) < 0) &&
(CrossProduct(seg2a, seg1a, seg1b) *
CrossProduct(seg2b, seg1a, seg1b) < 0);
}
}
inline Point64 GetClosestPointOnSegment(const Point64& offPt,
const Point64& seg1, const Point64& seg2)
{
if (seg1.x == seg2.x && seg1.y == seg2.y) return seg1;
double dx = static_cast<double>(seg2.x - seg1.x);
double dy = static_cast<double>(seg2.y - seg1.y);
double q =
(static_cast<double>(offPt.x - seg1.x) * dx +
static_cast<double>(offPt.y - seg1.y) * dy) /
(Sqr(dx) + Sqr(dy));
if (q < 0) q = 0; else if (q > 1) q = 1;
return Point64(
seg1.x + static_cast<int64_t>(nearbyint(q * dx)),
seg1.y + static_cast<int64_t>(nearbyint(q * dy)));
}
enum class PointInPolygonResult { IsOn, IsInside, IsOutside };
template <typename T>
inline PointInPolygonResult PointInPolygon(const Point<T>& pt, const Path<T>& polygon)
{
if (polygon.size() < 3)
return PointInPolygonResult::IsOutside;
int val = 0;
typename Path<T>::const_iterator cbegin = polygon.cbegin(), first = cbegin, curr, prev;
typename Path<T>::const_iterator cend = polygon.cend();
while (first != cend && first->y == pt.y) ++first;
if (first == cend) // not a proper polygon
return PointInPolygonResult::IsOutside;
bool is_above = first->y < pt.y, starting_above = is_above;
curr = first +1;
while (true)
{
if (curr == cend)
{
if (cend == first || first == cbegin) break;
cend = first;
curr = cbegin;
}
if (is_above)
{
while (curr != cend && curr->y < pt.y) ++curr;
if (curr == cend) continue;
}
else
{
while (curr != cend && curr->y > pt.y) ++curr;
if (curr == cend) continue;
}
if (curr == cbegin)
prev = polygon.cend() - 1; //nb: NOT cend (since might equal first)
else
prev = curr - 1;
if (curr->y == pt.y)
{
if (curr->x == pt.x ||
(curr->y == prev->y &&
((pt.x < prev->x) != (pt.x < curr->x))))
return PointInPolygonResult::IsOn;
++curr;
if (curr == first) break;
continue;
}
if (pt.x < curr->x && pt.x < prev->x)
{
// we're only interested in edges crossing on the left
}
else if (pt.x > prev->x && pt.x > curr->x)
val = 1 - val; // toggle val
else
{
double d = CrossProduct(*prev, *curr, pt);
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val;
}
is_above = !is_above;
++curr;
}
if (is_above != starting_above)
{
cend = polygon.cend();
if (curr == cend) curr = cbegin;
if (curr == cbegin) prev = cend - 1;
else prev = curr - 1;
double d = CrossProduct(*prev, *curr, pt);
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val;
}
return (val == 0) ?
PointInPolygonResult::IsOutside :
PointInPolygonResult::IsInside;
}
} // namespace
#endif // CLIPPER_CORE_H

View File

@ -0,0 +1,610 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 26 March 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_ENGINE_H
#define CLIPPER_ENGINE_H
constexpr auto CLIPPER2_VERSION = "1.2.2";
#include <cstdlib>
#include <iostream>
#include <queue>
#include <vector>
#include <functional>
#include <numeric>
#include <memory>
#include "clipper.core.h"
namespace Clipper2Lib {
struct Scanline;
struct IntersectNode;
struct Active;
struct Vertex;
struct LocalMinima;
struct OutRec;
struct HorzSegment;
//Note: all clipping operations except for Difference are commutative.
enum class ClipType { None, Intersection, Union, Difference, Xor };
enum class PathType { Subject, Clip };
enum class JoinWith { None, Left, Right };
enum class VertexFlags : uint32_t {
None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
};
constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
{
return (enum VertexFlags)(uint32_t(a) & uint32_t(b));
}
constexpr enum VertexFlags operator |(enum VertexFlags a, enum VertexFlags b)
{
return (enum VertexFlags)(uint32_t(a) | uint32_t(b));
}
struct Vertex {
Point64 pt;
Vertex* next = nullptr;
Vertex* prev = nullptr;
VertexFlags flags = VertexFlags::None;
};
struct OutPt {
Point64 pt;
OutPt* next = nullptr;
OutPt* prev = nullptr;
OutRec* outrec;
HorzSegment* horz = nullptr;
OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) {
next = this;
prev = this;
}
};
class PolyPath;
class PolyPath64;
class PolyPathD;
using PolyTree64 = PolyPath64;
using PolyTreeD = PolyPathD;
struct OutRec;
typedef std::vector<OutRec*> OutRecList;
//OutRec: contains a path in the clipping solution. Edges in the AEL will
//have OutRec pointers assigned when they form part of the clipping solution.
struct OutRec {
size_t idx = 0;
OutRec* owner = nullptr;
Active* front_edge = nullptr;
Active* back_edge = nullptr;
OutPt* pts = nullptr;
PolyPath* polypath = nullptr;
OutRecList* splits = nullptr;
Rect64 bounds = {};
Path64 path;
bool is_open = false;
bool horz_done = false;
~OutRec() {
if (splits) delete splits;
// nb: don't delete the split pointers
// as these are owned by ClipperBase's outrec_list_
};
};
///////////////////////////////////////////////////////////////////
//Important: UP and DOWN here are premised on Y-axis positive down
//displays, which is the orientation used in Clipper's development.
///////////////////////////////////////////////////////////////////
struct Active {
Point64 bot;
Point64 top;
int64_t curr_x = 0; //current (updated at every new scanline)
double dx = 0.0;
int wind_dx = 1; //1 or -1 depending on winding direction
int wind_cnt = 0;
int wind_cnt2 = 0; //winding count of the opposite polytype
OutRec* outrec = nullptr;
//AEL: 'active edge list' (Vatti's AET - active edge table)
// a linked list of all edges (from left to right) that are present
// (or 'active') within the current scanbeam (a horizontal 'beam' that
// sweeps from bottom to top over the paths in the clipping operation).
Active* prev_in_ael = nullptr;
Active* next_in_ael = nullptr;
//SEL: 'sorted edge list' (Vatti's ST - sorted table)
// linked list used when sorting edges into their new positions at the
// top of scanbeams, but also (re)used to process horizontals.
Active* prev_in_sel = nullptr;
Active* next_in_sel = nullptr;
Active* jump = nullptr;
Vertex* vertex_top = nullptr;
LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti)
bool is_left_bound = false;
JoinWith join_with = JoinWith::None;
};
struct LocalMinima {
Vertex* vertex;
PathType polytype;
bool is_open;
LocalMinima(Vertex* v, PathType pt, bool open) :
vertex(v), polytype(pt), is_open(open){}
};
struct IntersectNode {
Point64 pt;
Active* edge1;
Active* edge2;
IntersectNode() : pt(Point64(0,0)), edge1(NULL), edge2(NULL) {}
IntersectNode(Active* e1, Active* e2, Point64& pt_) :
pt(pt_), edge1(e1), edge2(e2) {}
};
struct HorzSegment {
OutPt* left_op;
OutPt* right_op = nullptr;
bool left_to_right = true;
HorzSegment() : left_op(nullptr) { }
explicit HorzSegment(OutPt* op) : left_op(op) { }
};
struct HorzJoin {
OutPt* op1 = nullptr;
OutPt* op2 = nullptr;
HorzJoin() {};
explicit HorzJoin(OutPt* ltr, OutPt* rtl) : op1(ltr), op2(rtl) { }
};
#ifdef USINGZ
typedef std::function<void(const Point64& e1bot, const Point64& e1top,
const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64;
typedef std::function<void(const PointD& e1bot, const PointD& e1top,
const PointD& e2bot, const PointD& e2top, PointD& pt)> ZCallbackD;
#endif
typedef std::vector<HorzSegment> HorzSegmentList;
typedef std::unique_ptr<LocalMinima> LocalMinima_ptr;
typedef std::vector<LocalMinima_ptr> LocalMinimaList;
typedef std::vector<IntersectNode> IntersectNodeList;
// ClipperBase -------------------------------------------------------------
class ClipperBase {
private:
ClipType cliptype_ = ClipType::None;
FillRule fillrule_ = FillRule::EvenOdd;
FillRule fillpos = FillRule::Positive;
int64_t bot_y_ = 0;
bool minima_list_sorted_ = false;
bool using_polytree_ = false;
Active* actives_ = nullptr;
Active *sel_ = nullptr;
LocalMinimaList minima_list_; //pointers in case of memory reallocs
LocalMinimaList::iterator current_locmin_iter_;
std::vector<Vertex*> vertex_lists_;
std::priority_queue<int64_t> scanline_list_;
IntersectNodeList intersect_nodes_;
HorzSegmentList horz_seg_list_;
std::vector<HorzJoin> horz_join_list_;
void Reset();
inline void InsertScanline(int64_t y);
inline bool PopScanline(int64_t &y);
inline bool PopLocalMinima(int64_t y, LocalMinima*& local_minima);
void DisposeAllOutRecs();
void DisposeVerticesAndLocalMinima();
void DeleteEdges(Active*& e);
inline void AddLocMin(Vertex &vert, PathType polytype, bool is_open);
bool IsContributingClosed(const Active &e) const;
inline bool IsContributingOpen(const Active &e) const;
void SetWindCountForClosedPathEdge(Active &edge);
void SetWindCountForOpenPathEdge(Active &e);
void InsertLocalMinimaIntoAEL(int64_t bot_y);
void InsertLeftEdge(Active &e);
inline void PushHorz(Active &e);
inline bool PopHorz(Active *&e);
inline OutPt* StartOpenPath(Active &e, const Point64& pt);
inline void UpdateEdgeIntoAEL(Active *e);
OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt);
inline void DeleteFromAEL(Active &e);
inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
void DoIntersections(const int64_t top_y);
void AddNewIntersectNode(Active &e1, Active &e2, const int64_t top_y);
bool BuildIntersectList(const int64_t top_y);
void ProcessIntersectList();
void SwapPositionsInAEL(Active& edge1, Active& edge2);
OutRec* NewOutRec();
OutPt* AddOutPt(const Active &e, const Point64& pt);
OutPt* AddLocalMinPoly(Active &e1, Active &e2,
const Point64& pt, bool is_new = false);
OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
void DoHorizontal(Active &horz);
bool ResetHorzDirection(const Active &horz, const Vertex* max_vertex,
int64_t &horz_left, int64_t &horz_right);
void DoTopOfScanbeam(const int64_t top_y);
Active *DoMaxima(Active &e);
void JoinOutrecPaths(Active &e1, Active &e2);
void CompleteSplit(OutPt* op1, OutPt* op2, OutRec& outrec);
void FixSelfIntersects(OutRec* outrec);
void DoSplitOp(OutRec* outRec, OutPt* splitOp);
inline void AddTrialHorzJoin(OutPt* op);
void ConvertHorzSegsToJoins();
void ProcessHorzJoins();
void Split(Active& e, const Point64& pt);
inline void CheckJoinLeft(Active& e,
const Point64& pt, bool check_curr_x = false);
inline void CheckJoinRight(Active& e,
const Point64& pt, bool check_curr_x = false);
protected:
int error_code_ = 0;
bool has_open_paths_ = false;
bool succeeded_ = true;
OutRecList outrec_list_; //pointers in case list memory reallocated
bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees);
void CleanCollinear(OutRec* outrec);
bool CheckBounds(OutRec* outrec);
void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath);
void DeepCheckOwners(OutRec* outrec, PolyPath* polypath);
#ifdef USINGZ
ZCallback64 zCallback_ = nullptr;
void SetZ(const Active& e1, const Active& e2, Point64& pt);
#endif
void CleanUp(); // unlike Clear, CleanUp preserves added paths
void AddPath(const Path64& path, PathType polytype, bool is_open);
void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
public:
virtual ~ClipperBase();
int ErrorCode() { return error_code_; };
bool PreserveCollinear = true;
bool ReverseSolution = false;
void Clear();
#ifdef USINGZ
int64_t DefaultZ = 0;
#endif
};
// PolyPath / PolyTree --------------------------------------------------------
//PolyTree: is intended as a READ-ONLY data structure for CLOSED paths returned
//by clipping operations. While this structure is more complex than the
//alternative Paths structure, it does preserve path 'ownership' - ie those
//paths that contain (or own) other paths. This will be useful to some users.
class PolyPath {
protected:
PolyPath* parent_;
public:
PolyPath(PolyPath* parent = nullptr): parent_(parent){}
virtual ~PolyPath() {};
//https://en.cppreference.com/w/cpp/language/rule_of_three
PolyPath(const PolyPath&) = delete;
PolyPath& operator=(const PolyPath&) = delete;
unsigned Level() const
{
unsigned result = 0;
const PolyPath* p = parent_;
while (p) { ++result; p = p->parent_; }
return result;
}
virtual PolyPath* AddChild(const Path64& path) = 0;
virtual void Clear() = 0;
virtual size_t Count() const { return 0; }
const PolyPath* Parent() const { return parent_; }
bool IsHole() const
{
unsigned lvl = Level();
//Even levels except level 0
return lvl && !(lvl & 1);
}
};
typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
typedef typename std::vector<std::unique_ptr<PolyPathD>> PolyPathDList;
class PolyPath64 : public PolyPath {
private:
PolyPath64List childs_;
Path64 polygon_;
public:
explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
~PolyPath64() {
childs_.resize(0);
}
const PolyPath64* operator [] (size_t index) const
{
return childs_[index].get();
}
const PolyPath64* Child(size_t index) const
{
return childs_[index].get();
}
PolyPath64List::const_iterator begin() const { return childs_.cbegin(); }
PolyPath64List::const_iterator end() const { return childs_.cend(); }
PolyPath64* AddChild(const Path64& path) override
{
auto p = std::make_unique<PolyPath64>(this);
auto* result = childs_.emplace_back(std::move(p)).get();
result->polygon_ = path;
return result;
}
void Clear() override
{
childs_.resize(0);
}
size_t Count() const override
{
return childs_.size();
}
const Path64& Polygon() const { return polygon_; };
double Area() const
{
return std::accumulate(childs_.cbegin(), childs_.cend(),
Clipper2Lib::Area<int64_t>(polygon_),
[](double a, const auto& child) {return a + child->Area(); });
}
};
class PolyPathD : public PolyPath {
private:
PolyPathDList childs_;
double inv_scale_;
PathD polygon_;
public:
explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent)
{
inv_scale_ = parent ? parent->inv_scale_ : 1.0;
}
~PolyPathD() {
childs_.resize(0);
}
const PolyPathD* operator [] (size_t index) const
{
return childs_[index].get();
}
const PolyPathD* Child(size_t index) const
{
return childs_[index].get();
}
PolyPathDList::const_iterator begin() const { return childs_.cbegin(); }
PolyPathDList::const_iterator end() const { return childs_.cend(); }
void SetInvScale(double value) { inv_scale_ = value; }
double InvScale() { return inv_scale_; }
PolyPathD* AddChild(const Path64& path) override
{
int error_code = 0;
auto p = std::make_unique<PolyPathD>(this);
PolyPathD* result = childs_.emplace_back(std::move(p)).get();
result->polygon_ = ScalePath<double, int64_t>(path, inv_scale_, error_code);
return result;
}
void Clear() override
{
childs_.resize(0);
}
size_t Count() const override
{
return childs_.size();
}
const PathD& Polygon() const { return polygon_; };
double Area() const
{
return std::accumulate(childs_.begin(), childs_.end(),
Clipper2Lib::Area<double>(polygon_),
[](double a, const auto& child) {return a + child->Area(); });
}
};
class Clipper64 : public ClipperBase
{
private:
void BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen);
void BuildTree64(PolyPath64& polytree, Paths64& open_paths);
public:
#ifdef USINGZ
void SetZCallback(ZCallback64 cb) { zCallback_ = cb; }
#endif
void AddSubject(const Paths64& subjects)
{
AddPaths(subjects, PathType::Subject, false);
}
void AddOpenSubject(const Paths64& open_subjects)
{
AddPaths(open_subjects, PathType::Subject, true);
}
void AddClip(const Paths64& clips)
{
AddPaths(clips, PathType::Clip, false);
}
bool Execute(ClipType clip_type,
FillRule fill_rule, Paths64& closed_paths)
{
Paths64 dummy;
return Execute(clip_type, fill_rule, closed_paths, dummy);
}
bool Execute(ClipType clip_type, FillRule fill_rule,
Paths64& closed_paths, Paths64& open_paths)
{
closed_paths.clear();
open_paths.clear();
if (ExecuteInternal(clip_type, fill_rule, false))
BuildPaths64(closed_paths, &open_paths);
CleanUp();
return succeeded_;
}
bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree)
{
Paths64 dummy;
return Execute(clip_type, fill_rule, polytree, dummy);
}
bool Execute(ClipType clip_type,
FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths)
{
if (ExecuteInternal(clip_type, fill_rule, true))
{
open_paths.clear();
polytree.Clear();
BuildTree64(polytree, open_paths);
}
CleanUp();
return succeeded_;
}
};
class ClipperD : public ClipperBase {
private:
double scale_ = 1.0, invScale_ = 1.0;
#ifdef USINGZ
ZCallbackD zCallbackD_ = nullptr;
#endif
void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen);
void BuildTreeD(PolyPathD& polytree, PathsD& open_paths);
public:
explicit ClipperD(int precision = 2) : ClipperBase()
{
CheckPrecision(precision, error_code_);
// to optimize scaling / descaling precision
// set the scale to a power of double's radix (2) (#25)
scale_ = std::pow(std::numeric_limits<double>::radix,
std::ilogb(std::pow(10, precision)) + 1);
invScale_ = 1 / scale_;
}
#ifdef USINGZ
void SetZCallback(ZCallbackD cb) { zCallbackD_ = cb; };
void ZCB(const Point64& e1bot, const Point64& e1top,
const Point64& e2bot, const Point64& e2top, Point64& pt)
{
// de-scale (x & y)
// temporarily convert integers to their initial float values
// this will slow clipping marginally but will make it much easier
// to understand the coordinates passed to the callback function
PointD tmp = PointD(pt) * invScale_;
PointD e1b = PointD(e1bot) * invScale_;
PointD e1t = PointD(e1top) * invScale_;
PointD e2b = PointD(e2bot) * invScale_;
PointD e2t = PointD(e2top) * invScale_;
zCallbackD_(e1b,e1t, e2b, e2t, tmp);
pt.z = tmp.z; // only update 'z'
};
void CheckCallback()
{
if(zCallbackD_)
// if the user defined float point callback has been assigned
// then assign the proxy callback function
ClipperBase::zCallback_ =
std::bind(&ClipperD::ZCB, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5);
else
ClipperBase::zCallback_ = nullptr;
}
#endif
void AddSubject(const PathsD& subjects)
{
AddPaths(ScalePaths<int64_t, double>(subjects, scale_, error_code_), PathType::Subject, false);
}
void AddOpenSubject(const PathsD& open_subjects)
{
AddPaths(ScalePaths<int64_t, double>(open_subjects, scale_, error_code_), PathType::Subject, true);
}
void AddClip(const PathsD& clips)
{
AddPaths(ScalePaths<int64_t, double>(clips, scale_, error_code_), PathType::Clip, false);
}
bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths)
{
PathsD dummy;
return Execute(clip_type, fill_rule, closed_paths, dummy);
}
bool Execute(ClipType clip_type,
FillRule fill_rule, PathsD& closed_paths, PathsD& open_paths)
{
#ifdef USINGZ
CheckCallback();
#endif
if (ExecuteInternal(clip_type, fill_rule, false))
{
BuildPathsD(closed_paths, &open_paths);
}
CleanUp();
return succeeded_;
}
bool Execute(ClipType clip_type, FillRule fill_rule, PolyTreeD& polytree)
{
PathsD dummy;
return Execute(clip_type, fill_rule, polytree, dummy);
}
bool Execute(ClipType clip_type,
FillRule fill_rule, PolyTreeD& polytree, PathsD& open_paths)
{
#ifdef USINGZ
CheckCallback();
#endif
if (ExecuteInternal(clip_type, fill_rule, true))
{
polytree.Clear();
polytree.SetInvScale(invScale_);
open_paths.clear();
BuildTreeD(polytree, open_paths);
}
CleanUp();
return succeeded_;
}
};
} // namespace
#endif // CLIPPER_ENGINE_H

View File

@ -0,0 +1,774 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 23 March 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This module exports the Clipper2 Library (ie DLL/so) *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
// The exported functions below refer to simple structures that
// can be understood across multiple languages. Consequently
// Path64, PathD, Polytree64 etc are converted from C++ classes
// (std::vector<> etc) into the following data structures:
//
// CPath64 (int64_t*) & CPathD (double_t*):
// Path64 and PathD are converted into arrays of x,y coordinates.
// However in these arrays the first x,y coordinate pair is a
// counter with 'x' containing the number of following coordinate
// pairs. ('y' should be 0, with one exception explained below.)
// __________________________________
// |counter|coord1|coord2|...|coordN|
// |N ,0 |x1, y1|x2, y2|...|xN, yN|
// __________________________________
//
// CPaths64 (int64_t**) & CPathsD (double_t**):
// These are arrays of pointers to CPath64 and CPathD where
// the first pointer is to a 'counter path'. This 'counter
// path' has a single x,y coord pair with 'y' (not 'x')
// containing the number of paths that follow. ('x' = 0).
// _______________________________
// |counter|path1|path2|...|pathN|
// |addr0 |addr1|addr2|...|addrN| (*addr0[0]=0; *addr0[1]=N)
// _______________________________
//
// The structures of CPolytree64 and CPolytreeD are defined
// below and these structures don't need to be explained here.
#ifndef CLIPPER2_EXPORT_H
#define CLIPPER2_EXPORT_H
#include <cstdlib>
#include <vector>
#include "clipper2/clipper.core.h"
#include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h"
#include "clipper2/clipper.rectclip.h"
namespace Clipper2Lib {
typedef int64_t* CPath64;
typedef int64_t** CPaths64;
typedef double* CPathD;
typedef double** CPathsD;
typedef struct CPolyPath64 {
CPath64 polygon;
uint32_t is_hole;
uint32_t child_count;
CPolyPath64* childs;
}
CPolyTree64;
typedef struct CPolyPathD {
CPathD polygon;
uint32_t is_hole;
uint32_t child_count;
CPolyPathD* childs;
}
CPolyTreeD;
template <typename T>
struct CRect {
T left;
T top;
T right;
T bottom;
};
typedef CRect<int64_t> CRect64;
typedef CRect<double> CRectD;
template <typename T>
inline bool CRectIsEmpty(const CRect<T>& rect)
{
return (rect.right <= rect.left) || (rect.bottom <= rect.top);
}
template <typename T>
inline Rect<T> CRectToRect(const CRect<T>& rect)
{
Rect<T> result;
result.left = rect.left;
result.top = rect.top;
result.right = rect.right;
result.bottom = rect.bottom;
return result;
}
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
//////////////////////////////////////////////////////
// EXPORTED FUNCTION DEFINITIONS
//////////////////////////////////////////////////////
EXTERN_DLL_EXPORT const char* Version();
// Some of the functions below will return data in the various CPath
// and CPolyTree structures which are pointers to heap allocated
// memory. Eventually this memory will need to be released with one
// of the following 'DisposeExported' functions. (This may be the
// only safe way to release this memory since the executable
// accessing these exported functions may use a memory manager that
// allocates and releases heap memory in a different way. Also,
// CPath structures that have been constructed by the executable
// should not be destroyed using these 'DisposeExported' functions.)
EXTERN_DLL_EXPORT void DisposeExportedCPath64(CPath64 p);
EXTERN_DLL_EXPORT void DisposeExportedCPaths64(CPaths64& pp);
EXTERN_DLL_EXPORT void DisposeExportedCPathD(CPathD p);
EXTERN_DLL_EXPORT void DisposeExportedCPathsD(CPathsD& pp);
EXTERN_DLL_EXPORT void DisposeExportedCPolyTree64(CPolyTree64*& cpt);
EXTERN_DLL_EXPORT void DisposeExportedCPolyTreeD(CPolyTreeD*& cpt);
// Boolean clipping:
// cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4
// fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3
EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPaths64& solution, CPaths64& solution_open,
bool preserve_collinear = true, bool reverse_solution = false);
EXTERN_DLL_EXPORT int BooleanOpPt64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPolyTree64*& solution, CPaths64& solution_open,
bool preserve_collinear = true, bool reverse_solution = false);
EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPathsD& solution, CPathsD& solution_open, int precision = 2,
bool preserve_collinear = true, bool reverse_solution = false);
EXTERN_DLL_EXPORT int BooleanOpPtD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPolyTreeD*& solution, CPathsD& solution_open, int precision = 2,
bool preserve_collinear = true, bool reverse_solution = false);
// Polygon offsetting (inflate/deflate):
// jointype: Square=0, Round=1, Miter=2
// endtype: Polygon=0, Joined=1, Butt=2, Square=3, Round=4
EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
double delta, uint8_t jointype, uint8_t endtype,
double miter_limit = 2.0, double arc_tolerance = 0.0,
bool reverse_solution = false);
EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
double delta, uint8_t jointype, uint8_t endtype,
int precision = 2, double miter_limit = 2.0,
double arc_tolerance = 0.0, bool reverse_solution = false);
// ExecuteRectClip & ExecuteRectClipLines:
EXTERN_DLL_EXPORT CPaths64 ExecuteRectClip64(const CRect64& rect,
const CPaths64 paths, bool convex_only = false);
EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect,
const CPathsD paths, int precision = 2, bool convex_only = false);
EXTERN_DLL_EXPORT CPaths64 ExecuteRectClipLines64(const CRect64& rect,
const CPaths64 paths);
EXTERN_DLL_EXPORT CPathsD ExecuteRectClipLinesD(const CRectD& rect,
const CPathsD paths, int precision = 2);
//////////////////////////////////////////////////////
// INTERNAL FUNCTIONS
//////////////////////////////////////////////////////
inline CPath64 CreateCPath64(size_t cnt1, size_t cnt2);
inline CPath64 CreateCPath64(const Path64& p);
inline CPaths64 CreateCPaths64(const Paths64& pp);
inline Path64 ConvertCPath64(const CPath64& p);
inline Paths64 ConvertCPaths64(const CPaths64& pp);
inline CPathD CreateCPathD(size_t cnt1, size_t cnt2);
inline CPathD CreateCPathD(const PathD& p);
inline CPathsD CreateCPathsD(const PathsD& pp);
inline PathD ConvertCPathD(const CPathD& p);
inline PathsD ConvertCPathsD(const CPathsD& pp);
// the following function avoid multiple conversions
inline CPathD CreateCPathD(const Path64& p, double scale);
inline CPathsD CreateCPathsD(const Paths64& pp, double scale);
inline Path64 ConvertCPathD(const CPathD& p, double scale);
inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale);
inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt);
inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale);
EXTERN_DLL_EXPORT const char* Version()
{
return CLIPPER2_VERSION;
}
EXTERN_DLL_EXPORT void DisposeExportedCPath64(CPath64 p)
{
if (p) delete[] p;
}
EXTERN_DLL_EXPORT void DisposeExportedCPaths64(CPaths64& pp)
{
if (!pp) return;
CPaths64 v = pp;
CPath64 cnts = *v;
const size_t cnt = static_cast<size_t>(cnts[1]);
for (size_t i = 0; i <= cnt; ++i) //nb: cnt +1
DisposeExportedCPath64(*v++);
delete[] pp;
pp = nullptr;
}
EXTERN_DLL_EXPORT void DisposeExportedCPathD(CPathD p)
{
if (p) delete[] p;
}
EXTERN_DLL_EXPORT void DisposeExportedCPathsD(CPathsD& pp)
{
if (!pp) return;
CPathsD v = pp;
CPathD cnts = *v;
size_t cnt = static_cast<size_t>(cnts[1]);
for (size_t i = 0; i <= cnt; ++i) //nb: cnt +1
DisposeExportedCPathD(*v++);
delete[] pp;
pp = nullptr;
}
EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPaths64& solution, CPaths64& solution_open,
bool preserve_collinear, bool reverse_solution)
{
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
Paths64 sub, sub_open, clp, sol, sol_open;
sub = ConvertCPaths64(subjects);
sub_open = ConvertCPaths64(subjects_open);
clp = ConvertCPaths64(clips);
Clipper64 clipper;
clipper.PreserveCollinear = preserve_collinear;
clipper.ReverseSolution = reverse_solution;
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open))
return -1; // clipping bug - should never happen :)
solution = CreateCPaths64(sol);
solution_open = CreateCPaths64(sol_open);
return 0; //success !!
}
EXTERN_DLL_EXPORT int BooleanOpPt64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPolyTree64*& solution, CPaths64& solution_open,
bool preserve_collinear, bool reverse_solution)
{
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
Paths64 sub, sub_open, clp, sol_open;
sub = ConvertCPaths64(subjects);
sub_open = ConvertCPaths64(subjects_open);
clp = ConvertCPaths64(clips);
PolyTree64 pt;
Clipper64 clipper;
clipper.PreserveCollinear = preserve_collinear;
clipper.ReverseSolution = reverse_solution;
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), pt, sol_open))
return -1; // clipping bug - should never happen :)
solution = CreateCPolyTree64(pt);
solution_open = CreateCPaths64(sol_open);
return 0; //success !!
}
EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPathsD& solution, CPathsD& solution_open, int precision,
bool preserve_collinear, bool reverse_solution)
{
if (precision < -8 || precision > 8) return -5;
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
const double scale = std::pow(10, precision);
Paths64 sub, sub_open, clp, sol, sol_open;
sub = ConvertCPathsD(subjects, scale);
sub_open = ConvertCPathsD(subjects_open, scale);
clp = ConvertCPathsD(clips, scale);
Clipper64 clipper;
clipper.PreserveCollinear = preserve_collinear;
clipper.ReverseSolution = reverse_solution;
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0)
clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype),
FillRule(fillrule), sol, sol_open)) return -1;
if (sol.size() > 0) solution = CreateCPathsD(sol, 1 / scale);
if (sol_open.size() > 0)
solution_open = CreateCPathsD(sol_open, 1 / scale);
return 0;
}
EXTERN_DLL_EXPORT int BooleanOpPtD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPolyTreeD*& solution, CPathsD& solution_open, int precision,
bool preserve_collinear, bool reverse_solution)
{
if (precision < -8 || precision > 8) return -5;
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
const double scale = std::pow(10, precision);
Paths64 sub, sub_open, clp, sol_open;
sub = ConvertCPathsD(subjects, scale);
sub_open = ConvertCPathsD(subjects_open, scale);
clp = ConvertCPathsD(clips, scale);
PolyTree64 sol;
Clipper64 clipper;
clipper.PreserveCollinear = preserve_collinear;
clipper.ReverseSolution = reverse_solution;
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0)
clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype),
FillRule(fillrule), sol, sol_open)) return -1;
solution = CreateCPolyTreeD(sol, 1 / scale);
if (sol_open.size() > 0)
solution_open = CreateCPathsD(sol_open, 1 / scale);
return 0;
}
EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
double delta, uint8_t jointype, uint8_t endtype, double miter_limit,
double arc_tolerance, bool reverse_solution)
{
Paths64 pp;
pp = ConvertCPaths64(paths);
ClipperOffset clip_offset( miter_limit,
arc_tolerance, reverse_solution);
clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta, result);
return CreateCPaths64(result);
}
EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
double delta, uint8_t jointype, uint8_t endtype,
int precision, double miter_limit,
double arc_tolerance, bool reverse_solution)
{
if (precision < -8 || precision > 8 || !paths) return nullptr;
const double scale = std::pow(10, precision);
ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution);
Paths64 pp = ConvertCPathsD(paths, scale);
clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta * scale, result);
return CreateCPathsD(result, 1/scale);
}
EXTERN_DLL_EXPORT CPaths64 ExecuteRectClip64(const CRect64& rect,
const CPaths64 paths, bool convex_only)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r64 = CRectToRect(rect);
class RectClip rc(r64);
Paths64 pp = ConvertCPaths64(paths);
Paths64 result = rc.Execute(pp, convex_only);
return CreateCPaths64(result);
}
EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect,
const CPathsD paths, int precision, bool convex_only)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
if (precision < -8 || precision > 8) return nullptr;
const double scale = std::pow(10, precision);
RectD r = CRectToRect(rect);
Rect64 rec = ScaleRect<int64_t, double>(r, scale);
Paths64 pp = ConvertCPathsD(paths, scale);
class RectClip rc(rec);
Paths64 result = rc.Execute(pp, convex_only);
return CreateCPathsD(result, 1/scale);
}
EXTERN_DLL_EXPORT CPaths64 ExecuteRectClipLines64(const CRect64& rect,
const CPaths64 paths)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r = CRectToRect(rect);
class RectClipLines rcl (r);
Paths64 pp = ConvertCPaths64(paths);
Paths64 result = rcl.Execute(pp);
return CreateCPaths64(result);
}
EXTERN_DLL_EXPORT CPathsD ExecuteRectClipLinesD(const CRectD& rect,
const CPathsD paths, int precision)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
if (precision < -8 || precision > 8) return nullptr;
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(CRectToRect(rect), scale);
class RectClipLines rcl(r);
Paths64 pp = ConvertCPathsD(paths, scale);
Paths64 result = rcl.Execute(pp);
return CreateCPathsD(result, 1/scale);
}
inline CPath64 CreateCPath64(size_t cnt1, size_t cnt2)
{
// allocates memory for CPath64, fills in the counter, and
// returns the structure ready to be filled with path data
CPath64 result = new int64_t[2 + cnt1 *2];
result[0] = cnt1;
result[1] = cnt2;
return result;
}
inline CPath64 CreateCPath64(const Path64& p)
{
// allocates memory for CPath64, fills the counter
// and returns the memory filled with path data
size_t cnt = p.size();
if (!cnt) return nullptr;
CPath64 result = CreateCPath64(cnt, 0);
CPath64 v = result;
v += 2; // skip counters
for (const Point64& pt : p)
{
*v++ = pt.x;
*v++ = pt.y;
}
return result;
}
inline Path64 ConvertCPath64(const CPath64& p)
{
Path64 result;
if (p && *p)
{
CPath64 v = p;
const size_t cnt = static_cast<size_t>(p[0]);
v += 2; // skip counters
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
// x,y here avoids right to left function evaluation
// result.push_back(Point64(*v++, *v++));
int64_t x = *v++;
int64_t y = *v++;
result.push_back(Point64(x, y));
}
}
return result;
}
inline CPaths64 CreateCPaths64(const Paths64& pp)
{
// allocates memory for multiple CPath64 and
// and returns this memory filled with path data
size_t cnt = pp.size(), cnt2 = cnt;
// don't allocate space for empty paths
for (size_t i = 0; i < cnt; ++i)
if (!pp[i].size()) --cnt2;
if (!cnt2) return nullptr;
CPaths64 result = new int64_t* [cnt2 + 1];
CPaths64 v = result;
*v++ = CreateCPath64(0, cnt2); // assign a counter path
for (const Path64& p : pp)
{
*v = CreateCPath64(p);
if (*v) ++v;
}
return result;
}
inline Paths64 ConvertCPaths64(const CPaths64& pp)
{
Paths64 result;
if (pp)
{
CPaths64 v = pp;
CPath64 cnts = pp[0];
const size_t cnt = static_cast<size_t>(cnts[1]); // nb 2nd cnt
++v; // skip cnts
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
result.push_back(ConvertCPath64(*v++));
}
return result;
}
inline CPathD CreateCPathD(size_t cnt1, size_t cnt2)
{
// allocates memory for CPathD, fills in the counter, and
// returns the structure ready to be filled with path data
CPathD result = new double[2 + cnt1 * 2];
result[0] = static_cast<double>(cnt1);
result[1] = static_cast<double>(cnt2);
return result;
}
inline CPathD CreateCPathD(const PathD& p)
{
// allocates memory for CPath, fills the counter
// and returns the memory fills with path data
size_t cnt = p.size();
if (!cnt) return nullptr;
CPathD result = CreateCPathD(cnt, 0);
CPathD v = result;
v += 2; // skip counters
for (const PointD& pt : p)
{
*v++ = pt.x;
*v++ = pt.y;
}
return result;
}
inline PathD ConvertCPathD(const CPathD& p)
{
PathD result;
if (p)
{
CPathD v = p;
size_t cnt = static_cast<size_t>(v[0]);
v += 2; // skip counters
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
// x,y here avoids right to left function evaluation
// result.push_back(PointD(*v++, *v++));
double x = *v++;
double y = *v++;
result.push_back(PointD(x, y));
}
}
return result;
}
inline CPathsD CreateCPathsD(const PathsD& pp)
{
size_t cnt = pp.size(), cnt2 = cnt;
// don't allocate space for empty paths
for (size_t i = 0; i < cnt; ++i)
if (!pp[i].size()) --cnt2;
if (!cnt2) return nullptr;
CPathsD result = new double * [cnt2 + 1];
CPathsD v = result;
*v++ = CreateCPathD(0, cnt2); // assign counter path
for (const PathD& p : pp)
{
*v = CreateCPathD(p);
if (*v) { ++v; }
}
return result;
}
inline PathsD ConvertCPathsD(const CPathsD& pp)
{
PathsD result;
if (pp)
{
CPathsD v = pp;
CPathD cnts = v[0];
size_t cnt = static_cast<size_t>(cnts[1]);
++v; // skip cnts path
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
result.push_back(ConvertCPathD(*v++));
}
return result;
}
inline Path64 ConvertCPathD(const CPathD& p, double scale)
{
Path64 result;
if (p)
{
CPathD v = p;
size_t cnt = static_cast<size_t>(*v);
v += 2; // skip counters
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
// x,y here avoids right to left function evaluation
// result.push_back(PointD(*v++, *v++));
double x = *v++ * scale;
double y = *v++ * scale;
result.push_back(Point64(x, y));
}
}
return result;
}
inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale)
{
Paths64 result;
if (pp)
{
CPathsD v = pp;
CPathD cnts = v[0];
size_t cnt = static_cast<size_t>(cnts[1]);
result.reserve(cnt);
++v; // skip cnts path
for (size_t i = 0; i < cnt; ++i)
result.push_back(ConvertCPathD(*v++, scale));
}
return result;
}
inline CPathD CreateCPathD(const Path64& p, double scale)
{
// allocates memory for CPathD, fills in the counter, and
// returns the structure filled with *scaled* path data
size_t cnt = p.size();
if (!cnt) return nullptr;
CPathD result = CreateCPathD(cnt, 0);
CPathD v = result;
v += 2; // skip cnts
for (const Point64& pt : p)
{
*v++ = pt.x * scale;
*v++ = pt.y * scale;
}
return result;
}
inline CPathsD CreateCPathsD(const Paths64& pp, double scale)
{
// allocates memory for *multiple* CPathD, and
// returns the structure filled with scaled path data
size_t cnt = pp.size(), cnt2 = cnt;
// don't allocate space for empty paths
for (size_t i = 0; i < cnt; ++i)
if (!pp[i].size()) --cnt2;
if (!cnt2) return nullptr;
CPathsD result = new double* [cnt2 + 1];
CPathsD v = result;
*v++ = CreateCPathD(0, cnt2);
for (const Path64& p : pp)
{
*v = CreateCPathD(p, scale);
if (*v) ++v;
}
return result;
}
inline void InitCPolyPath64(CPolyTree64* cpt,
bool is_hole, const std::unique_ptr <PolyPath64>& pp)
{
cpt->polygon = CreateCPath64(pp->Polygon());
cpt->is_hole = is_hole;
size_t child_cnt = pp->Count();
cpt->child_count = static_cast<uint32_t>(child_cnt);
cpt->childs = nullptr;
if (!child_cnt) return;
cpt->childs = new CPolyPath64[child_cnt];
CPolyPath64* child = cpt->childs;
for (const std::unique_ptr <PolyPath64>& pp_child : *pp)
InitCPolyPath64(child++, !is_hole, pp_child);
}
inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt)
{
CPolyTree64* result = new CPolyTree64();
result->polygon = nullptr;
result->is_hole = false;
size_t child_cnt = pt.Count();
result->childs = nullptr;
result->child_count = static_cast<uint32_t>(child_cnt);
if (!child_cnt) return result;
result->childs = new CPolyPath64[child_cnt];
CPolyPath64* child = result->childs;
for (const std::unique_ptr <PolyPath64>& pp : pt)
InitCPolyPath64(child++, true, pp);
return result;
}
inline void DisposeCPolyPath64(CPolyPath64* cpp)
{
if (!cpp->child_count) return;
CPolyPath64* child = cpp->childs;
for (size_t i = 0; i < cpp->child_count; ++i)
DisposeCPolyPath64(child);
delete[] cpp->childs;
}
EXTERN_DLL_EXPORT void DisposeExportedCPolyTree64(CPolyTree64*& cpt)
{
if (!cpt) return;
DisposeCPolyPath64(cpt);
delete cpt;
cpt = nullptr;
}
inline void InitCPolyPathD(CPolyTreeD* cpt,
bool is_hole, const std::unique_ptr <PolyPath64>& pp, double scale)
{
cpt->polygon = CreateCPathD(pp->Polygon(), scale);
cpt->is_hole = is_hole;
size_t child_cnt = pp->Count();
cpt->child_count = static_cast<uint32_t>(child_cnt);
cpt->childs = nullptr;
if (!child_cnt) return;
cpt->childs = new CPolyPathD[child_cnt];
CPolyPathD* child = cpt->childs;
for (const std::unique_ptr <PolyPath64>& pp_child : *pp)
InitCPolyPathD(child++, !is_hole, pp_child, scale);
}
inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale)
{
CPolyTreeD* result = new CPolyTreeD();
result->polygon = nullptr;
result->is_hole = false;
size_t child_cnt = pt.Count();
result->child_count = static_cast<uint32_t>(child_cnt);
result->childs = nullptr;
if (!child_cnt) return result;
result->childs = new CPolyPathD[child_cnt];
CPolyPathD* child = result->childs;
for (const std::unique_ptr <PolyPath64>& pp : pt)
InitCPolyPathD(child++, true, pp, scale);
return result;
}
inline void DisposeCPolyPathD(CPolyPathD* cpp)
{
if (!cpp->child_count) return;
CPolyPathD* child = cpp->childs;
for (size_t i = 0; i < cpp->child_count; ++i)
DisposeCPolyPathD(child++);
delete[] cpp->childs;
}
EXTERN_DLL_EXPORT void DisposeExportedCPolyTreeD(CPolyTreeD*& cpt)
{
if (!cpt) return;
DisposeCPolyPathD(cpt);
delete cpt;
cpt = nullptr;
}
} // end Clipper2Lib namespace
#endif // CLIPPER2_EXPORT_H

View File

@ -0,0 +1,776 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 23 March 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This module provides a simple interface to the Clipper Library *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_H
#define CLIPPER_H
#include <cstdlib>
#include <type_traits>
#include <vector>
#include "clipper.core.h"
#include "clipper.engine.h"
#include "clipper.offset.h"
#include "clipper.minkowski.h"
#include "clipper.rectclip.h"
namespace Clipper2Lib {
inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
const Paths64& subjects, const Paths64& clips)
{
Paths64 result;
Clipper64 clipper;
clipper.AddSubject(subjects);
clipper.AddClip(clips);
clipper.Execute(cliptype, fillrule, result);
return result;
}
inline void BooleanOp(ClipType cliptype, FillRule fillrule,
const Paths64& subjects, const Paths64& clips, PolyTree64& solution)
{
Paths64 sol_open;
Clipper64 clipper;
clipper.AddSubject(subjects);
clipper.AddClip(clips);
clipper.Execute(cliptype, fillrule, solution, sol_open);
}
inline PathsD BooleanOp(ClipType cliptype, FillRule fillrule,
const PathsD& subjects, const PathsD& clips, int precision = 2)
{
int error_code = 0;
CheckPrecision(precision, error_code);
PathsD result;
if (error_code) return result;
ClipperD clipper(precision);
clipper.AddSubject(subjects);
clipper.AddClip(clips);
clipper.Execute(cliptype, fillrule, result);
return result;
}
inline void BooleanOp(ClipType cliptype, FillRule fillrule,
const PathsD& subjects, const PathsD& clips,
PolyTreeD& polytree, int precision = 2)
{
polytree.Clear();
int error_code = 0;
CheckPrecision(precision, error_code);
if (error_code) return;
ClipperD clipper(precision);
clipper.AddSubject(subjects);
clipper.AddClip(clips);
clipper.Execute(cliptype, fillrule, polytree);
}
inline Paths64 Intersect(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
{
return BooleanOp(ClipType::Intersection, fillrule, subjects, clips);
}
inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec);
}
inline Paths64 Union(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
{
return BooleanOp(ClipType::Union, fillrule, subjects, clips);
}
inline PathsD Union(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Union, fillrule, subjects, clips, decimal_prec);
}
inline Paths64 Union(const Paths64& subjects, FillRule fillrule)
{
Paths64 result;
Clipper64 clipper;
clipper.AddSubject(subjects);
clipper.Execute(ClipType::Union, fillrule, result);
return result;
}
inline PathsD Union(const PathsD& subjects, FillRule fillrule, int precision = 2)
{
PathsD result;
int error_code = 0;
CheckPrecision(precision, error_code);
if (error_code) return result;
ClipperD clipper(precision);
clipper.AddSubject(subjects);
clipper.Execute(ClipType::Union, fillrule, result);
return result;
}
inline Paths64 Difference(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
{
return BooleanOp(ClipType::Difference, fillrule, subjects, clips);
}
inline PathsD Difference(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Difference, fillrule, subjects, clips, decimal_prec);
}
inline Paths64 Xor(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
{
return BooleanOp(ClipType::Xor, fillrule, subjects, clips);
}
inline PathsD Xor(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec);
}
inline Paths64 InflatePaths(const Paths64& paths, double delta,
JoinType jt, EndType et, double miter_limit = 2.0,
double arc_tolerance = 0.0)
{
if (!delta) return paths;
ClipperOffset clip_offset(miter_limit, arc_tolerance);
clip_offset.AddPaths(paths, jt, et);
Paths64 solution;
clip_offset.Execute(delta, solution);
return solution;
}
inline PathsD InflatePaths(const PathsD& paths, double delta,
JoinType jt, EndType et, double miter_limit = 2.0,
int precision = 2, double arc_tolerance = 0.0)
{
int error_code = 0;
CheckPrecision(precision, error_code);
if (!delta) return paths;
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
ClipperOffset clip_offset(miter_limit, arc_tolerance);
clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et);
if (error_code) return PathsD();
Paths64 solution;
clip_offset.Execute(delta * scale, solution);
return ScalePaths<double, int64_t>(solution, 1 / scale, error_code);
}
inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy)
{
Path64 result;
result.reserve(path.size());
std::transform(path.begin(), path.end(), back_inserter(result),
[dx, dy](const auto& pt) { return Point64(pt.x + dx, pt.y +dy); });
return result;
}
inline PathD TranslatePath(const PathD& path, double dx, double dy)
{
PathD result;
result.reserve(path.size());
std::transform(path.begin(), path.end(), back_inserter(result),
[dx, dy](const auto& pt) { return PointD(pt.x + dx, pt.y + dy); });
return result;
}
inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy)
{
Paths64 result;
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
return result;
}
inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy)
{
PathsD result;
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
return result;
}
inline Paths64 ExecuteRectClip(const Rect64& rect,
const Paths64& paths, bool convex_only = false)
{
if (rect.IsEmpty() || paths.empty()) return Paths64();
RectClip rc(rect);
return rc.Execute(paths, convex_only);
}
inline Paths64 ExecuteRectClip(const Rect64& rect,
const Path64& path, bool convex_only = false)
{
if (rect.IsEmpty() || path.empty()) return Paths64();
RectClip rc(rect);
return rc.Execute(Paths64{ path }, convex_only);
}
inline PathsD ExecuteRectClip(const RectD& rect,
const PathsD& paths, bool convex_only = false, int precision = 2)
{
if (rect.IsEmpty() || paths.empty()) return PathsD();
int error_code = 0;
CheckPrecision(precision, error_code);
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClip rc(r);
Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code);
if (error_code) return PathsD(); // ie: error_code result is lost
return ScalePaths<double, int64_t>(
rc.Execute(pp, convex_only), 1 / scale, error_code);
}
inline PathsD ExecuteRectClip(const RectD& rect,
const PathD& path, bool convex_only = false, int precision = 2)
{
return ExecuteRectClip(rect, PathsD{ path }, convex_only, precision);
}
inline Paths64 ExecuteRectClipLines(const Rect64& rect, const Paths64& lines)
{
if (rect.IsEmpty() || lines.empty()) return Paths64();
RectClipLines rcl(rect);
return rcl.Execute(lines);
}
inline Paths64 ExecuteRectClipLines(const Rect64& rect, const Path64& line)
{
return ExecuteRectClipLines(rect, Paths64{ line });
}
inline PathsD ExecuteRectClipLines(const RectD& rect, const PathD& line, int precision = 2)
{
return ExecuteRectClip(rect, PathsD{ line }, precision);
}
inline PathsD ExecuteRectClipLines(const RectD& rect, const PathsD& lines, int precision = 2)
{
if (rect.IsEmpty() || lines.empty()) return PathsD();
int error_code = 0;
CheckPrecision(precision, error_code);
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClipLines rcl(r);
Paths64 p = ScalePaths<int64_t, double>(lines, scale, error_code);
if (error_code) return PathsD();
p = rcl.Execute(p);
return ScalePaths<double, int64_t>(p, 1 / scale, error_code);
}
namespace details
{
inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths)
{
paths.push_back(polypath.Polygon());
for (const auto& child : polypath)
PolyPathToPaths64(*child, paths);
}
inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths)
{
paths.push_back(polypath.Polygon());
for (const auto& child : polypath)
PolyPathToPathsD(*child, paths);
}
inline bool PolyPath64ContainsChildren(const PolyPath64& pp)
{
for (const auto& child : pp)
{
// return false if this child isn't fully contained by its parent
// the following algorithm is a bit too crude, and doesn't account
// for rounding errors. A better algorithm is to return false when
// consecutive vertices are found outside the parent's polygon.
//const Path64& path = pp.Polygon();
//if (std::any_of(child->Polygon().cbegin(), child->Polygon().cend(),
// [path](const auto& pt) {return (PointInPolygon(pt, path) ==
// PointInPolygonResult::IsOutside); })) return false;
int outsideCnt = 0;
for (const Point64& pt : child->Polygon())
{
PointInPolygonResult result = PointInPolygon(pt, pp.Polygon());
if (result == PointInPolygonResult::IsInside) --outsideCnt;
else if (result == PointInPolygonResult::IsOutside) ++outsideCnt;
if (outsideCnt > 1) return false;
else if (outsideCnt < -1) break;
}
// now check any nested children too
if (child->Count() > 0 && !PolyPath64ContainsChildren(*child))
return false;
}
return true;
}
static void OutlinePolyPath(std::ostream& os,
bool isHole, size_t count, const std::string& preamble)
{
std::string plural = (count == 1) ? "." : "s.";
if (isHole)
{
if (count)
os << preamble << "+- Hole with " << count <<
" nested polygon" << plural << std::endl;
else
os << preamble << "+- Hole" << std::endl;
}
else
{
if (count)
os << preamble << "+- Polygon with " << count <<
" hole" << plural << std::endl;
else
os << preamble << "+- Polygon" << std::endl;
}
}
static void OutlinePolyPath64(std::ostream& os, const PolyPath64& pp,
std::string preamble, bool last_child)
{
OutlinePolyPath(os, pp.IsHole(), pp.Count(), preamble);
preamble += (!last_child) ? "| " : " ";
if (pp.Count())
{
PolyPath64List::const_iterator it = pp.begin();
for (; it < pp.end() - 1; ++it)
OutlinePolyPath64(os, **it, preamble, false);
OutlinePolyPath64(os, **it, preamble, true);
}
}
static void OutlinePolyPathD(std::ostream& os, const PolyPathD& pp,
std::string preamble, bool last_child)
{
OutlinePolyPath(os, pp.IsHole(), pp.Count(), preamble);
preamble += (!last_child) ? "| " : " ";
if (pp.Count())
{
PolyPathDList::const_iterator it = pp.begin();
for (; it < pp.end() - 1; ++it)
OutlinePolyPathD(os, **it, preamble, false);
OutlinePolyPathD(os, **it, preamble, true);
}
}
} // end details namespace
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
{
PolyPath64List::const_iterator it = pp.begin();
for (; it < pp.end() - 1; ++it)
details::OutlinePolyPath64(os, **it, " ", false);
details::OutlinePolyPath64(os, **it, " ", true);
os << std::endl << std::endl;
if (!pp.Level()) os << std::endl;
return os;
}
inline std::ostream& operator<< (std::ostream& os, const PolyTreeD& pp)
{
PolyPathDList::const_iterator it = pp.begin();
for (; it < pp.end() - 1; ++it)
details::OutlinePolyPathD(os, **it, " ", false);
details::OutlinePolyPathD(os, **it, " ", true);
os << std::endl << std::endl;
if (!pp.Level()) os << std::endl;
return os;
}
inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree)
{
Paths64 result;
for (const auto& child : polytree)
details::PolyPathToPaths64(*child, result);
return result;
}
inline PathsD PolyTreeToPathsD(const PolyTreeD& polytree)
{
PathsD result;
for (const auto& child : polytree)
details::PolyPathToPathsD(*child, result);
return result;
}
inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree)
{
for (const auto& child : polytree)
if (child->Count() > 0 &&
!details::PolyPath64ContainsChildren(*child))
return false;
return true;
}
namespace details {
template<typename T, typename U>
inline constexpr void MakePathGeneric(const T list, size_t size,
std::vector<U>& result)
{
for (size_t i = 0; i < size; ++i)
#ifdef USINGZ
result[i / 2] = U{list[i], list[++i], 0};
#else
result[i / 2] = U{list[i], list[++i]};
#endif
}
} // end details namespace
template<typename T,
typename std::enable_if<
std::is_integral<T>::value &&
!std::is_same<char, T>::value, bool
>::type = true>
inline Path64 MakePath(const std::vector<T>& list)
{
const auto size = list.size() - list.size() % 2;
if (list.size() != size)
DoError(non_pair_error_i); // non-fatal without exception handling
Path64 result(size / 2); // else ignores unpaired value
details::MakePathGeneric(list, size, result);
return result;
}
template<typename T, std::size_t N,
typename std::enable_if<
std::is_integral<T>::value &&
!std::is_same<char, T>::value, bool
>::type = true>
inline Path64 MakePath(const T(&list)[N])
{
// Make the compiler error on unpaired value (i.e. no runtime effects).
static_assert(N % 2 == 0, "MakePath requires an even number of arguments");
Path64 result(N / 2);
details::MakePathGeneric(list, N, result);
return result;
}
template<typename T,
typename std::enable_if<
std::is_arithmetic<T>::value &&
!std::is_same<char, T>::value, bool
>::type = true>
inline PathD MakePathD(const std::vector<T>& list)
{
const auto size = list.size() - list.size() % 2;
if (list.size() != size)
DoError(non_pair_error_i); // non-fatal without exception handling
PathD result(size / 2); // else ignores unpaired value
details::MakePathGeneric(list, size, result);
return result;
}
template<typename T, std::size_t N,
typename std::enable_if<
std::is_arithmetic<T>::value &&
!std::is_same<char, T>::value, bool
>::type = true>
inline PathD MakePathD(const T(&list)[N])
{
// Make the compiler error on unpaired value (i.e. no runtime effects).
static_assert(N % 2 == 0, "MakePath requires an even number of arguments");
PathD result(N / 2);
details::MakePathGeneric(list, N, result);
return result;
}
inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false)
{
size_t len = p.size();
if (len < 3)
{
if (!is_open_path || len < 2 || p[0] == p[1]) return Path64();
else return p;
}
Path64 dst;
dst.reserve(len);
Path64::const_iterator srcIt = p.cbegin(), prevIt, stop = p.cend() - 1;
if (!is_open_path)
{
while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1)))
++srcIt;
while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt))
--stop;
if (srcIt == stop) return Path64();
}
prevIt = srcIt++;
dst.push_back(*prevIt);
for (; srcIt != stop; ++srcIt)
{
if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1)))
{
prevIt = srcIt;
dst.push_back(*prevIt);
}
}
if (is_open_path)
dst.push_back(*srcIt);
else if (CrossProduct(*prevIt, *stop, dst[0]))
dst.push_back(*stop);
else
{
while (dst.size() > 2 &&
!CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
dst.pop_back();
if (dst.size() < 3) return Path64();
}
return dst;
}
inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false)
{
int error_code = 0;
CheckPrecision(precision, error_code);
if (error_code) return PathD();
const double scale = std::pow(10, precision);
Path64 p = ScalePath<int64_t, double>(path, scale, error_code);
if (error_code) return PathD();
p = TrimCollinear(p, is_open_path);
return ScalePath<double, int64_t>(p, 1/scale, error_code);
}
template <typename T>
inline double Distance(const Point<T> pt1, const Point<T> pt2)
{
return std::sqrt(DistanceSqr(pt1, pt2));
}
template <typename T>
inline double Length(const Path<T>& path, bool is_closed_path = false)
{
double result = 0.0;
if (path.size() < 2) return result;
auto it = path.cbegin(), stop = path.end() - 1;
for (; it != stop; ++it)
result += Distance(*it, *(it + 1));
if (is_closed_path)
result += Distance(*stop, *path.cbegin());
return result;
}
template <typename T>
inline bool NearCollinear(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3, double sin_sqrd_min_angle_rads)
{
double cp = std::abs(CrossProduct(pt1, pt2, pt3));
return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads;
}
template <typename T>
inline Path<T> Ellipse(const Rect<T>& rect, int steps = 0)
{
return Ellipse(rect.MidPoint(),
static_cast<double>(rect.Width()) *0.5,
static_cast<double>(rect.Height()) * 0.5, steps);
}
template <typename T>
inline Path<T> Ellipse(const Point<T>& center,
double radiusX, double radiusY = 0, int steps = 0)
{
if (radiusX <= 0) return Path<T>();
if (radiusY <= 0) radiusY = radiusX;
if (steps <= 2)
steps = static_cast<int>(PI * sqrt((radiusX + radiusY) / 2));
double si = std::sin(2 * PI / steps);
double co = std::cos(2 * PI / steps);
double dx = co, dy = si;
Path<T> result;
result.reserve(steps);
result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y)));
for (int i = 1; i < steps; ++i)
{
result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy));
double x = dx * co - dy * si;
dy = dy * co + dx * si;
dx = x;
}
return result;
}
template <typename T>
inline double PerpendicDistFromLineSqrd(const Point<T>& pt,
const Point<T>& line1, const Point<T>& line2)
{
double a = static_cast<double>(pt.x - line1.x);
double b = static_cast<double>(pt.y - line1.y);
double c = static_cast<double>(line2.x - line1.x);
double d = static_cast<double>(line2.y - line1.y);
if (c == 0 && d == 0) return 0;
return Sqr(a * d - c * b) / (c * c + d * d);
}
inline size_t GetNext(size_t current, size_t high,
const std::vector<bool>& flags)
{
++current;
while (current <= high && flags[current]) ++current;
if (current <= high) return current;
current = 0;
while (flags[current]) ++current;
return current;
}
inline size_t GetPrior(size_t current, size_t high,
const std::vector<bool>& flags)
{
if (current == 0) current = high;
else --current;
while (current > 0 && flags[current]) --current;
if (!flags[current]) return current;
current = high;
while (flags[current]) --current;
return current;
}
template <typename T>
inline Path<T> SimplifyPath(const Path<T> path,
double epsilon, bool isOpenPath = false)
{
const size_t len = path.size(), high = len -1;
const double epsSqr = Sqr(epsilon);
if (len < 4) return Path<T>(path);
std::vector<bool> flags(len);
std::vector<double> distSqr(len);
size_t prior = high, curr = 0, start, next, prior2, next2;
if (isOpenPath)
{
distSqr[0] = MAX_DBL;
distSqr[high] = MAX_DBL;
}
else
{
distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]);
distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]);
}
for (size_t i = 1; i < high; ++i)
distSqr[i] = PerpendicDistFromLineSqrd(path[i], path[i - 1], path[i + 1]);
for (;;)
{
if (distSqr[curr] > epsSqr)
{
start = curr;
do
{
curr = GetNext(curr, high, flags);
} while (curr != start && distSqr[curr] > epsSqr);
if (curr == start) break;
}
prior = GetPrior(curr, high, flags);
next = GetNext(curr, high, flags);
if (next == prior) break;
if (distSqr[next] < distSqr[curr])
{
flags[next] = true;
next = GetNext(next, high, flags);
next2 = GetNext(next, high, flags);
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
if (next != high || !isOpenPath)
distSqr[next] = PerpendicDistFromLineSqrd(path[next], path[curr], path[next2]);
curr = next;
}
else
{
flags[curr] = true;
curr = next;
next = GetNext(next, high, flags);
prior2 = GetPrior(prior, high, flags);
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
if (prior != 0 || !isOpenPath)
distSqr[prior] = PerpendicDistFromLineSqrd(path[prior], path[prior2], path[curr]);
}
}
Path<T> result;
result.reserve(len);
for (typename Path<T>::size_type i = 0; i < len; ++i)
if (!flags[i]) result.push_back(path[i]);
return result;
}
template <typename T>
inline Paths<T> SimplifyPaths(const Paths<T> paths,
double epsilon, bool isOpenPath = false)
{
Paths<T> result;
result.reserve(paths.size());
for (const auto& path : paths)
result.push_back(SimplifyPath(path, epsilon, isOpenPath));
return result;
}
template <typename T>
inline void RDP(const Path<T> path, std::size_t begin,
std::size_t end, double epsSqrd, std::vector<bool>& flags)
{
typename Path<T>::size_type idx = 0;
double max_d = 0;
while (end > begin && path[begin] == path[end]) flags[end--] = false;
for (typename Path<T>::size_type i = begin + 1; i < end; ++i)
{
// PerpendicDistFromLineSqrd - avoids expensive Sqrt()
double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]);
if (d <= max_d) continue;
max_d = d;
idx = i;
}
if (max_d <= epsSqrd) return;
flags[idx] = true;
if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags);
if (idx < end - 1) RDP(path, idx, end, epsSqrd, flags);
}
template <typename T>
inline Path<T> RamerDouglasPeucker(const Path<T>& path, double epsilon)
{
const typename Path<T>::size_type len = path.size();
if (len < 5) return Path<T>(path);
std::vector<bool> flags(len);
flags[0] = true;
flags[len - 1] = true;
RDP(path, 0, len - 1, Sqr(epsilon), flags);
Path<T> result;
result.reserve(len);
for (typename Path<T>::size_type i = 0; i < len; ++i)
if (flags[i])
result.push_back(path[i]);
return result;
}
template <typename T>
inline Paths<T> RamerDouglasPeucker(const Paths<T>& paths, double epsilon)
{
Paths<T> result;
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[epsilon](const auto& path)
{ return RamerDouglasPeucker<T>(path, epsilon); });
return result;
}
} // end Clipper2Lib namespace
#endif // CLIPPER_H

View File

@ -0,0 +1,120 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 28 January 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Minkowski Sum and Difference *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_MINKOWSKI_H
#define CLIPPER_MINKOWSKI_H
#include <cstdlib>
#include <vector>
#include <string>
#include "clipper.core.h"
namespace Clipper2Lib
{
namespace detail
{
inline Paths64 Minkowski(const Path64& pattern, const Path64& path, bool isSum, bool isClosed)
{
size_t delta = isClosed ? 0 : 1;
size_t patLen = pattern.size(), pathLen = path.size();
if (patLen == 0 || pathLen == 0) return Paths64();
Paths64 tmp;
tmp.reserve(pathLen);
if (isSum)
{
for (const Point64& p : path)
{
Path64 path2(pattern.size());
std::transform(pattern.cbegin(), pattern.cend(),
path2.begin(), [p](const Point64& pt2) {return p + pt2; });
tmp.push_back(path2);
}
}
else
{
for (const Point64& p : path)
{
Path64 path2(pattern.size());
std::transform(pattern.cbegin(), pattern.cend(),
path2.begin(), [p](const Point64& pt2) {return p - pt2; });
tmp.push_back(path2);
}
}
Paths64 result;
result.reserve((pathLen - delta) * patLen);
size_t g = isClosed ? pathLen - 1 : 0;
for (size_t h = patLen - 1, i = delta; i < pathLen; ++i)
{
for (size_t j = 0; j < patLen; j++)
{
Path64 quad;
quad.reserve(4);
{
quad.push_back(tmp[g][h]);
quad.push_back(tmp[i][h]);
quad.push_back(tmp[i][j]);
quad.push_back(tmp[g][j]);
};
if (!IsPositive(quad))
std::reverse(quad.begin(), quad.end());
result.push_back(quad);
h = j;
}
g = i;
}
return result;
}
inline Paths64 Union(const Paths64& subjects, FillRule fillrule)
{
Paths64 result;
Clipper64 clipper;
clipper.AddSubject(subjects);
clipper.Execute(ClipType::Union, fillrule, result);
return result;
}
} // namespace internal
inline Paths64 MinkowskiSum(const Path64& pattern, const Path64& path, bool isClosed)
{
return detail::Union(detail::Minkowski(pattern, path, true, isClosed), FillRule::NonZero);
}
inline PathsD MinkowskiSum(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2)
{
int error_code = 0;
double scale = pow(10, decimalPlaces);
Path64 pat64 = ScalePath<int64_t, double>(pattern, scale, error_code);
Path64 path64 = ScalePath<int64_t, double>(path, scale, error_code);
Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, true, isClosed), FillRule::NonZero);
return ScalePaths<double, int64_t>(tmp, 1 / scale, error_code);
}
inline Paths64 MinkowskiDiff(const Path64& pattern, const Path64& path, bool isClosed)
{
return detail::Union(detail::Minkowski(pattern, path, false, isClosed), FillRule::NonZero);
}
inline PathsD MinkowskiDiff(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2)
{
int error_code = 0;
double scale = pow(10, decimalPlaces);
Path64 pat64 = ScalePath<int64_t, double>(pattern, scale, error_code);
Path64 path64 = ScalePath<int64_t, double>(path, scale, error_code);
Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, false, isClosed), FillRule::NonZero);
return ScalePaths<double, int64_t>(tmp, 1 / scale, error_code);
}
} // Clipper2Lib namespace
#endif // CLIPPER_MINKOWSKI_H

View File

@ -0,0 +1,114 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 March 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Path Offset (Inflate/Shrink) *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_OFFSET_H_
#define CLIPPER_OFFSET_H_
#include "clipper.core.h"
#include "clipper.engine.h"
namespace Clipper2Lib {
enum class JoinType { Square, Round, Miter };
enum class EndType {Polygon, Joined, Butt, Square, Round};
//Butt : offsets both sides of a path, with square blunt ends
//Square : offsets both sides of a path, with square extended ends
//Round : offsets both sides of a path, with round extended ends
//Joined : offsets both sides of a path, with joined ends
//Polygon: offsets only one side of a closed path
class ClipperOffset {
private:
class Group {
public:
Paths64 paths_in;
Paths64 paths_out;
Path64 path;
bool is_reversed = false;
JoinType join_type;
EndType end_type;
Group(const Paths64& _paths, JoinType _join_type, EndType _end_type) :
paths_in(_paths), join_type(_join_type), end_type(_end_type) {}
};
int error_code_ = 0;
double delta_ = 0.0;
double group_delta_ = 0.0;
double abs_group_delta_ = 0.0;
double temp_lim_ = 0.0;
double steps_per_rad_ = 0.0;
double step_sin_ = 0.0;
double step_cos_ = 0.0;
PathD norms;
Paths64 solution;
std::vector<Group> groups_;
JoinType join_type_ = JoinType::Square;
EndType end_type_ = EndType::Polygon;
double miter_limit_ = 0.0;
double arc_tolerance_ = 0.0;
bool preserve_collinear_ = false;
bool reverse_solution_ = false;
#ifdef USINGZ
ZCallback64 zCallback64_ = nullptr;
#endif
void DoSquare(Group& group, const Path64& path, size_t j, size_t k);
void DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a);
void DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle);
void BuildNormals(const Path64& path);
void OffsetPolygon(Group& group, Path64& path);
void OffsetOpenJoined(Group& group, Path64& path);
void OffsetOpenPath(Group& group, Path64& path);
void OffsetPoint(Group& group, Path64& path, size_t j, size_t& k);
void DoGroupOffset(Group &group);
void ExecuteInternal(double delta);
public:
explicit ClipperOffset(double miter_limit = 2.0,
double arc_tolerance = 0.0,
bool preserve_collinear = false,
bool reverse_solution = false) :
miter_limit_(miter_limit), arc_tolerance_(arc_tolerance),
preserve_collinear_(preserve_collinear),
reverse_solution_(reverse_solution) { };
~ClipperOffset() { Clear(); };
int ErrorCode() { return error_code_; };
void AddPath(const Path64& path, JoinType jt_, EndType et_);
void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
void Clear() { groups_.clear(); norms.clear(); };
void Execute(double delta, Paths64& paths);
void Execute(double delta, PolyTree64& polytree);
double MiterLimit() const { return miter_limit_; }
void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; }
//ArcTolerance: needed for rounded offsets (See offset_triginometry2.svg)
double ArcTolerance() const { return arc_tolerance_; }
void ArcTolerance(double arc_tolerance) { arc_tolerance_ = arc_tolerance; }
bool PreserveCollinear() const { return preserve_collinear_; }
void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;}
bool ReverseSolution() const { return reverse_solution_; }
void ReverseSolution(bool reverse_solution) {reverse_solution_ = reverse_solution;}
#ifdef USINGZ
void SetZCallback(ZCallback64 cb) { zCallback64_ = cb; }
#endif
};
}
#endif /* CLIPPER_OFFSET_H_ */

View File

@ -0,0 +1,82 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 9 February 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_RECTCLIP_H
#define CLIPPER_RECTCLIP_H
#include <cstdlib>
#include <vector>
#include <queue>
#include "clipper.h"
#include "clipper.core.h"
namespace Clipper2Lib
{
enum class Location { Left, Top, Right, Bottom, Inside };
class OutPt2;
typedef std::vector<OutPt2*> OutPt2List;
class OutPt2 {
public:
Point64 pt;
size_t owner_idx;
OutPt2List* edge;
OutPt2* next;
OutPt2* prev;
};
//------------------------------------------------------------------------------
// RectClip
//------------------------------------------------------------------------------
class RectClip {
private:
void ExecuteInternal(const Path64& path);
Path64 GetPath(OutPt2*& op);
protected:
const Rect64 rect_;
const Path64 rect_as_path_;
const Point64 rect_mp_;
Rect64 path_bounds_;
std::deque<OutPt2> op_container_;
OutPt2List results_; // each path can be broken into multiples
OutPt2List edges_[8]; // clockwise and counter-clockwise
std::vector<Location> start_locs_;
void CheckEdges();
void TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw);
void GetNextLocation(const Path64& path,
Location& loc, int& i, int highI);
OutPt2* Add(Point64 pt, bool start_new = false);
void AddCorner(Location prev, Location curr);
void AddCorner(Location& loc, bool isClockwise);
public:
explicit RectClip(const Rect64& rect) :
rect_(rect),
rect_as_path_(rect.AsPath()),
rect_mp_(rect.MidPoint()) {}
Paths64 Execute(const Paths64& paths, bool convex_only = false);
};
//------------------------------------------------------------------------------
// RectClipLines
//------------------------------------------------------------------------------
class RectClipLines : public RectClip {
private:
void ExecuteInternal(const Path64& path);
Path64 GetPath(OutPt2*& op);
public:
explicit RectClipLines(const Rect64& rect) : RectClip(rect) {};
Paths64 Execute(const Paths64& paths);
};
} // Clipper2Lib namespace
#endif // CLIPPER_RECTCLIP_H

2979
thirdparty/clipper2/src/clipper.engine.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,618 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 March 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Path Offset (Inflate/Shrink) *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#include <cmath>
#include "clipper2/clipper.h"
#include "clipper2/clipper.offset.h"
namespace Clipper2Lib {
const double default_arc_tolerance = 0.25;
const double floating_point_tolerance = 1e-12;
//------------------------------------------------------------------------------
// Miscellaneous methods
//------------------------------------------------------------------------------
void GetBoundsAndLowestPolyIdx(const Paths64& paths, Rect64& r, int & idx)
{
idx = -1;
r = MaxInvalidRect64;
int64_t lpx = 0;
for (int i = 0; i < static_cast<int>(paths.size()); ++i)
for (const Point64& p : paths[i])
{
if (p.y >= r.bottom)
{
if (p.y > r.bottom || p.x < lpx)
{
idx = i;
lpx = p.x;
r.bottom = p.y;
}
}
else if (p.y < r.top) r.top = p.y;
if (p.x > r.right) r.right = p.x;
else if (p.x < r.left) r.left = p.x;
}
//if (idx < 0) r = Rect64(0, 0, 0, 0);
//if (r.top == INT64_MIN) r.bottom = r.top;
//if (r.left == INT64_MIN) r.left = r.right;
}
bool IsSafeOffset(const Rect64& r, double abs_delta)
{
return r.left > min_coord + abs_delta &&
r.right < max_coord - abs_delta &&
r.top > min_coord + abs_delta &&
r.bottom < max_coord - abs_delta;
}
PointD GetUnitNormal(const Point64& pt1, const Point64& pt2)
{
double dx, dy, inverse_hypot;
if (pt1 == pt2) return PointD(0.0, 0.0);
dx = static_cast<double>(pt2.x - pt1.x);
dy = static_cast<double>(pt2.y - pt1.y);
inverse_hypot = 1.0 / hypot(dx, dy);
dx *= inverse_hypot;
dy *= inverse_hypot;
return PointD(dy, -dx);
}
inline bool AlmostZero(double value, double epsilon = 0.001)
{
return std::fabs(value) < epsilon;
}
inline double Hypot(double x, double y)
{
//see https://stackoverflow.com/a/32436148/359538
return std::sqrt(x * x + y * y);
}
inline PointD NormalizeVector(const PointD& vec)
{
double h = Hypot(vec.x, vec.y);
if (AlmostZero(h)) return PointD(0,0);
double inverseHypot = 1 / h;
return PointD(vec.x * inverseHypot, vec.y * inverseHypot);
}
inline PointD GetAvgUnitVector(const PointD& vec1, const PointD& vec2)
{
return NormalizeVector(PointD(vec1.x + vec2.x, vec1.y + vec2.y));
}
inline bool IsClosedPath(EndType et)
{
return et == EndType::Polygon || et == EndType::Joined;
}
inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta)
{
#ifdef USINGZ
return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z);
#else
return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta);
#endif
}
inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta)
{
#ifdef USINGZ
return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z);
#else
return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta);
#endif
}
inline void NegatePath(PathD& path)
{
for (PointD& pt : path)
{
pt.x = -pt.x;
pt.y = -pt.y;
#ifdef USINGZ
pt.z = pt.z;
#endif
}
}
//------------------------------------------------------------------------------
// ClipperOffset methods
//------------------------------------------------------------------------------
void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_)
{
Paths64 paths;
paths.push_back(path);
AddPaths(paths, jt_, et_);
}
void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_)
{
if (paths.size() == 0) return;
groups_.push_back(Group(paths, jt_, et_));
}
void ClipperOffset::BuildNormals(const Path64& path)
{
norms.clear();
norms.reserve(path.size());
if (path.size() == 0) return;
Path64::const_iterator path_iter, path_last_iter = --path.cend();
for (path_iter = path.cbegin(); path_iter != path_last_iter; ++path_iter)
norms.push_back(GetUnitNormal(*path_iter,*(path_iter +1)));
norms.push_back(GetUnitNormal(*path_last_iter, *(path.cbegin())));
}
inline PointD TranslatePoint(const PointD& pt, double dx, double dy)
{
#ifdef USINGZ
return PointD(pt.x + dx, pt.y + dy, pt.z);
#else
return PointD(pt.x + dx, pt.y + dy);
#endif
}
inline PointD ReflectPoint(const PointD& pt, const PointD& pivot)
{
#ifdef USINGZ
return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z);
#else
return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y));
#endif
}
PointD IntersectPoint(const PointD& pt1a, const PointD& pt1b,
const PointD& pt2a, const PointD& pt2b)
{
if (pt1a.x == pt1b.x) //vertical
{
if (pt2a.x == pt2b.x) return PointD(0, 0);
double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x);
double b2 = pt2a.y - m2 * pt2a.x;
return PointD(pt1a.x, m2 * pt1a.x + b2);
}
else if (pt2a.x == pt2b.x) //vertical
{
double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x);
double b1 = pt1a.y - m1 * pt1a.x;
return PointD(pt2a.x, m1 * pt2a.x + b1);
}
else
{
double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x);
double b1 = pt1a.y - m1 * pt1a.x;
double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x);
double b2 = pt2a.y - m2 * pt2a.x;
if (m1 == m2) return PointD(0, 0);
double x = (b2 - b1) / (m1 - m2);
return PointD(x, m1 * x + b1);
}
}
void ClipperOffset::DoSquare(Group& group, const Path64& path, size_t j, size_t k)
{
PointD vec;
if (j == k)
vec = PointD(norms[0].y, -norms[0].x);
else
vec = GetAvgUnitVector(
PointD(-norms[k].y, norms[k].x),
PointD(norms[j].y, -norms[j].x));
// now offset the original vertex delta units along unit vector
PointD ptQ = PointD(path[j]);
ptQ = TranslatePoint(ptQ, abs_group_delta_ * vec.x, abs_group_delta_ * vec.y);
// get perpendicular vertices
PointD pt1 = TranslatePoint(ptQ, group_delta_ * vec.y, group_delta_ * -vec.x);
PointD pt2 = TranslatePoint(ptQ, group_delta_ * -vec.y, group_delta_ * vec.x);
// get 2 vertices along one edge offset
PointD pt3 = GetPerpendicD(path[k], norms[k], group_delta_);
if (j == k)
{
PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_);
PointD pt = IntersectPoint(pt1, pt2, pt3, pt4);
#ifdef USINGZ
pt.z = ptQ.z;
#endif
//get the second intersect point through reflecion
group.path.push_back(Point64(ReflectPoint(pt, ptQ)));
group.path.push_back(Point64(pt));
}
else
{
PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
PointD pt = IntersectPoint(pt1, pt2, pt3, pt4);
#ifdef USINGZ
pt.z = ptQ.z;
#endif
group.path.push_back(Point64(pt));
//get the second intersect point through reflecion
group.path.push_back(Point64(ReflectPoint(pt, ptQ)));
}
}
void ClipperOffset::DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a)
{
double q = group_delta_ / (cos_a + 1);
#ifdef USINGZ
group.path.push_back(Point64(
path[j].x + (norms[k].x + norms[j].x) * q,
path[j].y + (norms[k].y + norms[j].y) * q,
path[j].z));
#else
group.path.push_back(Point64(
path[j].x + (norms[k].x + norms[j].x) * q,
path[j].y + (norms[k].y + norms[j].y) * q));
#endif
}
void ClipperOffset::DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle)
{
Point64 pt = path[j];
PointD offsetVec = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_);
if (j == k) offsetVec.Negate();
#ifdef USINGZ
group.path.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z));
#else
group.path.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y));
#endif
if (angle > -PI + 0.01) // avoid 180deg concave
{
int steps = static_cast<int>(std::ceil(steps_per_rad_ * std::abs(angle))); // #448, #456
for (int i = 1; i < steps; ++i) // ie 1 less than steps
{
offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y,
offsetVec.x * step_sin_ + offsetVec.y * step_cos_);
#ifdef USINGZ
group.path.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z));
#else
group.path.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y));
#endif
}
}
group.path.push_back(GetPerpendic(path[j], norms[j], group_delta_));
}
void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k)
{
// Let A = change in angle where edges join
// A == 0: ie no change in angle (flat join)
// A == PI: edges 'spike'
// sin(A) < 0: right turning
// cos(A) < 0: change in angle is more than 90 degree
if (path[j] == path[k]) { k = j; return; }
double sin_a = CrossProduct(norms[j], norms[k]);
double cos_a = DotProduct(norms[j], norms[k]);
if (sin_a > 1.0) sin_a = 1.0;
else if (sin_a < -1.0) sin_a = -1.0;
if (cos_a > 0.99) // almost straight - less than 8 degrees
{
group.path.push_back(GetPerpendic(path[j], norms[k], group_delta_));
if (cos_a < 0.9998) // greater than 1 degree (#424)
group.path.push_back(GetPerpendic(path[j], norms[j], group_delta_)); // (#418)
}
else if (cos_a > -0.99 && (sin_a * group_delta_ < 0))
{
// is concave
group.path.push_back(GetPerpendic(path[j], norms[k], group_delta_));
// this extra point is the only (simple) way to ensure that
// path reversals are fully cleaned with the trailing clipper
group.path.push_back(path[j]); // (#405)
group.path.push_back(GetPerpendic(path[j], norms[j], group_delta_));
}
else if (join_type_ == JoinType::Round)
DoRound(group, path, j, k, std::atan2(sin_a, cos_a));
else if (join_type_ == JoinType::Miter)
{
// miter unless the angle is so acute the miter would exceeds ML
if (cos_a > temp_lim_ - 1) DoMiter(group, path, j, k, cos_a);
else DoSquare(group, path, j, k);
}
// don't bother squaring angles that deviate < ~20 degrees because
// squaring will be indistinguishable from mitering and just be a lot slower
else if (cos_a > 0.9)
DoMiter(group, path, j, k, cos_a);
else
DoSquare(group, path, j, k);
k = j;
}
void ClipperOffset::OffsetPolygon(Group& group, Path64& path)
{
for (Path64::size_type i = 0, j = path.size() -1; i < path.size(); j = i, ++i)
OffsetPoint(group, path, i, j);
group.paths_out.push_back(group.path);
}
void ClipperOffset::OffsetOpenJoined(Group& group, Path64& path)
{
OffsetPolygon(group, path);
std::reverse(path.begin(), path.end());
//rebuild normals // BuildNormals(path);
std::reverse(norms.begin(), norms.end());
norms.push_back(norms[0]);
norms.erase(norms.begin());
NegatePath(norms);
group.path.clear();
OffsetPolygon(group, path);
}
void ClipperOffset::OffsetOpenPath(Group& group, Path64& path)
{
// do the line start cap
switch (end_type_)
{
case EndType::Butt:
#ifdef USINGZ
group.path.push_back(Point64(
path[0].x - norms[0].x * group_delta_,
path[0].y - norms[0].y * group_delta_,
path[0].z));
#else
group.path.push_back(Point64(
path[0].x - norms[0].x * group_delta_,
path[0].y - norms[0].y * group_delta_));
#endif
group.path.push_back(GetPerpendic(path[0], norms[0], group_delta_));
break;
case EndType::Round:
DoRound(group, path, 0,0, PI);
break;
default:
DoSquare(group, path, 0, 0);
break;
}
size_t highI = path.size() - 1;
// offset the left side going forward
for (Path64::size_type i = 1, k = 0; i < highI; ++i)
OffsetPoint(group, path, i, k);
// reverse normals
for (size_t i = highI; i > 0; --i)
norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y);
norms[0] = norms[highI];
// do the line end cap
switch (end_type_)
{
case EndType::Butt:
#ifdef USINGZ
group.path.push_back(Point64(
path[highI].x - norms[highI].x * group_delta_,
path[highI].y - norms[highI].y * group_delta_,
path[highI].z));
#else
group.path.push_back(Point64(
path[highI].x - norms[highI].x * group_delta_,
path[highI].y - norms[highI].y * group_delta_));
#endif
group.path.push_back(GetPerpendic(path[highI], norms[highI], group_delta_));
break;
case EndType::Round:
DoRound(group, path, highI, highI, PI);
break;
default:
DoSquare(group, path, highI, highI);
break;
}
for (size_t i = highI, k = 0; i > 0; --i)
OffsetPoint(group, path, i, k);
group.paths_out.push_back(group.path);
}
void ClipperOffset::DoGroupOffset(Group& group)
{
Rect64 r;
int idx = -1;
//the lowermost polygon must be an outer polygon. So we can use that as the
//designated orientation for outer polygons (needed for tidy-up clipping)
GetBoundsAndLowestPolyIdx(group.paths_in, r, idx);
if (idx < 0) return;
if (group.end_type == EndType::Polygon)
{
double area = Area(group.paths_in[idx]);
//if (area == 0) return; // probably unhelpful (#430)
group.is_reversed = (area < 0);
if (group.is_reversed) group_delta_ = -delta_;
else group_delta_ = delta_;
}
else
{
group.is_reversed = false;
group_delta_ = std::abs(delta_) * 0.5;
}
abs_group_delta_ = std::fabs(group_delta_);
// do range checking
if (!IsSafeOffset(r, abs_group_delta_))
{
DoError(range_error_i);
error_code_ |= range_error_i;
return;
}
join_type_ = group.join_type;
end_type_ = group.end_type;
//calculate a sensible number of steps (for 360 deg for the given offset
if (group.join_type == JoinType::Round || group.end_type == EndType::Round)
{
// arcTol - when arc_tolerance_ is undefined (0), the amount of
// curve imprecision that's allowed is based on the size of the
// offset (delta). Obviously very large offsets will almost always
// require much less precision. See also offset_triginometry2.svg
double arcTol = (arc_tolerance_ > floating_point_tolerance ?
std::min(abs_group_delta_, arc_tolerance_) :
std::log10(2 + abs_group_delta_) * default_arc_tolerance);
double steps_per_360 = PI / std::acos(1 - arcTol / abs_group_delta_);
if (steps_per_360 > abs_group_delta_ * PI)
steps_per_360 = abs_group_delta_ * PI; //ie avoids excessive precision
step_sin_ = std::sin(2 * PI / steps_per_360);
step_cos_ = std::cos(2 * PI / steps_per_360);
if (group_delta_ < 0.0) step_sin_ = -step_sin_;
steps_per_rad_ = steps_per_360 / (2 *PI);
}
bool is_joined =
(end_type_ == EndType::Polygon) ||
(end_type_ == EndType::Joined);
Paths64::const_iterator path_iter;
for(path_iter = group.paths_in.cbegin(); path_iter != group.paths_in.cend(); ++path_iter)
{
Path64 path = StripDuplicates(*path_iter, is_joined);
Path64::size_type cnt = path.size();
if (cnt == 0 || ((cnt < 3) && group.end_type == EndType::Polygon))
continue;
group.path.clear();
if (cnt == 1) // single point - only valid with open paths
{
if (group_delta_ < 1) continue;
//single vertex so build a circle or square ...
if (group.join_type == JoinType::Round)
{
double radius = abs_group_delta_;
group.path = Ellipse(path[0], radius, radius);
#ifdef USINGZ
for (auto& p : group.path) p.z = path[0].z;
#endif
}
else
{
int d = (int)std::ceil(abs_group_delta_);
r = Rect64(path[0].x - d, path[0].y - d, path[0].x + d, path[0].y + d);
group.path = r.AsPath();
#ifdef USINGZ
for (auto& p : group.path) p.z = path[0].z;
#endif
}
group.paths_out.push_back(group.path);
}
else
{
if ((cnt == 2) && (group.end_type == EndType::Joined))
{
if (group.join_type == JoinType::Round)
end_type_ = EndType::Round;
else
end_type_ = EndType::Square;
}
BuildNormals(path);
if (end_type_ == EndType::Polygon) OffsetPolygon(group, path);
else if (end_type_ == EndType::Joined) OffsetOpenJoined(group, path);
else OffsetOpenPath(group, path);
}
}
solution.reserve(solution.size() + group.paths_out.size());
copy(group.paths_out.begin(), group.paths_out.end(), back_inserter(solution));
group.paths_out.clear();
}
void ClipperOffset::ExecuteInternal(double delta)
{
error_code_ = 0;
solution.clear();
if (groups_.size() == 0) return;
if (std::abs(delta) < 0.5)
{
for (const Group& group : groups_)
{
solution.reserve(solution.size() + group.paths_in.size());
copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(solution));
}
}
else
{
temp_lim_ = (miter_limit_ <= 1) ?
2.0 :
2.0 / (miter_limit_ * miter_limit_);
delta_ = delta;
std::vector<Group>::iterator git;
for (git = groups_.begin(); git != groups_.end(); ++git)
{
DoGroupOffset(*git);
if (!error_code_) continue; // all OK
solution.clear();
}
}
}
void ClipperOffset::Execute(double delta, Paths64& paths)
{
paths.clear();
ExecuteInternal(delta);
if (!solution.size()) return;
paths = solution;
//clean up self-intersections ...
Clipper64 c;
c.PreserveCollinear = false;
//the solution should retain the orientation of the input
c.ReverseSolution = reverse_solution_ != groups_[0].is_reversed;
#ifdef USINGZ
if (zCallback64_) {
c.SetZCallback(zCallback64_);
}
#endif
c.AddSubject(solution);
if (groups_[0].is_reversed)
c.Execute(ClipType::Union, FillRule::Negative, paths);
else
c.Execute(ClipType::Union, FillRule::Positive, paths);
}
void ClipperOffset::Execute(double delta, PolyTree64& polytree)
{
polytree.Clear();
ExecuteInternal(delta);
if (!solution.size()) return;
//clean up self-intersections ...
Clipper64 c;
c.PreserveCollinear = false;
//the solution should retain the orientation of the input
c.ReverseSolution = reverse_solution_ != groups_[0].is_reversed;
#ifdef USINGZ
if (zCallback64_) {
c.SetZCallback(zCallback64_);
}
#endif
c.AddSubject(solution);
if (groups_[0].is_reversed)
c.Execute(ClipType::Union, FillRule::Negative, polytree);
else
c.Execute(ClipType::Union, FillRule::Positive, polytree);
}
} // namespace

View File

@ -0,0 +1,976 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 14 February 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#include <cmath>
#include "clipper2/clipper.h"
#include "clipper2/clipper.rectclip.h"
namespace Clipper2Lib {
//------------------------------------------------------------------------------
// Miscellaneous methods
//------------------------------------------------------------------------------
inline bool Path1ContainsPath2(const Path64& path1, const Path64& path2)
{
int io_count = 0;
// precondition: no (significant) overlap
for (const Point64& pt : path2)
{
PointInPolygonResult pip = PointInPolygon(pt, path1);
switch (pip)
{
case PointInPolygonResult::IsOutside: ++io_count; break;
case PointInPolygonResult::IsInside: --io_count; break;
default: continue;
}
if (std::abs(io_count) > 1) break;
}
return io_count <= 0;
}
inline bool GetLocation(const Rect64& rec,
const Point64& pt, Location& loc)
{
if (pt.x == rec.left && pt.y >= rec.top && pt.y <= rec.bottom)
{
loc = Location::Left;
return false;
}
else if (pt.x == rec.right && pt.y >= rec.top && pt.y <= rec.bottom)
{
loc = Location::Right;
return false;
}
else if (pt.y == rec.top && pt.x >= rec.left && pt.x <= rec.right)
{
loc = Location::Top;
return false;
}
else if (pt.y == rec.bottom && pt.x >= rec.left && pt.x <= rec.right)
{
loc = Location::Bottom;
return false;
}
else if (pt.x < rec.left) loc = Location::Left;
else if (pt.x > rec.right) loc = Location::Right;
else if (pt.y < rec.top) loc = Location::Top;
else if (pt.y > rec.bottom) loc = Location::Bottom;
else loc = Location::Inside;
return true;
}
inline bool GetIntersection(const Path64& rectPath,
const Point64& p, const Point64& p2, Location& loc, Point64& ip)
{
// gets the intersection closest to 'p'
// when Result = false, loc will remain unchanged
switch (loc)
{
case Location::Left:
if (SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true))
GetIntersectPoint(p, p2, rectPath[0], rectPath[3], ip);
else if (p.y < rectPath[0].y &&
SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true))
{
GetIntersectPoint(p, p2, rectPath[0], rectPath[1], ip);
loc = Location::Top;
}
else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true))
{
GetIntersectPoint(p, p2, rectPath[2], rectPath[3], ip);
loc = Location::Bottom;
}
else return false;
break;
case Location::Top:
if (SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true))
GetIntersectPoint(p, p2, rectPath[0], rectPath[1], ip);
else if (p.x < rectPath[0].x &&
SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true))
{
GetIntersectPoint(p, p2, rectPath[0], rectPath[3], ip);
loc = Location::Left;
}
else if (p.x > rectPath[1].x &&
SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true))
{
GetIntersectPoint(p, p2, rectPath[1], rectPath[2], ip);
loc = Location::Right;
}
else return false;
break;
case Location::Right:
if (SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true))
GetIntersectPoint(p, p2, rectPath[1], rectPath[2], ip);
else if (p.y < rectPath[0].y &&
SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true))
{
GetIntersectPoint(p, p2, rectPath[0], rectPath[1], ip);
loc = Location::Top;
}
else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true))
{
GetIntersectPoint(p, p2, rectPath[2], rectPath[3], ip);
loc = Location::Bottom;
}
else return false;
break;
case Location::Bottom:
if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true))
GetIntersectPoint(p, p2, rectPath[2], rectPath[3], ip);
else if (p.x < rectPath[3].x &&
SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true))
{
GetIntersectPoint(p, p2, rectPath[0], rectPath[3], ip);
loc = Location::Left;
}
else if (p.x > rectPath[2].x &&
SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true))
{
GetIntersectPoint(p, p2, rectPath[1], rectPath[2], ip);
loc = Location::Right;
}
else return false;
break;
default: // loc == rInside
if (SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true))
{
GetIntersectPoint(p, p2, rectPath[0], rectPath[3], ip);
loc = Location::Left;
}
else if (SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true))
{
GetIntersectPoint(p, p2, rectPath[0], rectPath[1], ip);
loc = Location::Top;
}
else if (SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true))
{
GetIntersectPoint(p, p2, rectPath[1], rectPath[2], ip);
loc = Location::Right;
}
else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true))
{
GetIntersectPoint(p, p2, rectPath[2], rectPath[3], ip);
loc = Location::Bottom;
}
else return false;
break;
}
return true;
}
inline Location GetAdjacentLocation(Location loc, bool isClockwise)
{
int delta = (isClockwise) ? 1 : 3;
return static_cast<Location>((static_cast<int>(loc) + delta) % 4);
}
inline bool HeadingClockwise(Location prev, Location curr)
{
return (static_cast<int>(prev) + 1) % 4 == static_cast<int>(curr);
}
inline bool AreOpposites(Location prev, Location curr)
{
return abs(static_cast<int>(prev) - static_cast<int>(curr)) == 2;
}
inline bool IsClockwise(Location prev, Location curr,
const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp)
{
if (AreOpposites(prev, curr))
return CrossProduct(prev_pt, rect_mp, curr_pt) < 0;
else
return HeadingClockwise(prev, curr);
}
inline OutPt2* UnlinkOp(OutPt2* op)
{
if (op->next == op) return nullptr;
op->prev->next = op->next;
op->next->prev = op->prev;
return op->next;
}
inline OutPt2* UnlinkOpBack(OutPt2* op)
{
if (op->next == op) return nullptr;
op->prev->next = op->next;
op->next->prev = op->prev;
return op->prev;
}
inline uint32_t GetEdgesForPt(const Point64& pt, const Rect64& rec)
{
uint32_t result = 0;
if (pt.x == rec.left) result = 1;
else if (pt.x == rec.right) result = 4;
if (pt.y == rec.top) result += 2;
else if (pt.y == rec.bottom) result += 8;
return result;
}
inline bool IsHeadingClockwise(const Point64& pt1, const Point64& pt2, int edgeIdx)
{
switch (edgeIdx)
{
case 0: return pt2.y < pt1.y;
case 1: return pt2.x > pt1.x;
case 2: return pt2.y > pt1.y;
default: return pt2.x < pt1.x;
}
}
inline bool HasHorzOverlap(const Point64& left1, const Point64& right1,
const Point64& left2, const Point64& right2)
{
return (left1.x < right2.x) && (right1.x > left2.x);
}
inline bool HasVertOverlap(const Point64& top1, const Point64& bottom1,
const Point64& top2, const Point64& bottom2)
{
return (top1.y < bottom2.y) && (bottom1.y > top2.y);
}
inline void AddToEdge(OutPt2List& edge, OutPt2* op)
{
if (op->edge) return;
op->edge = &edge;
edge.push_back(op);
}
inline void UncoupleEdge(OutPt2* op)
{
if (!op->edge) return;
for (size_t i = 0; i < op->edge->size(); ++i)
{
OutPt2* op2 = (*op->edge)[i];
if (op2 == op)
{
(*op->edge)[i] = nullptr;
break;
}
}
op->edge = nullptr;
}
inline void SetNewOwner(OutPt2* op, size_t new_idx)
{
op->owner_idx = new_idx;
OutPt2* op2 = op->next;
while (op2 != op)
{
op2->owner_idx = new_idx;
op2 = op2->next;
}
}
//----------------------------------------------------------------------------
// RectClip64
//----------------------------------------------------------------------------
OutPt2* RectClip::Add(Point64 pt, bool start_new)
{
// this method is only called by InternalExecute.
// Later splitting & rejoining won't create additional op's,
// though they will change the (non-storage) results_ count.
int curr_idx = static_cast<int>(results_.size()) - 1;
OutPt2* result;
if (curr_idx < 0 || start_new)
{
result = &op_container_.emplace_back(OutPt2());
result->pt = pt;
result->next = result;
result->prev = result;
results_.push_back(result);
}
else
{
OutPt2* prevOp = results_[curr_idx];
if (prevOp->pt == pt) return prevOp;
result = &op_container_.emplace_back(OutPt2());
result->owner_idx = curr_idx;
result->pt = pt;
result->next = prevOp->next;
prevOp->next->prev = result;
prevOp->next = result;
result->prev = prevOp;
results_[curr_idx] = result;
}
return result;
}
void RectClip::AddCorner(Location prev, Location curr)
{
if (HeadingClockwise(prev, curr))
Add(rect_as_path_[static_cast<int>(prev)]);
else
Add(rect_as_path_[static_cast<int>(curr)]);
}
void RectClip::AddCorner(Location& loc, bool isClockwise)
{
if (isClockwise)
{
Add(rect_as_path_[static_cast<int>(loc)]);
loc = GetAdjacentLocation(loc, true);
}
else
{
loc = GetAdjacentLocation(loc, false);
Add(rect_as_path_[static_cast<int>(loc)]);
}
}
void RectClip::GetNextLocation(const Path64& path,
Location& loc, int& i, int highI)
{
switch (loc)
{
case Location::Left:
while (i <= highI && path[i].x <= rect_.left) ++i;
if (i > highI) break;
else if (path[i].x >= rect_.right) loc = Location::Right;
else if (path[i].y <= rect_.top) loc = Location::Top;
else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
else loc = Location::Inside;
break;
case Location::Top:
while (i <= highI && path[i].y <= rect_.top) ++i;
if (i > highI) break;
else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
else if (path[i].x <= rect_.left) loc = Location::Left;
else if (path[i].x >= rect_.right) loc = Location::Right;
else loc = Location::Inside;
break;
case Location::Right:
while (i <= highI && path[i].x >= rect_.right) ++i;
if (i > highI) break;
else if (path[i].x <= rect_.left) loc = Location::Left;
else if (path[i].y <= rect_.top) loc = Location::Top;
else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
else loc = Location::Inside;
break;
case Location::Bottom:
while (i <= highI && path[i].y >= rect_.bottom) ++i;
if (i > highI) break;
else if (path[i].y <= rect_.top) loc = Location::Top;
else if (path[i].x <= rect_.left) loc = Location::Left;
else if (path[i].x >= rect_.right) loc = Location::Right;
else loc = Location::Inside;
break;
case Location::Inside:
while (i <= highI)
{
if (path[i].x < rect_.left) loc = Location::Left;
else if (path[i].x > rect_.right) loc = Location::Right;
else if (path[i].y > rect_.bottom) loc = Location::Bottom;
else if (path[i].y < rect_.top) loc = Location::Top;
else { Add(path[i]); ++i; continue; }
break; //inner loop
}
break;
} //switch
}
void RectClip::ExecuteInternal(const Path64& path)
{
int i = 0, highI = static_cast<int>(path.size()) - 1;
Location prev = Location::Inside, loc;
Location crossing_loc = Location::Inside;
Location first_cross_ = Location::Inside;
if (!GetLocation(rect_, path[highI], loc))
{
i = highI - 1;
while (i >= 0 && !GetLocation(rect_, path[i], prev)) --i;
if (i < 0)
{
// all of path must be inside fRect
for (const auto& pt : path) Add(pt);
return;
}
if (prev == Location::Inside) loc = Location::Inside;
i = 0;
}
Location startingLoc = loc;
///////////////////////////////////////////////////
while (i <= highI)
{
prev = loc;
Location crossing_prev = crossing_loc;
GetNextLocation(path, loc, i, highI);
if (i > highI) break;
Point64 ip, ip2;
Point64 prev_pt = (i) ?
path[static_cast<size_t>(i - 1)] :
path[highI];
crossing_loc = loc;
if (!GetIntersection(rect_as_path_,
path[i], prev_pt, crossing_loc, ip))
{
// ie remaining outside
if (crossing_prev == Location::Inside)
{
bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);
do {
start_locs_.push_back(prev);
prev = GetAdjacentLocation(prev, isClockw);
} while (prev != loc);
crossing_loc = crossing_prev; // still not crossed
}
else if (prev != Location::Inside && prev != loc)
{
bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);
do {
AddCorner(prev, isClockw);
} while (prev != loc);
}
++i;
continue;
}
////////////////////////////////////////////////////
// we must be crossing the rect boundary to get here
////////////////////////////////////////////////////
if (loc == Location::Inside) // path must be entering rect
{
if (first_cross_ == Location::Inside)
{
first_cross_ = crossing_loc;
start_locs_.push_back(prev);
}
else if (prev != crossing_loc)
{
bool isClockw = IsClockwise(prev, crossing_loc, prev_pt, path[i], rect_mp_);
do {
AddCorner(prev, isClockw);
} while (prev != crossing_loc);
}
}
else if (prev != Location::Inside)
{
// passing right through rect. 'ip' here will be the second
// intersect pt but we'll also need the first intersect pt (ip2)
loc = prev;
GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2);
if (crossing_prev != Location::Inside)
AddCorner(crossing_prev, loc);
if (first_cross_ == Location::Inside)
{
first_cross_ = loc;
start_locs_.push_back(prev);
}
loc = crossing_loc;
Add(ip2);
if (ip == ip2)
{
// it's very likely that path[i] is on rect
GetLocation(rect_, path[i], loc);
AddCorner(crossing_loc, loc);
crossing_loc = loc;
continue;
}
}
else // path must be exiting rect
{
loc = crossing_loc;
if (first_cross_ == Location::Inside)
first_cross_ = crossing_loc;
}
Add(ip);
} //while i <= highI
///////////////////////////////////////////////////
if (first_cross_ == Location::Inside)
{
// path never intersects
if (startingLoc != Location::Inside)
{
// path is outside rect
// but being outside, it still may not contain rect
if (path_bounds_.Contains(rect_) &&
Path1ContainsPath2(path, rect_as_path_))
{
// yep, the path does fully contain rect
// so add rect to the solution
for (size_t j = 0; j < 4; ++j)
{
Add(rect_as_path_[j]);
// we may well need to do some splitting later, so
AddToEdge(edges_[j * 2], results_[0]);
}
}
}
}
else if (loc != Location::Inside &&
(loc != first_cross_ || start_locs_.size() > 2))
{
if (start_locs_.size() > 0)
{
prev = loc;
for (auto loc2 : start_locs_)
{
if (prev == loc2) continue;
AddCorner(prev, HeadingClockwise(prev, loc2));
prev = loc2;
}
loc = prev;
}
if (loc != first_cross_)
AddCorner(loc, HeadingClockwise(loc, first_cross_));
}
}
void RectClip::CheckEdges()
{
for (size_t i = 0; i < results_.size(); ++i)
{
OutPt2* op = results_[i];
if (!op) continue;
OutPt2* op2 = op;
do
{
if (!CrossProduct(op2->prev->pt,
op2->pt, op2->next->pt))
{
if (op2 == op)
{
op2 = UnlinkOpBack(op2);
if (!op2) break;
op = op2->prev;
}
else
{
op2 = UnlinkOpBack(op2);
if (!op2) break;
}
}
else
op2 = op2->next;
} while (op2 != op);
if (!op2)
{
results_[i] = nullptr;
continue;
}
results_[i] = op; // safety first
uint32_t edgeSet1 = GetEdgesForPt(op->prev->pt, rect_);
op2 = op;
do
{
uint32_t edgeSet2 = GetEdgesForPt(op2->pt, rect_);
if (edgeSet2 && !op2->edge)
{
uint32_t combinedSet = (edgeSet1 & edgeSet2);
for (int j = 0; j < 4; ++j)
{
if (combinedSet & (1 << j))
{
if (IsHeadingClockwise(op2->prev->pt, op2->pt, j))
AddToEdge(edges_[j * 2], op2);
else
AddToEdge(edges_[j * 2 + 1], op2);
}
}
}
edgeSet1 = edgeSet2;
op2 = op2->next;
} while (op2 != op);
}
}
void RectClip::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw)
{
if (ccw.empty()) return;
bool isHorz = ((idx == 1) || (idx == 3));
bool cwIsTowardLarger = ((idx == 1) || (idx == 2));
size_t i = 0, j = 0;
OutPt2* p1, * p2, * p1a, * p2a, * op, * op2;
while (i < cw.size())
{
p1 = cw[i];
if (!p1 || p1->next == p1->prev)
{
cw[i++]->edge = nullptr;
j = 0;
continue;
}
size_t jLim = ccw.size();
while (j < jLim &&
(!ccw[j] || ccw[j]->next == ccw[j]->prev)) ++j;
if (j == jLim)
{
++i;
j = 0;
continue;
}
if (cwIsTowardLarger)
{
// p1 >>>> p1a;
// p2 <<<< p2a;
p1 = cw[i]->prev;
p1a = cw[i];
p2 = ccw[j];
p2a = ccw[j]->prev;
}
else
{
// p1 <<<< p1a;
// p2 >>>> p2a;
p1 = cw[i];
p1a = cw[i]->prev;
p2 = ccw[j]->prev;
p2a = ccw[j];
}
if ((isHorz && !HasHorzOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)) ||
(!isHorz && !HasVertOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)))
{
++j;
continue;
}
// to get here we're either splitting or rejoining
bool isRejoining = cw[i]->owner_idx != ccw[j]->owner_idx;
if (isRejoining)
{
results_[p2->owner_idx] = nullptr;
SetNewOwner(p2, p1->owner_idx);
}
// do the split or re-join
if (cwIsTowardLarger)
{
// p1 >> | >> p1a;
// p2 << | << p2a;
p1->next = p2;
p2->prev = p1;
p1a->prev = p2a;
p2a->next = p1a;
}
else
{
// p1 << | << p1a;
// p2 >> | >> p2a;
p1->prev = p2;
p2->next = p1;
p1a->next = p2a;
p2a->prev = p1a;
}
if (!isRejoining)
{
size_t new_idx = results_.size();
results_.push_back(p1a);
SetNewOwner(p1a, new_idx);
}
if (cwIsTowardLarger)
{
op = p2;
op2 = p1a;
}
else
{
op = p1;
op2 = p2a;
}
results_[op->owner_idx] = op;
results_[op2->owner_idx] = op2;
// and now lots of work to get ready for the next loop
bool opIsLarger, op2IsLarger;
if (isHorz) // X
{
opIsLarger = op->pt.x > op->prev->pt.x;
op2IsLarger = op2->pt.x > op2->prev->pt.x;
}
else // Y
{
opIsLarger = op->pt.y > op->prev->pt.y;
op2IsLarger = op2->pt.y > op2->prev->pt.y;
}
if ((op->next == op->prev) ||
(op->pt == op->prev->pt))
{
if (op2IsLarger == cwIsTowardLarger)
{
cw[i] = op2;
ccw[j++] = nullptr;
}
else
{
ccw[j] = op2;
cw[i++] = nullptr;
}
}
else if ((op2->next == op2->prev) ||
(op2->pt == op2->prev->pt))
{
if (opIsLarger == cwIsTowardLarger)
{
cw[i] = op;
ccw[j++] = nullptr;
}
else
{
ccw[j] = op;
cw[i++] = nullptr;
}
}
else if (opIsLarger == op2IsLarger)
{
if (opIsLarger == cwIsTowardLarger)
{
cw[i] = op;
UncoupleEdge(op2);
AddToEdge(cw, op2);
ccw[j++] = nullptr;
}
else
{
cw[i++] = nullptr;
ccw[j] = op2;
UncoupleEdge(op);
AddToEdge(ccw, op);
j = 0;
}
}
else
{
if (opIsLarger == cwIsTowardLarger)
cw[i] = op;
else
ccw[j] = op;
if (op2IsLarger == cwIsTowardLarger)
cw[i] = op2;
else
ccw[j] = op2;
}
}
}
Path64 RectClip::GetPath(OutPt2*& op)
{
if (!op || op->next == op->prev) return Path64();
OutPt2* op2 = op->next;
while (op2 && op2 != op)
{
if (CrossProduct(op2->prev->pt,
op2->pt, op2->next->pt) == 0)
{
op = op2->prev;
op2 = UnlinkOp(op2);
}
else
op2 = op2->next;
}
op = op2; // needed for op cleanup
if (!op2) return Path64();
Path64 result;
result.push_back(op->pt);
op2 = op->next;
while (op2 != op)
{
result.push_back(op2->pt);
op2 = op2->next;
}
return result;
}
Paths64 RectClip::Execute(const Paths64& paths, bool convex_only)
{
Paths64 result;
if (rect_.IsEmpty()) return result;
for (const auto& path : paths)
{
if (path.size() < 3) continue;
path_bounds_ = GetBounds(path);
if (!rect_.Intersects(path_bounds_))
continue; // the path must be completely outside rect_
else if (rect_.Contains(path_bounds_))
{
// the path must be completely inside rect_
result.push_back(path);
continue;
}
ExecuteInternal(path);
if (!convex_only)
{
CheckEdges();
for (int i = 0; i < 4; ++i)
TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);
}
for (OutPt2*& op : results_)
{
Path64 tmp = GetPath(op);
if (!tmp.empty())
result.emplace_back(tmp);
}
//clean up after every loop
op_container_ = std::deque<OutPt2>();
results_.clear();
for (OutPt2List edge : edges_) edge.clear();
start_locs_.clear();
}
return result;
}
//------------------------------------------------------------------------------
// RectClipLines
//------------------------------------------------------------------------------
Paths64 RectClipLines::Execute(const Paths64& paths)
{
Paths64 result;
if (rect_.IsEmpty()) return result;
for (const auto& path : paths)
{
if (path.size() < 2) continue;
Rect64 pathrec = GetBounds(path);
if (!rect_.Intersects(pathrec)) continue;
ExecuteInternal(path);
for (OutPt2*& op : results_)
{
Path64 tmp = GetPath(op);
if (!tmp.empty())
result.emplace_back(tmp);
}
results_.clear();
op_container_ = std::deque<OutPt2>();
start_locs_.clear();
}
return result;
}
void RectClipLines::ExecuteInternal(const Path64& path)
{
if (rect_.IsEmpty() || path.size() < 2) return;
results_.clear();
op_container_ = std::deque<OutPt2>();
start_locs_.clear();
int i = 1, highI = static_cast<int>(path.size()) - 1;
Location prev = Location::Inside, loc;
Location crossing_loc;
if (!GetLocation(rect_, path[0], loc))
{
while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i;
if (i > highI)
{
// all of path must be inside fRect
for (const auto& pt : path) Add(pt);
return;
}
if (prev == Location::Inside) loc = Location::Inside;
i = 1;
}
if (loc == Location::Inside) Add(path[0]);
///////////////////////////////////////////////////
while (i <= highI)
{
prev = loc;
GetNextLocation(path, loc, i, highI);
if (i > highI) break;
Point64 ip, ip2;
Point64 prev_pt = path[static_cast<size_t>(i - 1)];
crossing_loc = loc;
if (!GetIntersection(rect_as_path_,
path[i], prev_pt, crossing_loc, ip))
{
// ie remaining outside
++i;
continue;
}
////////////////////////////////////////////////////
// we must be crossing the rect boundary to get here
////////////////////////////////////////////////////
if (loc == Location::Inside) // path must be entering rect
{
Add(ip, true);
}
else if (prev != Location::Inside)
{
// passing right through rect. 'ip' here will be the second
// intersect pt but we'll also need the first intersect pt (ip2)
crossing_loc = prev;
GetIntersection(rect_as_path_,
prev_pt, path[i], crossing_loc, ip2);
Add(ip2, true);
Add(ip);
}
else // path must be exiting rect
{
Add(ip);
}
} //while i <= highI
///////////////////////////////////////////////////
}
Path64 RectClipLines::GetPath(OutPt2*& op)
{
Path64 result;
if (!op || op == op->next) return result;
op = op->next; // starting at path beginning
result.push_back(op->pt);
OutPt2 *op2 = op->next;
while (op2 != op)
{
result.push_back(op2->pt);
op2 = op2->next;
}
return result;
}
} // namespace