From d154de277ce4c5b8ad6a328f0646ae8168c62c39 Mon Sep 17 00:00:00 2001 From: ChickenStorm Date: Sat, 1 Aug 2020 12:03:31 +0200 Subject: [PATCH] convex polygon, polygons, and button --- ...n.svg-2d658a9fda99f155f6d8b7d14df32b3b.md5 | 3 + ....svg-2d658a9fda99f155f6d8b7d14df32b3b.stex | Bin 0 -> 5729 bytes global/geometry/convex_polygon_2d.gd | 74 ++++++ global/geometry/polygon.gd | 224 ++++++++++++++++++ global/utils.gd | 4 + gui/circular_button.gd | 1 + gui/polygonal_button.gd | 150 ++++++++++++ project.godot | 18 ++ resources/editor/polygonal_button.svg | 92 +++++++ resources/editor/polygonal_button.svg.import | 34 +++ 10 files changed, 600 insertions(+) create mode 100644 .import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.md5 create mode 100644 .import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.stex create mode 100644 global/geometry/convex_polygon_2d.gd create mode 100644 global/geometry/polygon.gd create mode 100644 gui/polygonal_button.gd create mode 100644 resources/editor/polygonal_button.svg create mode 100644 resources/editor/polygonal_button.svg.import diff --git a/.import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.md5 b/.import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.md5 new file mode 100644 index 00000000..460d6b11 --- /dev/null +++ b/.import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.md5 @@ -0,0 +1,3 @@ +source_md5="fea3be65d5f88dc9a57ed5d9b0236798" +dest_md5="52b501c75401e79f4200af6f4b93386c" + diff --git a/.import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.stex b/.import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.stex new file mode 100644 index 0000000000000000000000000000000000000000..7bb2d29fcbcb19da8f249256dde0b704427d5e94 GIT binary patch literal 5729 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 literal 0 HcmV?d00001 diff --git a/global/geometry/convex_polygon_2d.gd b/global/geometry/convex_polygon_2d.gd new file mode 100644 index 00000000..a2cd5324 --- /dev/null +++ b/global/geometry/convex_polygon_2d.gd @@ -0,0 +1,74 @@ +tool +class_name ConvexPolygon2D +extends Polygon + +export(bool) var is_oriented = false setget set_is_oriented +# this is used when checking if the point is inside. If this is oriented then the inside is the part left to the segements +export(bool) var are_border_inside = true setget set_are_border_inside + + +func _init(new_points = PoolVector2Array(), is_oriented_p = false, are_border_inside_p = true): + ._init() + is_oriented = is_oriented_p + are_border_inside = are_border_inside_p + # normaly signals cannot be connected at this point + _set_point_test(new_points) + + +func set_points(new_points): + if not _set_point_test(new_points): + printerr("array is not convex or has intersection, cannot assign") + + +func set_is_oriented(oriented): + is_oriented = oriented + emit_signal("changed") + + +func set_are_border_inside(border_inside): + are_border_inside = border_inside + emit_signal("changed") + + +func is_inside_convex(point): + # use it only if are_border_inside = false or is_oriented = true is important + # otherwise use is_inside + return _is_array_inside_convex(point, points, are_border_inside, is_oriented) + + +func get_orientation(): + # optimized for convex polygones + return _get_array_first_orientation(points) + + +func is_valid(): + return .is_valid() and is_convex() + + +func _set_point_test(new_points): + if _is_array_convex(new_points) and not _has_intersection_array(new_points): + points = new_points + emit_signal("changed") + return true + return false + + +static func _is_array_inside_convex(point, points_array, are_border_inside = true, is_oriented = false): + # only work for convex poly + # if not convex te output is unprevisible + var orientation = _get_array_first_orientation(points_array) + # bool that determine which region we need to check (bounded vs unbounded) + var unbounded_check = is_oriented and orientation == Polygon.Orientation.ANTI_TRIGONOMETRIC + for i in range(points_array.size()): + var edge = points_array[(i + 1) % points_array.size()] - points_array[i % points_array.size()] + var vector_to_point = point - points_array[i % points_array.size()] + var cross = edge.cross(vector_to_point) + var cross_oriented = (cross * orientation) if orientation != Polygon.Orientation.NOT_DEFINED else cross + if cross_oriented < 0.0 or (cross_oriented == 0.0 and not Utils.xor(unbounded_check, are_border_inside)): + # the first check determine if the point is to the side of the unbounded area + # the second condition determine if se are colinear to the edge + # the thrid one with the xor determine the behaviour depending if we consider boder inside + # as the border are considered inside event when we check for the unbounded area + # so in that case we check that teh point is not in the bounded area with inverse value of are_border_inside (achived by the xor) + return unbounded_check + return not unbounded_check diff --git a/global/geometry/polygon.gd b/global/geometry/polygon.gd new file mode 100644 index 00000000..14a5ebc0 --- /dev/null +++ b/global/geometry/polygon.gd @@ -0,0 +1,224 @@ +tool +class_name Polygon +extends Resource + +enum Orientation{ + ANTI_TRIGONOMETRIC = -1, + NOT_DEFINED = 0, + TRIGONOMETRIC = 1, +} + +export(PoolVector2Array) var points = PoolVector2Array() setget set_points + + +func _init(new_points = PoolVector2Array()): + ._init() + _set_point_test(new_points) + + +func set_points(new_points): + _set_point_test(new_points) + + +func reverse_orientation(): + points.invert() + emit_signal("changed") + + +func is_inside(point): + return Geometry.is_point_in_polygon(point, points) + + +func is_convex(): + return _is_array_convex(points) and not _has_intersection_array(points) + + +func push_back_point(point): + var new_points = points + new_points.push_back(point) + return _set_point_test(new_points) + + +func insert_point(point, index): + if index >= points.size(): + return + var new_points = points + new_points.insert(index, point) + return _set_point_test(new_points) + + +func pop_back_point(): + return pop_point(points.size() - 1) + + +func pop_point(index): + if index >= points.size(): + return null + var new_points = points + var point_back = points[index] + new_points.remove(index) + if _set_point_test(new_points): + return point_back + return null + + +func get_bounds(): + return _get_bounds_array(points) + + +func _set_point_test(new_points): + points = new_points + emit_signal("changed") + return true + + +func is_valid(): + return points != null and points.size() >= 3 + + +func get_center_of_mass(): + return _get_center_of_mass_array(points) + + +func set_orientation(orientation): + if orientation != Orientation.TRIGONOMETRIC and orientation != Orientation.ANTI_TRIGONOMETRIC: + return + var poly_orientation = get_orientation() + if poly_orientation != Orientation.NOT_DEFINED and poly_orientation != orientation: + reverse_orientation() + + +func get_shadow_points(shadow_size = 1, shadow_size_offset = Vector2.ZERO): + return _get_shadow_points_array(points, shadow_size, shadow_size_offset) + + +func get_orientation(): + return _get_poly_orientation(points) + + +func get_border_of_poly(edge_index, border_width): + return _get_border_of_array(points, edge_index, border_width) + + +static func _get_center_of_mass_array(points_array): + var center = Vector2.ZERO + if points_array.size() == 0: + return center + for i in points_array: + center += points_array + return center / points_array.size() + + +static func _get_bounds_array(points_array): + if points_array.size() == 0: + return Rect2(Vector2.ZERO, Vector2.ZERO) + var min_x = points_array[0].x + var min_y = points_array[0].y + var max_x = points_array[0].x + var max_y = points_array[0].y + for i in range(1, points_array.size()): + min_x = min(min_x, points_array[i].x) + min_y = min(min_y, points_array[i].y) + max_x = max(max_x, points_array[i].x) + max_y = max(max_y, points_array[i].y) + return Rect2(Vector2(min_x, min_y), Vector2(max_x - min_x, max_y - min_y)) + + +static func _has_intersection_array(points_array): + if points_array.size() < 3: + return false + for i in range(points_array.size() - 2): + for j in range(i + 2, points_array.size()): + var segment_1 = points_array[(i + 1) % points_array.size()] - points_array[i % points_array.size()] + var segment_2 = points_array[(j + 1) % points_array.size()] - points_array[j % points_array.size()] + var intersection = Geometry.line_intersects_line_2d(points_array[i % points_array.size()], segment_1, points_array[j % points_array.size()], segment_2) + var intersetion_segment = intersection - points_array[i % points_array.size()] + if _has_intersection_segent(points_array[i % points_array.size()], points_array[(i + 1) % points_array.size()], points_array[j % points_array.size()], points_array[(j + 1) % points_array.size()]): + return true + return false + + +static func _has_intersection_segent(point_1_start, point_1_end, point_2_start, point_2_end): + var segment_1 = point_1_end - point_1_start + var segment_2 = point_2_end - point_2_start + var intersection = Geometry.line_intersects_line_2d(point_1_start, segment_1, point_2_start, segment_2) + var intersetion_segment = intersection - point_1_start + # segemnt 1 and intersetion_segment are colinear + # the goal is to determine wehter of not intersection is inside the segment + # juste checking the length is not enought as it can be on the other side (dot is < 0) + return intersection != null and intersetion_segment.length() <= segment_1.length() and intersetion_segment.dot(segment_1) >= 0.0 + + +static func _is_array_convex(points_array): + if points_array.size() < 3: + return false + var orientation_head = _get_array_head_orientation(points_array) + for i in range(1, points_array.size()): + var segment_array = PoolVector2Array([points_array[i % points_array.size()] , points_array[(i + 1) % points_array.size()], points_array[(i + 2) % points_array.size() ]]) + var orientation = _get_array_head_orientation(segment_array) + if orientation_head == Orientation.NOT_DEFINED: + orientation_head = orientation + elif orientation != orientation_head and orientation != Orientation.NOT_DEFINED: + return false + return orientation_head != Orientation.NOT_DEFINED + + +static func _get_array_head_orientation(points_array): + if points_array.size() < 3: + return Orientation.NOT_DEFINED + var edge_1 = points_array[1] - points_array[0] + var edge_2 = points_array[2] - points_array[1] + var angle = fposmod(edge_2.angle_to(edge_1), 2.0 * PI) + if angle == PI or angle == 0.0: + return Orientation.NOT_DEFINED + return Orientation.TRIGONOMETRIC if angle < PI else Orientation.ANTI_TRIGONOMETRIC + + +static func _get_array_first_orientation(points_array): + # return the first not undefined orientation + # for convec poly this is the orientation of the poly + if points_array.size() < 3 or not _is_array_convex(points_array): + return Orientation.NOT_DEFINED + else: + for i in range(1, points_array.size()): + var segment_array = PoolVector2Array([points_array[i % points_array.size()], points_array[(i + 1) % points_array.size()], points_array[(i + 2) % points_array.size()]]) + var orientation = _get_array_head_orientation(segment_array) + if orientation != Orientation.NOT_DEFINED: + return orientation + return Orientation.NOT_DEFINED + + +static func _get_poly_orientation(points_array): + if points_array.size() < 3: + return Orientation.NOT_DEFINED + var total_angle = 0 + for i in range(points_array.size()): + var edge_1 = points_array[(i + 1) % points_array.size()] - points_array[i % points_array.size()] + var edge_2 = points_array[(i + 2) % points_array.size()] - points_array[(i + 1) % points_array.size()] + total_angle += - fposmod(edge_2.angle_to(edge_1), 2.0 * PI) + PI + if total_angle == 2.0 * PI or total_angle == 0.0: + # if total_angle this is jute that all point are colinear and therfore does not have an orientation + return Orientation.NOT_DEFINED + return Orientation.TRIGONOMETRIC if total_angle > 0.0 else Orientation.ANTI_TRIGONOMETRIC + + +static func _get_shadow_points_array(points_array, shadow_size = 1, shadow_size_offset = Vector2.ZERO): + # todo improve + var center_of_mass = _get_center_of_mass_array(points_array) + var array = PoolVector2Array() + for vector in points_array: + array.push_back((vector - center_of_mass).normalized() * shadow_size + vector + shadow_size_offset) + return array + + +static func _get_border_of_array(points_array, edge_index, border_width): + if edge_index > points_array.size() or border_width == 0: + return PoolVector2Array() + var orientation = _get_array_first_orientation(points_array) + var edge: Vector2 = points_array[(edge_index + 1) % points_array.size()] - points_array[edge_index % points_array.size()] + var edge_previous: Vector2 = points_array[edge_index % points_array.size()] - points_array[(edge_index - 1) % points_array.size()] + var edge_next: Vector2 = points_array[(edge_index + 2) % points_array.size()] - points_array[(edge_index + 1) % points_array.size()] + var array = [points_array[(edge_index) % points_array.size()], points_array[edge_index % points_array.size()]] + array.push_back((edge + edge_next).tangent().normalized() * orientation * border_width ) + array.push_back((edge + edge_previous).tangent().normalized() * orientation * border_width ) + return PoolVector2Array(array) diff --git a/global/utils.gd b/global/utils.gd index a8e51205..52f37e85 100644 --- a/global/utils.gd +++ b/global/utils.gd @@ -119,3 +119,7 @@ static func int_max(a: int, b: int) -> int: return a else: return b + + +static func xor(bool_1, bool_2): + return (bool_1 and not bool_2) or (not bool_1 and bool_2) diff --git a/gui/circular_button.gd b/gui/circular_button.gd index 43f88fdd..320dff59 100644 --- a/gui/circular_button.gd +++ b/gui/circular_button.gd @@ -143,6 +143,7 @@ func update(): static func get_vector_radial(angle, radius = 1.0): return Vector2(cos(angle), sin(angle)) * radius + func set_radius_out(new_radius): if new_radius < radius_in: return diff --git a/gui/polygonal_button.gd b/gui/polygonal_button.gd new file mode 100644 index 00000000..740eab6e --- /dev/null +++ b/gui/polygonal_button.gd @@ -0,0 +1,150 @@ +tool +class_name PolygonalButton, "res://resources/editor/polygonal_button.svg" +extends CustomShapeButton + +export(StyleBoxFlat) var base_style setget set_base_style +export(StyleBoxFlat) var hover_style setget set_hover_style +export(StyleBoxFlat) var selected_style setget set_selected_style + +export(Texture) var texture setget set_texture +export(Vector2) var texture_size setget set_texture_size + +var polygon: Polygon = null setget set_polygon + + +func _ready(): + ._ready() + if polygon != null: + polygon.connect("changed", self, "_on_changed") + if base_style != null and not base_style.is_connected("changed",self,"_on_changed"): + base_style.connect("changed",self,"_on_changed") + if selected_style != null and not selected_style.is_connected("changed",self,"_on_changed"): + selected_style.connect("changed",self,"_on_changed") + if hover_style != null and not hover_style.is_connected("changed",self,"_on_changed"): + hover_style.connect("changed",self,"_on_changed") + if texture != null and not texture.is_connected("changed",self,"_on_changed"): + texture.connect("changed",self,"_on_changed") + + +func _draw(): + # todo add theme + if rect_size.x == 0.0 or rect_size.y == 0.0: + return + # style setup + var colors_bg + var color_border + var border_width + var corner_detail = 8 + var shadow_color = Color(0.0, 0.0, 0.0, 0.0) + var shadow_size = 0 + var shadow_offseet = Vector2(0,0) + var anti_alaising = true + var draw_center = true + var style_active + if selected: + style_active = selected_style + elif _hover: + style_active = hover_style + else: + style_active = base_style + if style_active == null or base_style is StyleBoxEmpty: + colors_bg = Color(0.5,0.5,0.5) if (_hover or selected) else Color(0.7,0.7,0.7) + color_border = Color(0.0, 0.0, 0.0) + border_width = [1,1,1,1] + else: + colors_bg = style_active.bg_color + color_border = style_active.border_color + border_width = [style_active.border_width_left, style_active.border_width_top, style_active.border_width_right, style_active.border_width_bottom] + corner_detail = style_active.corner_detail + shadow_color = style_active.shadow_color + shadow_size = style_active.shadow_size + shadow_offseet = style_active.shadow_offset + anti_alaising = style_active.anti_aliasing + draw_center = style_active.draw_center + # draw + var center = rect_size / 2.0 + var points = polygon.points + # draw shadow (todo improve) + if shadow_size > 0: + var point_shadow = polygon.get_shadow_points(shadow_size, shadow_offseet) + draw_polygon(point_shadow, PoolColorArray([ shadow_color ]), PoolVector2Array(), null, null, anti_alaising) + # darw inner + if draw_center: + draw_polygon(points, PoolColorArray([ colors_bg ]), PoolVector2Array(), null, null, anti_alaising) + # draw border + for i in range(points.size()): + if border_width[i % 4] > 0: + # todo add way to override border_width to add more egdes + draw_polygon(polygon.get_border_of_poly(i ,border_width[i % 4]), PoolColorArray([color_border]), PoolVector2Array(), null, null, anti_alaising) + # draw texture + if texture != null: + _draw_texture() + # add foccus + # add text + + +func _draw_texture(): + var center = polygon.get_center_of_mass() + var rect_rexture = Rect2(center - texture_size / 2.0, texture_size) + draw_texture_rect(texture,rect_rexture,false) + + +func _on_changed(): + update() + + +func _is_inside(position): + return polygon.is_inside(position) + + +func set_polygon(new_polygon): + if new_polygon == null or not new_polygon.is_valid(): + return + if polygon != null: + polygon.disconnect("changed", self, "_on_changed") + polygon = new_polygon + polygon.connect("changed", self, "_on_changed") + _on_changed() + + +func set_base_style(style): + if base_style != null: + base_style.disconnect("changed",self,"_on_changed") + base_style = style + if base_style != null: + base_style.connect("changed",self,"_on_changed") + update() + + +func set_hover_style(style): + if hover_style != null: + hover_style.disconnect("changed",self,"_on_changed") + hover_style = style + if hover_style != null: + hover_style.connect("changed",self,"_on_changed") + update() + + +func set_texture(new_texture): + if texture != null: + texture.disconnect("changed",self,"_on_changed") + texture = new_texture + if texture != null: + texture.connect("changed",self,"_on_changed") + update() + + +func set_selected_style(style): + if selected_style != null: + selected_style.disconnect("changed",self,"_on_changed") + selected_style = style + if selected_style != null: + selected_style.connect("changed",self,"_on_changed") + update() + + +func set_texture_size(new_size): + if new_size.x < 0.0 or new_size.y < 0.0: + return + texture_size = new_size + update() diff --git a/project.godot b/project.godot index c990c737..b7a86c56 100644 --- a/project.godot +++ b/project.godot @@ -79,6 +79,11 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://hud/system/buildings/contruction/construction_building_item.gd" }, { +"base": "Polygon", +"class": "ConvexPolygon2D", +"language": "GDScript", +"path": "res://global/geometry/convex_polygon_2d.gd" +}, { "base": "Sprite", "class": "CrownSprite", "language": "GDScript", @@ -209,6 +214,16 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://matchmaking/player/player_info_faction_column.gd" }, { +"base": "Resource", +"class": "Polygon", +"language": "GDScript", +"path": "res://global/geometry/polygon.gd" +}, { +"base": "CustomShapeButton", +"class": "PolygonalButton", +"language": "GDScript", +"path": "res://gui/polygonal_button.gd" +}, { "base": "DictResource", "class": "RemoteDictResource", "language": "GDScript", @@ -309,6 +324,7 @@ _global_script_class_icons={ "CircularContainer": "res://resources/editor/circular_container.svg", "ConstanteRemoteResource": "", "ConstructionBuildingItem": "", +"ConvexPolygon2D": "", "CrownSprite": "res://resources/assets/2d/map/kalankar/crown_bottom.png", "CustomShapeButton": "res://resources/editor/custom_shape_button.svg", "DictResource": "", @@ -335,6 +351,8 @@ _global_script_class_icons={ "OptionKeyBinding": "", "Player": "", "PlayerInfoFactionColumn": "", +"Polygon": "", +"PolygonalButton": "res://resources/editor/polygonal_button.svg", "RemoteDictResource": "", "RoundedSelectablePanelLeft": "", "RoundedSelectablePanelRight": "", diff --git a/resources/editor/polygonal_button.svg b/resources/editor/polygonal_button.svg new file mode 100644 index 00000000..e5ecd0f4 --- /dev/null +++ b/resources/editor/polygonal_button.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/resources/editor/polygonal_button.svg.import b/resources/editor/polygonal_button.svg.import new file mode 100644 index 00000000..5ac9e5e9 --- /dev/null +++ b/resources/editor/polygonal_button.svg.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resources/editor/polygonal_button.svg" +dest_files=[ "res://.import/polygonal_button.svg-2d658a9fda99f155f6d8b7d14df32b3b.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0