From 08034bb71db50810f49076b64609304554c51fe0 Mon Sep 17 00:00:00 2001 From: Daniel Snider Date: Sun, 15 May 2022 22:17:48 -0700 Subject: [PATCH] Initial commit --- .gitignore | 11 ++++ Circle.gd | 116 +++++++++++++++++++++++++++++++++++++++++++ Global.gd | 18 +++++++ Main.tscn | 28 +++++++++++ MinimumAreaCircle.gd | 24 +++++++++ Point.gd | 17 +++++++ Point.tscn | 19 +++++++ Set.gd | 52 +++++++++++++++++++ default_env.tres | 7 +++ icon.png | Bin 0 -> 3305 bytes icon.png.import | 35 +++++++++++++ point.png | Bin 0 -> 286 bytes point.png.import | 35 +++++++++++++ project.godot | 64 ++++++++++++++++++++++++ 14 files changed, 426 insertions(+) create mode 100644 .gitignore create mode 100644 Circle.gd create mode 100644 Global.gd create mode 100644 Main.tscn create mode 100644 MinimumAreaCircle.gd create mode 100644 Point.gd create mode 100644 Point.tscn create mode 100644 Set.gd create mode 100644 default_env.tres create mode 100644 icon.png create mode 100644 icon.png.import create mode 100644 point.png create mode 100644 point.png.import create mode 100644 project.godot 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 0000000000000000000000000000000000000000..c98fbb601c83c81ec8c22b1dba7d1d57c62b323c GIT binary patch literal 3305 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a408280d4a489deeb96c2776950991d556a27be7 GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt-!PT^vI!de=@mE!gBB!jf*Q zt*&UmefWaRpOht;4QdX2O!G4JcJJb9@-_^8f8%eO{iQpMLJxTEG%TMhW5IY*Vh^iU z-EyW&iaR75VrTUoxh8k(&s?Dn)yzcMYR}aTtGiU*{$cyx@K$f9ilp`mRb6H&kD%P1 z&O3q}E%80VBEl@K`aAptV(!Hrn*Wh;jeUv3ssHksmv5i+IyLd^-wo=Wjmi@kx6b9U eD9JiMQB3sxMDxdPjafivGkCiCxvX