Created
November 1, 2025 22:24
-
-
Save CodeZombie/efa6c0b37b2e5a6c7cc947540b472fbd to your computer and use it in GitHub Desktop.
PolyTools
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [plugin] | |
| name="PolyTools" | |
| description="Adds tools to the inspector to make editing a Polygon2d a little easier." | |
| author="CodeZombie" | |
| version="1.0" | |
| script="polytools_plugin.gd" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| extends EditorInspectorPlugin | |
| #var RandomIntEditor = preload("res://addons/my_inspector_plugin/random_int_editor.gd") | |
| var STORED_POLYGON_LIST_META_ID: String = "stored_polygons" | |
| var poly_control_container: VBoxContainer | |
| var _add_poly_spinbox: SpinBox | |
| func _can_handle(object): | |
| return object is Polygon2D | |
| func _parse_begin(object): | |
| if not object.has_meta(STORED_POLYGON_LIST_META_ID): | |
| object.set_meta(STORED_POLYGON_LIST_META_ID, {}) | |
| var tool_label: Label = Label.new() | |
| tool_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL | |
| tool_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER | |
| tool_label.text = "📐 PolyTools" | |
| self.add_custom_control(tool_label) | |
| self.poly_control_container = VBoxContainer.new() | |
| self.repopulate_poly_controls(object) | |
| self.add_custom_control(self.poly_control_container) | |
| var add_poly_hbox: HBoxContainer = HBoxContainer.new() | |
| add_poly_hbox.alignment = BoxContainer.ALIGNMENT_CENTER | |
| var add_poly_label: Label = Label.new() | |
| add_poly_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL | |
| add_poly_label.text = "Add Polygon by Index" | |
| add_poly_hbox.add_child(add_poly_label) | |
| self._add_poly_spinbox = SpinBox.new() | |
| add_poly_hbox.add_child(self._add_poly_spinbox) | |
| var add_poly_button: Button = Button.new() | |
| add_poly_button.text = "Add" | |
| add_poly_button.pressed.connect(self._add_or_update_poly.bind(object)) | |
| add_poly_hbox.add_child(add_poly_button) | |
| self.add_custom_control(add_poly_hbox) | |
| var poly_global_controls_hbox: HBoxContainer = HBoxContainer.new() | |
| self.add_custom_control(poly_global_controls_hbox) | |
| var restore_all_button: Button = Button.new() | |
| restore_all_button.pressed.connect(self._restore_all.bind(object)) | |
| restore_all_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL | |
| restore_all_button.text = "Restore All" | |
| poly_global_controls_hbox.add_child(restore_all_button) | |
| var add_all_button: Button = Button.new() | |
| add_all_button.pressed.connect(self._add_all.bind(object)) | |
| add_all_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL | |
| add_all_button.text = "Add All" | |
| poly_global_controls_hbox.add_child(add_all_button) | |
| #var update_all_button: Button = Button.new() | |
| #update_all_button.pressed.connect(self._update_all.bind(object)) | |
| #update_all_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL | |
| #update_all_button.text = "Update All" | |
| #poly_global_controls_hbox.add_child(update_all_button) | |
| var delete_intersecting_button: Button = Button.new() | |
| delete_intersecting_button.pressed.connect(self._delete_intersecting_with_stored.bind(object)) | |
| delete_intersecting_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL | |
| delete_intersecting_button.text = "Delete Intersecting" | |
| poly_global_controls_hbox.add_child(delete_intersecting_button) | |
| var delete_all_button: Button = Button.new() | |
| delete_all_button.pressed.connect(self._delete_all.bind(object)) | |
| delete_all_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL | |
| delete_all_button.text = "Delete All" | |
| poly_global_controls_hbox.add_child(delete_all_button) | |
| var triangulate_button = Button.new() | |
| triangulate_button.size_flags_horizontal = Control.SIZE_SHRINK_END | |
| triangulate_button.text = "(Re)Triangulate Vertices" | |
| triangulate_button.pressed.connect(self._clear_and_triangulate_polygons.bind(object)) | |
| self.add_custom_control(triangulate_button) | |
| func repopulate_poly_controls(polygon_2d: Polygon2D): | |
| print("repopulate_poly_controls") | |
| var stored_poly : Dictionary = polygon_2d.get_meta(STORED_POLYGON_LIST_META_ID, {}) | |
| var stored_poly_keys : Array = stored_poly.keys() | |
| stored_poly_keys.sort() | |
| for child in self.poly_control_container.get_children(): | |
| self.poly_control_container.remove_child(child) | |
| child.queue_free() | |
| for stored_polygon_key in stored_poly_keys: | |
| var hbox_container: HBoxContainer = HBoxContainer.new() | |
| var polygon_label: Label = Label.new() | |
| polygon_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL | |
| polygon_label.text = str(stored_polygon_key, " (", len(stored_poly[stored_polygon_key]), " vertices)") | |
| hbox_container.add_child(polygon_label) | |
| var restore_button : Button = Button.new() | |
| restore_button.pressed.connect(self._restore_polygon.bind(polygon_2d, stored_polygon_key)) | |
| restore_button.text = "Restore" | |
| hbox_container.add_child(restore_button) | |
| #var update_button : Button = Button.new() | |
| #update_button.pressed.connect(self._add_or_update_poly.bind(polygon_2d, stored_polygon_key)) | |
| #update_button.text = "Update" | |
| #hbox_container.add_child(update_button) | |
| var delete_button : Button = Button.new() | |
| delete_button.pressed.connect(self._delete_stored_poly.bind(polygon_2d, stored_polygon_key)) | |
| delete_button.text = "Delete" | |
| hbox_container.add_child(delete_button) | |
| self.poly_control_container.add_child(hbox_container) | |
| func _restore_all(polygon_2d: Polygon2D): | |
| print("Restore All") | |
| var stored_polylist: Dictionary = polygon_2d.get_meta(STORED_POLYGON_LIST_META_ID) | |
| for stored_poly_key in stored_polylist.keys(): | |
| polygon_2d.polygons.append(stored_polylist[stored_poly_key].duplicate()) | |
| func _add_all(polygon_2d: Polygon2D): | |
| print("Add All") | |
| var stored_polylist: Dictionary = {} | |
| for i in range(len(polygon_2d.polygons)): | |
| stored_polylist[i] = polygon_2d.polygons[i].duplicate() | |
| polygon_2d.set_meta(STORED_POLYGON_LIST_META_ID, stored_polylist) | |
| self.repopulate_poly_controls(polygon_2d) | |
| #func _update_all(polygon_2d: Polygon2D): | |
| #print("Update All") | |
| #var stored_polylist: Dictionary = polygon_2d.get_meta(STORED_POLYGON_LIST_META_ID) | |
| #for polygon_id in stored_polylist.keys(): | |
| #stored_polylist[polygon_id] = polygon_2d.polygons[polygon_id].duplicate() | |
| #polygon_2d.set_meta(STORED_POLYGON_LIST_META_ID, stored_polylist) | |
| #self.repopulate_poly_controls(polygon_2d) | |
| func _delete_all(polygon_2d: Polygon2D): | |
| print("Delete All") | |
| polygon_2d.set_meta(STORED_POLYGON_LIST_META_ID, {}) | |
| self.repopulate_poly_controls(polygon_2d) | |
| func _restore_polygon(polygon_2d: Polygon2D, polygon_id: int): | |
| print("Restore poly") | |
| var stored_polylist: Dictionary = polygon_2d.get_meta(STORED_POLYGON_LIST_META_ID) | |
| polygon_2d.polygons.append(stored_polylist[polygon_id].duplicate()) | |
| func _add_or_update_poly(polygon_2d: Polygon2D, polygon_id: int = int(self._add_poly_spinbox.value)): | |
| print("Add or Update poly") | |
| var stored_polylist: Dictionary = polygon_2d.get_meta(STORED_POLYGON_LIST_META_ID) | |
| stored_polylist[polygon_id] = polygon_2d.polygons[polygon_id].duplicate() | |
| polygon_2d.set_meta(STORED_POLYGON_LIST_META_ID, stored_polylist) | |
| self.repopulate_poly_controls(polygon_2d) | |
| func _delete_stored_poly(polygon_2d: Polygon2D, polygon_id: int): | |
| print("Delete Stored Poly ", polygon_id) | |
| var stored_polylist: Dictionary = polygon_2d.get_meta(STORED_POLYGON_LIST_META_ID) | |
| stored_polylist.erase(polygon_id) | |
| polygon_2d.set_meta(STORED_POLYGON_LIST_META_ID, stored_polylist) | |
| self.repopulate_poly_controls(polygon_2d) | |
| # Taken from https://github.com/dweremeichik/godot_autopoly_plugin/ | |
| func _clear_and_triangulate_polygons(polygon_2d : Polygon2D) -> void: | |
| print("Clear and triangluate") | |
| if polygon_2d.polygon.size() < 3: | |
| # Can't triangulate without a triangle... | |
| return | |
| # var stored_polygon_index_arrays : Array = polygon_2d.get_meta(STORED_POLYGON_LIST_META_ID, {}).values() | |
| var stored_polygons = [] | |
| #for stored_polygon_index_array in stored_polygon_index_arrays: | |
| #var polygon = [] | |
| #for value_index in stored_polygon_index_array: | |
| #polygon.append(polygon_2d.polygon[value_index]) | |
| #stored_polygons.append(polygon) | |
| # | |
| #print(stored_polygons) | |
| polygon_2d.polygons = [] | |
| var points = Geometry2D.triangulate_delaunay(polygon_2d.polygon) | |
| #print("Fucking") | |
| # | |
| #for stored_polygon in stored_polygons: | |
| #points = Geometry2D.clip_polygons(PackedVector2Array(points), PackedVector2Array(stored_polygon)) | |
| #print("Shit") | |
| # Outer verticies are stored at the beginning of the PackedVector2Array | |
| var outer_polygon = polygon_2d.polygon.slice(0, polygon_2d.polygon.size() - polygon_2d.internal_vertex_count) | |
| for point in range(0, points.size(), 3): | |
| var triangle = [] | |
| triangle.push_back(points[point]) | |
| triangle.push_back(points[point + 1]) | |
| triangle.push_back(points[point + 2]) | |
| # only add the triangle if all points are inside the polygon | |
| var a : Vector2 = polygon_2d.polygon[points[point]] | |
| var b : Vector2 = polygon_2d.polygon[points[point + 1]] | |
| var c : Vector2 = polygon_2d.polygon[points[point + 2]] | |
| #for stored_poly in stored_polygons: | |
| #if len(Geometry2D.intersect_polygons(PackedVector2Array(triangle), PackedVector2Array(stored_poly))) > 0: | |
| #print("Polygon interseected. Skipping") | |
| #continue | |
| #if _points_are_inside_polygon(a, b, c, outer_polygon): | |
| #var poly_intersecting : bool = false | |
| #for stored_poly in stored_polygons: | |
| #if _points_are_inside_polygon(a, b, c, PackedVector2Array(stored_poly)): | |
| #poly_intersecting = true | |
| #if not poly_intersecting: | |
| polygon_2d.polygons.push_back(triangle) | |
| func _get_array_of_positions_from_vertex_indices(polygon_2d: Polygon2D, vertex_indices: Array) -> Array: | |
| var position_array = [] | |
| for vertex_index in vertex_indices: | |
| position_array.append(polygon_2d.polygon[vertex_index]) | |
| print("_get_array_of_positions_from_vertex_indices: ", vertex_indices, " -> ", position_array) | |
| return position_array | |
| func _delete_intersecting_with_stored(polygon_2d: Polygon2D): | |
| print("Deleting Intersecting") | |
| for stored_polygon_index_array in polygon_2d.get_meta(STORED_POLYGON_LIST_META_ID, {}).values(): | |
| var polygon_a = self._get_array_of_positions_from_vertex_indices(polygon_2d, stored_polygon_index_array) | |
| var polyrange = range(len(polygon_2d.polygons)) | |
| polyrange.reverse() | |
| for i in polyrange: | |
| var polygon_b = self._get_array_of_positions_from_vertex_indices(polygon_2d, polygon_2d.polygons[i]) | |
| if len(Geometry2D.intersect_polygons(polygon_a, polygon_b)) > 0: | |
| polygon_2d.polygons.remove_at(i) | |
| print("Finished deleting intersecting") | |
| func _points_are_inside_polygon(a: Vector2, b: Vector2, c: Vector2, polygon: PackedVector2Array) -> bool: | |
| var center = (a + b + c) / 3 | |
| # move points inside the triangle so we don't check for intersection with polygon edge | |
| a = a - (a - center).normalized() * 0.01 | |
| b = b - (b - center).normalized() * 0.01 | |
| c = c - (c - center).normalized() * 0.01 | |
| return Geometry2D.is_point_in_polygon(a, polygon) \ | |
| and Geometry2D.is_point_in_polygon(b, polygon) \ | |
| and Geometry2D.is_point_in_polygon(c, polygon) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @tool | |
| extends EditorPlugin | |
| var plugin | |
| func _enter_tree(): | |
| # EditorInspectorPlugin is a resource, so we use `new()` instead of `instance()`. | |
| plugin = preload("res://addons/polytools/polytools.gd").new() | |
| add_inspector_plugin(plugin) | |
| func _exit_tree(): | |
| remove_inspector_plugin(plugin) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment