commit 08034bb71db50810f49076b64609304554c51fe0 Author: Daniel Snider Date: Sun May 15 22:17:48 2022 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4f48ad7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ diff --git a/Circle.gd b/Circle.gd new file mode 100644 index 0000000..a1b654f --- /dev/null +++ b/Circle.gd @@ -0,0 +1,116 @@ +extends Object + +const Set = preload("res://Set.gd") + +var position: Vector2 +var radius: float + +func _init(p_position: Vector2 = Vector2(), p_radius: float = 0.0): + position = p_position + radius = p_radius + +func contains_point(p: Vector2): + return position.distance_squared_to(p) <= radius * radius + 0.1 + +func contains_all_points(ps: Array): + for p in ps: + if !contains_point(p): + return false + return true + +func circumscribe_two_points(a: Vector2, b: Vector2): + position = a.linear_interpolate(b, 0.5) + radius = a.distance_to(b) / 2.0 + +func circumscribe_three_points(a: Vector2, b: Vector2, c: Vector2): + var bary_mat := [b - a, c - a] + var det = bary_mat[0].x * bary_mat[1].y - bary_mat[1].x * bary_mat[0].y + var area = 0.5 * det + + if area < 0.001: + position = a + radius = 0.0 + return + + position = Vector2( + a.x + 1.0 / (4.0 * area) * (bary_mat[1].y * a.distance_squared_to(b) - bary_mat[0].y * a.distance_squared_to(c)), + a.y + 1.0 / (4.0 * area) * (bary_mat[0].x * a.distance_squared_to(c) - bary_mat[1].x * a.distance_squared_to(b)) + ) + radius = position.distance_to(a) + +func circumscribe(points: Array): + var n = len(points) + assert(n > 1) + + position = Vector2(0, 0) + radius = INF + + if n == 2: + circumscribe_two_points(points[0], points[1]) + return + elif n == 3: + circumscribe_three_points(points[0], points[1], points[2]) + return + + # Check every two points + for i in range(n): + for j in range(n): + if i == j: continue + var c = get_script().new() + c.circumscribe_two_points(points[i], points[j]) + if c.radius < radius and c.contains_all_points(points): + position = c.position + radius = c.radius + + # Check every three points + for i in range(n): + for j in range(n): + for k in range(n): + if i == j or j == k or i == k: continue + var c = get_script().new() + c.circumscribe_three_points(points[i], points[j], points[k]) + if c.radius < radius and c.contains_all_points(points): + position = c.position + radius = c.radius + +func circumscribe2_internal(input: Set, support: Set): + var c = get_script().new() + if !input.empty(): + var input_prime: Set = input.duplicate() + var p: Vector2 = input_prime.pop_random() # remove P from Input + + c.circumscribe2_internal(input_prime, support); + if c.contains_point(p): + return c + else: + var support_prime = support.duplicate() + support_prime.add(p) + + c.circumscribe2_internal(input_prime, support_prime) + return c + else: + var support_values = support.values() + if len(support_values) == 2: + c.circumscribe_two_points(support_values[0], support_values[1]) + elif len(support_values) == 3: + c.circumscribe_three_points(support_values[0], support_values[1], support_values[2]) + return c + +func circumscribe2(points: Array): + var n = len(points) + assert(n > 1) + + if n == 2: + circumscribe_two_points(points[0], points[1]) + return + elif n == 3: + circumscribe_three_points(points[0], points[1], points[2]) + return + + var points_set = Set.new() + points_set.add_all(points) + var c = circumscribe2_internal(points_set, Set.new()) + position = c.position + radius = c.radius + + diff --git a/Global.gd b/Global.gd new file mode 100644 index 0000000..cbde44d --- /dev/null +++ b/Global.gd @@ -0,0 +1,18 @@ +extends Node + + +# Declare member variables here. Examples: +# var a = 2 +# var b = "text" + + +# Called when the node enters the scene tree for the first time. +func _init(): + OS.set_window_position( + OS.get_screen_position(OS.get_current_screen()) + + OS.get_screen_size()*0.5 - OS.get_window_size()*0.5) + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass diff --git a/Main.tscn b/Main.tscn new file mode 100644 index 0000000..69e0e52 --- /dev/null +++ b/Main.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://MinimumAreaCircle.gd" type="Script" id=1] +[ext_resource path="res://Point.tscn" type="PackedScene" id=2] + +[node name="Main" type="Node2D"] + +[node name="Camera2D" type="Camera2D" parent="."] +position = Vector2( 2, 2 ) +current = true + +[node name="MinimumAreaCircle" type="Node2D" parent="."] +script = ExtResource( 1 ) +color = Color( 0.305882, 0.34902, 0.827451, 1 ) + +[node name="Point" parent="MinimumAreaCircle" instance=ExtResource( 2 )] + +[node name="Point2" parent="MinimumAreaCircle" instance=ExtResource( 2 )] +position = Vector2( -218, -131 ) + +[node name="Point3" parent="MinimumAreaCircle" instance=ExtResource( 2 )] +position = Vector2( -186, -51 ) + +[node name="Point4" parent="MinimumAreaCircle" instance=ExtResource( 2 )] +position = Vector2( -285, 34 ) + +[node name="Point5" parent="MinimumAreaCircle" instance=ExtResource( 2 )] +position = Vector2( -173, 87 ) diff --git a/MinimumAreaCircle.gd b/MinimumAreaCircle.gd new file mode 100644 index 0000000..566fb03 --- /dev/null +++ b/MinimumAreaCircle.gd @@ -0,0 +1,24 @@ +extends Node2D + +const Circle = preload("res://Circle.gd") + +onready var circle: Circle = Circle.new() +export var color: Color = Color.red + +var selected_idx = 0 + +func _ready(): + update_circle() + for child in get_children(): + if child is CanvasItem: + child.connect("moved", self, "update_circle") + +func update_circle(): + var ps = [] + for child in get_children(): + ps.append(child.position) + circle.circumscribe(ps) + update() + +func _draw(): + draw_arc(circle.position, circle.radius, 0, TAU, 200, color, 2.0, true) diff --git a/Point.gd b/Point.gd new file mode 100644 index 0000000..6e50d2e --- /dev/null +++ b/Point.gd @@ -0,0 +1,17 @@ +extends Node2D + +signal moved + +var dragging := false + +func _unhandled_input(event): + if event is InputEventMouseMotion and dragging and !event.has_meta("handled"): + position = get_viewport().canvas_transform.xform_inv(event.position) + emit_signal("moved") + event.set_meta("handled", true) + elif event.is_action_released("drag"): + dragging = false + +func _on_HitBox_input_event(viewport, event: InputEvent, shape_idx): + if event.is_action_pressed("drag"): + dragging = true diff --git a/Point.tscn b/Point.tscn new file mode 100644 index 0000000..c562a1a --- /dev/null +++ b/Point.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://point.png" type="Texture" id=1] +[ext_resource path="res://Point.gd" type="Script" id=2] + +[sub_resource type="CircleShape2D" id=1] +radius = 14.0 + +[node name="Point" type="Sprite"] +position = Vector2( -292, -74 ) +texture = ExtResource( 1 ) +script = ExtResource( 2 ) + +[node name="HitBox" type="Area2D" parent="."] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="HitBox"] +shape = SubResource( 1 ) + +[connection signal="input_event" from="HitBox" to="." method="_on_HitBox_input_event"] diff --git a/Set.gd b/Set.gd new file mode 100644 index 0000000..429c9b7 --- /dev/null +++ b/Set.gd @@ -0,0 +1,52 @@ +extends Object + +var _values: Dictionary + +func _init(): + _values = Dictionary() + +func size() -> int: + return _values.size() + +func empty() -> bool: + return _values.size() == 0 + +func add(value): + _values[value] = true + +func add_all(values): + for value in values: + add(value) + +func remove(value): + _values.erase(value) + +func remove_all(values): + for value in values: + remove(value) + +func get_random(): + return _values.keys()[randi() % _values.size()] + +func pop_random(): + assert(size() > 0) + var value = get_random() + remove(value) + return value + +func values() -> Array: + return _values.keys() + +func has(value) -> bool: + return _values.has(value) + +func has_all(values) -> bool: + return _values.has_all(values) + +func duplicate(): + var new_set = get_script().new() + new_set._values = _values.duplicate() + return new_set + +func clear(): + _values.clear() diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..20207a4 --- /dev/null +++ b/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..c98fbb6 Binary files /dev/null and b/icon.png differ diff --git a/icon.png.import b/icon.png.import new file mode 100644 index 0000000..a4c02e6 --- /dev/null +++ b/icon.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.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 +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/point.png b/point.png new file mode 100644 index 0000000..a408280 Binary files /dev/null and b/point.png differ diff --git a/point.png.import b/point.png.import new file mode 100644 index 0000000..5c6e91d --- /dev/null +++ b/point.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/point.png-85d91f0a7902375f86d31abcb57f3516.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://point.png" +dest_files=[ "res://.import/point.png-85d91f0a7902375f86d31abcb57f3516.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 +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..b755142 --- /dev/null +++ b/project.godot @@ -0,0 +1,64 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +[application] + +config/name="minareacircle" +run/main_scene="res://Main.tscn" +config/icon="res://icon.png" + +[autoload] + +Global="*res://Global.gd" + +[input] + +ui_left={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":65,"unicode":0,"echo":false,"script":null) + ] +} +ui_right={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":68,"unicode":0,"echo":false,"script":null) + ] +} +ui_up={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":87,"unicode":0,"echo":false,"script":null) + ] +} +ui_down={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":83,"unicode":0,"echo":false,"script":null) + ] +} +drag={ +"deadzone": 0.5, +"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null) + ] +} + +[physics] + +common/enable_pause_aware_picking=true + +[rendering] + +quality/depth/hdr=false +environment/default_environment="res://default_env.tres"