paramètres + prévisualisation

pull/4/head
Valentin Stark 3 years ago
parent 0c4c474e5d
commit 2523700b09
  1. 21
      LICENSE
  2. 28
      Poisson.gd
  3. 92
      Poisson.tscn
  4. 23
      Previsualisation.gd
  5. 32
      addons/PoissonDiscSampling/Demo/PolygonTester.gd
  6. 12
      addons/PoissonDiscSampling/Demo/PolygonTester.tscn
  7. 137
      addons/PoissonDiscSampling/PoissonDiscSampling.gd
  8. 10
      project.godot

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Udit Parmar
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.

@ -0,0 +1,28 @@
extends Control
export var default_width = 500
export var default_height = 500
export var default_spacing = 5
var width = default_width
var height = default_height
var spacing = default_spacing
var poisson_disc_sampling: PoissonDiscSampling = PoissonDiscSampling.new()
var points = []
signal new_poisson(points)
func _ready():
$HBoxContainer/Properties/width/width_edit.text = str(default_width)
$HBoxContainer/Properties/height/height_edit.text = str(default_height)
$HBoxContainer/Properties/spacing/spacing_edit.text = str(default_spacing)
pass
func _on_Button_pressed():
width = int($HBoxContainer/Properties/width/width_edit.text)
height = int($HBoxContainer/Properties/height/height_edit.text)
spacing = int($HBoxContainer/Properties/spacing/spacing_edit.text)
var rect = Rect2(Vector2(0, 0), Vector2(width, height))
points = poisson_disc_sampling.generate_points(spacing, rect, 20)
emit_signal("new_poisson", points)

@ -1,5 +1,93 @@
[gd_scene format=2]
[gd_scene load_steps=3 format=2]
[node name="Control" type="Control"]
[ext_resource path="res://Poisson.gd" type="Script" id=1]
[ext_resource path="res://Previsualisation.gd" type="Script" id=2]
[node name="Poisson" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
[node name="HBoxContainer" type="HBoxContainer" parent="."]
margin_right = 40.0
margin_bottom = 40.0
[node name="Properties" type="VBoxContainer" parent="HBoxContainer"]
margin_right = 121.0
margin_bottom = 152.0
[node name="width" type="HBoxContainer" parent="HBoxContainer/Properties"]
margin_right = 121.0
margin_bottom = 24.0
[node name="width_label" type="Label" parent="HBoxContainer/Properties/width"]
margin_top = 5.0
margin_right = 55.0
margin_bottom = 19.0
text = "Largeur :"
[node name="width_edit" type="LineEdit" parent="HBoxContainer/Properties/width"]
margin_left = 59.0
margin_right = 117.0
margin_bottom = 24.0
[node name="height" type="HBoxContainer" parent="HBoxContainer/Properties"]
margin_top = 28.0
margin_right = 121.0
margin_bottom = 52.0
[node name="height_label" type="Label" parent="HBoxContainer/Properties/height"]
margin_top = 5.0
margin_right = 59.0
margin_bottom = 19.0
text = "Hauteur :"
[node name="height_edit" type="LineEdit" parent="HBoxContainer/Properties/height"]
margin_left = 63.0
margin_right = 121.0
margin_bottom = 24.0
[node name="spacing" type="HBoxContainer" parent="HBoxContainer/Properties"]
margin_top = 56.0
margin_right = 121.0
margin_bottom = 80.0
[node name="spacing_label" type="Label" parent="HBoxContainer/Properties/spacing"]
margin_top = 5.0
margin_right = 58.0
margin_bottom = 19.0
text = "Densité :"
[node name="spacing_edit" type="LineEdit" parent="HBoxContainer/Properties/spacing"]
margin_left = 62.0
margin_right = 120.0
margin_bottom = 24.0
[node name="generate" type="Button" parent="HBoxContainer/Properties"]
margin_top = 84.0
margin_right = 121.0
margin_bottom = 104.0
text = "Générer"
[node name="open" type="Button" parent="HBoxContainer/Properties"]
margin_top = 108.0
margin_right = 121.0
margin_bottom = 128.0
text = "Ouvrir"
[node name="save" type="Button" parent="HBoxContainer/Properties"]
margin_top = 132.0
margin_right = 121.0
margin_bottom = 152.0
text = "Enregistrer"
[node name="ScrollContainer" type="ScrollContainer" parent="."]
margin_left = 125.0
margin_right = 653.0
margin_bottom = 502.0
[node name="Previsualisation" type="TextureRect" parent="ScrollContainer"]
script = ExtResource( 2 )
[connection signal="new_poisson" from="." to="ScrollContainer/Previsualisation" method="_on_Control_new_poisson"]
[connection signal="pressed" from="HBoxContainer/Properties/generate" to="." method="_on_Button_pressed"]

@ -0,0 +1,23 @@
extends TextureRect
var image
func create_image(points):
image = Image.new()
image.create(get_parent().get_parent().width, get_parent().get_parent().height, false, Image.FORMAT_RGBA8)
image.fill(Color.white)
image.lock()
for point in points:
image.set_pixel(point.x, point.y, Color.black)
image.unlock()
func update_texture():
var texture = ImageTexture.new()
texture.create_from_image(image)
set_texture(texture)
func _on_Control_new_poisson(points):
create_image(points)
update_texture()

@ -0,0 +1,32 @@
extends Node2D
onready var polygon: Array = $Polygon2D.polygon
onready var n = polygon.size()
var radius: int = 20
var k: int = 0
var points := []
func _draw() -> void:
for i in n:
draw_line(polygon[i], polygon[(i+1)%n], Color(1,1,0), 2, 1)
draw_circle(points[k], radius / 2, Color( 1, 0, 0, 1 ))
draw_circle(points[k], 2, Color( 1, 1, 0, 1 ))
func _ready() -> void:
var pds = PoissonDiscSampling.new()
var start_time = OS.get_ticks_msec()
points = pds.generate_points(radius, $Polygon2D.polygon, 30)
print(points.size(), " points generated in ", OS.get_ticks_msec() - start_time, " miliseconds" )
get_viewport().render_target_clear_mode = Viewport.UPDATE_ONCE
func _process(delta: float) -> void:
if k < points.size() - 1:
update()
k += 1

@ -0,0 +1,12 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/PoissonDiscSampling/Demo/PolygonTester.gd" type="Script" id=1]
[node name="PolygonTester" type="Node2D"]
script = ExtResource( 1 )
[node name="Polygon2D" type="Polygon2D" parent="."]
position = Vector2( -0.568787, 0 )
color = Color( 0.921569, 1, 0, 0 )
antialiased = true
polygon = PoolVector2Array( 1652.09, 404.164, 1020.9, 114.599, 791.346, 514.627, 474.889, 192.896, 357.097, 495.288, 277.983, 75.1039, 70.5279, 184.106, 35.3661, 625.387, 239.305, 918.988, 156.674, 609.564, 126.787, 249.155, 300.838, 609.564, 581.675, 703.491, 506.535, 345.85, 833.54, 739.663, 1089.23, 313.065, 1499.18, 501.771, 1495.92, 836.886, 1356.02, 862.915, 1050.19, 882.436, 1144.54, 583.11, 1277.93, 599.377, 1398.32, 758.801, 1378.79, 573.349, 1125.02, 443.207, 907.03, 934.493, 1609.8, 983.296 )

@ -0,0 +1,137 @@
class_name PoissonDiscSampling
var _radius: float
var _sample_region_shape
var _retries: int
var _start_pos: Vector2
var _sample_region_rect: Rect2
var _cell_size: float
var _rows: int
var _cols: int
var _cell_size_scaled: Vector2
var _grid: Array = []
var _points: Array = []
var _spawn_points: Array = []
var _transpose: Vector2
# radius - minimum distance between points
# sample_region_shape - takes any of the following:
# -a Rect2 for rectangular region
# -an array of Vector2 for polygon region
# -a Vector3 with x,y as the position and z as the radius of the circle
# retries - maximum number of attempts to look around a sample point, reduce this value to speed up generation
# start_pos - optional parameter specifying the starting point
#
# returns an Array of Vector2D with points in the order of their discovery
func generate_points(radius: float, sample_region_shape, retries: int, start_pos := Vector2(INF, INF)) -> Array:
_radius = radius
_sample_region_shape = sample_region_shape
_retries = retries
_start_pos = start_pos
_init_vars()
while _spawn_points.size() > 0:
var spawn_index: int = randi() % _spawn_points.size()
var spawn_centre: Vector2 = _spawn_points[spawn_index]
var sample_accepted: bool = false
for i in retries:
var angle: float = 2 * PI * randf()
var sample: Vector2 = spawn_centre + Vector2(cos(angle), sin(angle)) * (radius + radius * randf())
if _is_valid_sample(sample):
_grid[int((_transpose.x + sample.x) / _cell_size_scaled.x)][int((_transpose.y + sample.y) / _cell_size_scaled.y)] = _points.size()
_points.append(sample)
_spawn_points.append(sample)
sample_accepted = true
break
if not sample_accepted:
_spawn_points.remove(spawn_index)
return _points
func _is_valid_sample(sample: Vector2) -> bool:
if _is_point_in_sample_region(sample):
var cell := Vector2(int((_transpose.x + sample.x) / _cell_size_scaled.x), int((_transpose.y + sample.y) / _cell_size_scaled.y))
var cell_start := Vector2(max(0, cell.x - 2), max(0, cell.y - 2))
var cell_end := Vector2(min(cell.x + 2, _cols - 1), min(cell.y + 2, _rows - 1))
for i in range(cell_start.x, cell_end.x + 1):
for j in range(cell_start.y, cell_end.y + 1):
var search_index: int = _grid[i][j]
if search_index != -1:
var dist: float = _points[search_index].distance_to(sample)
if dist < _radius:
return false
return true
return false
func _is_point_in_sample_region(sample: Vector2) -> bool:
if _sample_region_rect.has_point(sample):
match typeof(_sample_region_shape):
TYPE_RECT2:
return true
TYPE_VECTOR2_ARRAY, TYPE_ARRAY:
if Geometry.is_point_in_polygon(sample, _sample_region_shape):
return true
TYPE_VECTOR3:
if Geometry.is_point_in_circle(sample, Vector2(_sample_region_shape.x, _sample_region_shape.y), _sample_region_shape.z):
return true
_:
return false
return false
func _init_vars() -> void:
randomize()
# identify the type of shape and it's bounding rectangle and starting point
match typeof(_sample_region_shape):
TYPE_RECT2:
_sample_region_rect = _sample_region_shape
if _start_pos.x == INF:
_start_pos.x = _sample_region_rect.position.x + _sample_region_rect.size.x * randf()
_start_pos.y = _sample_region_rect.position.y + _sample_region_rect.size.y * randf()
TYPE_VECTOR2_ARRAY, TYPE_ARRAY:
var start: Vector2 = _sample_region_shape[0]
var end: Vector2 = _sample_region_shape[0]
for i in range(1, _sample_region_shape.size()):
start.x = min(start.x, _sample_region_shape[i].x)
start.y = min(start.y, _sample_region_shape[i].y)
end.x = max(end.x, _sample_region_shape[i].x)
end.y = max(end.y, _sample_region_shape[i].y)
_sample_region_rect = Rect2(start, end - start)
if _start_pos.x == INF:
var n: int = _sample_region_shape.size()
var i: int = randi() % n
_start_pos = _sample_region_shape[i] + (_sample_region_shape[(i + 1) % n] - _sample_region_shape[i]) * randf()
TYPE_VECTOR3:
var x = _sample_region_shape.x
var y = _sample_region_shape.y
var r = _sample_region_shape.z
_sample_region_rect = Rect2(x - r, y - r, r * 2, r * 2)
if _start_pos.x == INF:
var angle: float = 2 * PI * randf()
_start_pos = Vector2(x, y) + Vector2(cos(angle), sin(angle)) * r * randf()
_:
_sample_region_shape = Rect2(0, 0, 0, 0)
push_error("Unrecognized shape!!! Please input a valid shape")
_cell_size = _radius / sqrt(2)
_cols = max(floor(_sample_region_rect.size.x / _cell_size), 1)
_rows = max(floor(_sample_region_rect.size.y / _cell_size), 1)
# scale the cell size in each axis
_cell_size_scaled.x = _sample_region_rect.size.x / _cols
_cell_size_scaled.y = _sample_region_rect.size.y / _rows
# use tranpose to map points starting from origin to calculate grid position
_transpose = -_sample_region_rect.position
_grid = []
for i in _cols:
_grid.append([])
for j in _rows:
_grid[i].append(-1)
_points = []
_spawn_points = []
_spawn_points.append(_start_pos)

@ -8,6 +8,16 @@
config_version=4
_global_script_classes=[ {
"base": "Reference",
"class": "PoissonDiscSampling",
"language": "GDScript",
"path": "res://addons/PoissonDiscSampling/PoissonDiscSampling.gd"
} ]
_global_script_class_icons={
"PoissonDiscSampling": ""
}
[application]
config/name="Societer Utils"

Loading…
Cancel
Save