Poisson-disk sampling, new org-roam content

This commit is contained in:
Joseph Ferano 2024-08-14 16:07:02 +07:00
parent c5096cf3f9
commit c2309066e3
19 changed files with 243 additions and 58 deletions

2
.dir-locals.el Normal file
View File

@ -0,0 +1,2 @@
((nil . ((org-roam-directory . "/home/joe/Development/gaming-pads/content")
(org-roam-db-location . "/home/joe/Development/gaming-pads/org-roam.db"))))

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/org-roam.db

View File

@ -0,0 +1,12 @@
:PROPERTIES:
:ID: 6793b3f2-32d1-46dc-b104-e2d446c9a66e
:END:
#+title: Gaming Pads
It's how the acronym GAIMENPAAADS sounds, but that's a crappy acronym. Here is
a hierarchical overview of the topics
* [[id:f2b91852-e931-4026-a9a4-cc4188eecbb0][Math for Game Programmers]]
* [[id:bdba2433-f92c-48cc-a401-a9d8f777df8f][Graphics]]
* [[id:f548e57e-3d8e-4fe4-a5eb-5ebd338b1825][Artificial Intelligence]]

View File

@ -1,24 +1,26 @@
#+TEST: [[file:SpatialPartitioning.org][Spacial Partitioning]] :PROPERTIES:
:ID: 4f3f07d3-a05e-40ad-94f9-cb35dad69ef1
:END:
#+title: Quadtree
* Quadtree
Recursively subdivide an AABB into 4 regions, hence Quad. They are usually Recursively subdivide an AABB into 4 regions, hence Quad. They are usually
denoted as North West, North East, South West, and South East (NW, NE, SW, SE). denoted as North West, North East, South West, and South East (NW, NE, SW, SE).
Several things can be created with this; Several things can be created with this;
** Linked Implementation :datastructure: * Linked Implementation :datastructure:
** Array Implementation :datastructure: * Array Implementation :datastructure:
** Insertion :algorithm: * Insertion :algorithm:
** Query :algorithm: * Query :algorithm:
** Find Nearest Neighbor :algorithm: * Find Nearest Neighbor :algorithm:
** Morton codes * Morton codes
This is a technique that allows us to convert coordinates into an array index, This is a technique that allows us to convert coordinates into an array index,
allowing us to pack them contiguously in memory allowing us to pack them contiguously in memory
http://johnsietsma.com/2019/12/05/morton-order-introduction/ http://johnsietsma.com/2019/12/05/morton-order-introduction/
** Resources * Resources
[[http://ericandrewlewis.github.io/how-a-quadtree-works/][Visualize a Quadtree]] [[http://ericandrewlewis.github.io/how-a-quadtree-works/][Visualize a Quadtree]]
[[http://donar.umiacs.umd.edu/quadtree/][Academic Interactive Demo]] [[http://donar.umiacs.umd.edu/quadtree/][Academic Interactive Demo]]
[[https://codepen.io/_bm/pen/ExPBMrW][Codepen (Js example)]] [[https://codepen.io/_bm/pen/ExPBMrW][Codepen (Js example)]]
[[https://gist.github.com/patricksurry/6478178][D3.js Example]] [[https://gist.github.com/patricksurry/6478178][D3.js Example]]
** Notes * Notes
#+begin_src js #+begin_src js
// exclude node if point is farther away than best distance in either axis // exclude node if point is farther away than best distance in either axis
if (x < x1 - best.d || x > x2 + best.d || y < y1 - best.d || y > y2 + best.d) { if (x < x1 - best.d || x > x2 + best.d || y < y1 - best.d || y > y2 + best.d) {

View File

@ -0,0 +1,6 @@
:PROPERTIES:
:ID: f2b91852-e931-4026-a9a4-cc4188eecbb0
:END:
#+title: Math for Game Programmers
* [[id:42583e83-bc7d-4428-8228-c1c04f91806f][Geometry]]

View File

@ -0,0 +1,7 @@
:PROPERTIES:
:ID: 42583e83-bc7d-4428-8228-c1c04f91806f
:END:
#+title: Geometry
* [[id:62ab48ad-9a26-4fb7-a41b-dd341165cdec][Spatial Partitioning]]
* [[id:729ccfcc-8015-4438-b0c6-3097f311e505][Triangulation]]

View File

@ -0,0 +1,8 @@
:PROPERTIES:
:ID: 62ab48ad-9a26-4fb7-a41b-dd341165cdec
:END:
#+title: Spatial Partitioning
* [[id:4f3f07d3-a05e-40ad-94f9-cb35dad69ef1][Quadtree]]
* [[id:9d793370-5e77-4ae6-bf7a-6f3e6377a79c][KD-Tree]]
* [[id:137d9b32-c670-4b30-89ff-4b2da47006db][Poisson-disk sampling]]

View File

@ -0,0 +1,10 @@
:PROPERTIES:
:ID: 9d793370-5e77-4ae6-bf7a-6f3e6377a79c
:END:
#+title: KD-Tree
Info about a KD-Tree
* Linked Implementation :datastructure:
* Array Implementation :datastructure:
* Insertion :algorithm:
* Find Nearest Neighbor :algorithm:

View File

@ -1,12 +1,14 @@
#+TEST: [[file:Geometry][Geometry]] :PROPERTIES:
:ID: 729ccfcc-8015-4438-b0c6-3097f311e505
:END:
#+title: Triangulation
* Triangulation
Generate triangles from points Generate triangles from points
** Convex Hull Jarvis March (Gift wrapping) :algorithm: * Convex Hull Jarvis March (Gift wrapping) :algorithm:
[[file:~/Development/gaming-pads/convexhull-jarvismarch.py][Python Code]] [[file:~/Development/gaming-pads/convexhull-jarvismarch.py][Python Code]]
** Resources * Resources
https://www.youtube.com/watch?v=Vu84lmMzP2o https://www.youtube.com/watch?v=Vu84lmMzP2o
https://www.habrador.com/tutorials/math/8-convex-hull/ https://www.habrador.com/tutorials/math/8-convex-hull/
https://www.youtube.com/watch?v=B2AJoQSZf4M https://www.youtube.com/watch?v=B2AJoQSZf4M
https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
** Notes * Notes

View File

@ -0,0 +1,9 @@
:PROPERTIES:
:ID: 98eb9900-fead-478c-8db4-29310ced82fb
:END:
#+title: Procedural Generation
These are used to divide a space in either a 2D or 3D world into smaller spaces
that can make operations like searching and filtering more efficient.
* [[id:137d9b32-c670-4b30-89ff-4b2da47006db][Poisson-disk sampling]]

View File

@ -0,0 +1,5 @@
:PROPERTIES:
:ID: 137d9b32-c670-4b30-89ff-4b2da47006db
:END:
#+title: Poisson-disk sampling

View File

@ -0,0 +1,7 @@
:PROPERTIES:
:ID: bdba2433-f92c-48cc-a401-a9d8f777df8f
:END:
#+title: Graphics
* [[id:98eb9900-fead-478c-8db4-29310ced82fb][Procedural Generation]]
* [[id:729ccfcc-8015-4438-b0c6-3097f311e505][Triangulation]]

View File

@ -0,0 +1,6 @@
:PROPERTIES:
:ID: f548e57e-3d8e-4fe4-a5eb-5ebd338b1825
:END:
#+title: Artificial Intelligence
* [[id:98eb9900-fead-478c-8db4-29310ced82fb][Procedural Generation]]

View File

@ -1,19 +0,0 @@
* Gaming Pads
It's how the acronym GAIMENPAAADS sounds, but that's a crappy acronym. Here is
a hierarchical overview of the topics
** Math
*** Trigonometry
*** Data Structures
*** Algorithms
*** Linear Algebra
*** Geometry
**** Bezier Curves
**** Triangulation
**** Spatial Partitioning
#+transclude: [[file:Quadtree.org]] :level 5 :exclude-elements "keyword"
#+transclude: [[file:KD-Tree.org]] :level 5 :exclude-elements "keyword"
** Engineering
*** Machine Architecture
*** Networking
*** Compilers
*** Operating Systems

View File

@ -1,6 +0,0 @@
#+TODO: ⬜ 🟩️ | ✅
* Geometry
** [[file:SpatialPartitioning.org][Spatial Partitioning]]
** [[file:Triangulation.org][Triangulation]]
** Bezier Curves

View File

@ -1,8 +0,0 @@
#+NAVIGATION_UP: [[file:SpatialPartitioning.org][Spacial Partitioning]]
* KD-Tree
Info about a KD-Tree
** Linked Implementation :datastructure:
** Array Implementation :datastructure:
** Insertion :algorithm:
** Find Nearest Neighbor :algorithm:

View File

@ -1 +0,0 @@
* Math

View File

@ -1,9 +0,0 @@
# -*- mode: Org; eval: (org-transclusion-mode 0) -*- #
#+NAVIGATION-UP: [[file:Geometry.org][Geometry]]
* Spatial Partitioning
These are used to divide a space in either a 2D or 3D world into smaller spaces
that can make operations like searching and filtering more efficient.
#+transclude: [[file:Quadtree.org]] :level 2
#+transclude: [[file:KD-Tree.org]] :level 2

151
poisson-disk-sampling.py Normal file
View File

@ -0,0 +1,151 @@
import pyray as RL
from pyray import (Rectangle as Rect, Vector2 as Vec2, Vector3 as Vec3, Camera3D, BoundingBox, Color)
import math
import pdb
import random
from typing import Optional, Tuple, List
from dataclasses import dataclass, field
def dump(struct):
s = f"{RL.ffi.typeof(struct)}: (".replace('<ctype ', '').replace('>', '')
for field in dir(struct):
data = struct.__getattribute__(field)
if str(data).startswith("<cdata"):
data = dump(data)
s += f"{field}:{data} "
s += ")"
return s
screen_width = 1024
screen_height = 512
r = 10
k = 30
cell_size = r / math.sqrt(2)
cols = int(screen_width // cell_size)
rows = int(screen_height // cell_size)
@dataclass
class World:
grid: List[Vec2]
points: List[Vec2]
active: List[Vec2]
pidx = 0
start = False
point_count = 0
frame_count: int = 0
debug_draw: bool = False
def init() -> World:
grid = [None] * rows * cols
points = [Vec2(screen_width // 2, screen_height // 2)]
# points = []
active = points.copy()
for p in points:
idx = int(p.y // cell_size * cols + p.x // cell_size)
grid[idx] = p
return World(grid, points, active)
def player_input(w: World):
if RL.is_mouse_button_pressed(0):
p = RL.get_mouse_position()
w.points.append(p)
idx = int(p.y // cell_size * cols + p.x // cell_size)
if idx < len(w.grid):
w.grid[idx] = 1
if RL.is_key_pressed(RL.KEY_SPACE):
w.debug_draw = not w.debug_draw
if RL.is_key_pressed(RL.KEY_ENTER):
w.start = True
def update(w: World):
# if w.frame_count % 1 != 0:
# return
if not w.start or not w.points or not w.active:
return
for _ in range(25):
if not w.active:
break
point = w.active[-1]
w.pidx = (w.pidx + 1) % len(w.points)
found = False
for i in range(k):
r_angle = random.random() * math.pi * 2
r_dist = random.uniform(r, 2 * r)
new_point = Vec2(point.x + math.cos(r_angle) * r_dist, point.y + math.sin(r_angle) * r_dist)
new_point_idx = int(new_point.y // cell_size * cols + new_point.x // cell_size)
if (new_point_idx >= len(w.grid) or w.grid[new_point_idx] is not None
or new_point.x < 0 or new_point.x > cell_size * cols
or new_point.y < 0 or new_point.y > cell_size * rows):
continue
new_point_row = new_point_idx // cols
new_point_col = new_point_idx % cols
collides = False
for i in range(-2, 3):
for j in range(-2, 3):
if collides:
break
row = new_point_row + i
col = new_point_col + j
if row < 0 or row >= rows or col < 0 or col >= cols:
continue
idx = row * cols + col
if w.grid[idx] is not None:
# Check distance
dist_sqr = RL.vector_2distance_sqr(w.grid[idx], new_point)
if dist_sqr < r * r:
collides = True
if collides:
continue
w.grid[new_point_idx] = new_point
w.points.append(new_point)
w.point_count += 1
w.active.append(new_point)
found = True
break
if not found:
w.active.pop()
def draw_2d(w: World):
# for p in w.points:
# prx = p.x // cell_size * cell_size
# pry = p.y // cell_size * cell_size
# rect = Rect(prx, pry, cell_size, cell_size)
# RL.draw_rectangle_rec(rect, Color(230, 230, 230, 255))
if w.debug_draw:
for i,c in enumerate(w.grid):
if c != -1:
cell_x = (i % cols) * cell_size
cell_y = (i // cols) * cell_size
rect = Rect(cell_x, cell_y, cell_size, cell_size)
RL.draw_rectangle_rec(rect, Color(230, 230, 230, 255))
for i in range(int((screen_width // cell_size) + 1) - 1):
for j in range(int((screen_height // cell_size) + 1) - 1):
rect = Rect(cell_size * i, cell_size * j, cell_size, cell_size)
RL.draw_rectangle_lines_ex(rect, 2, RL.GREEN)
for i,p in enumerate(w.points):
red = min(200, i % 255)
green = min(200, i * 2 % 255)
blue = min(200, i * 3 % 255)
RL.draw_circle_v(p, 2, Color(red, green, blue, 255))
RL.init_window(screen_width, screen_height, "Poisson-disk Sampling");
RL.set_target_fps(60)
w = init()
while not RL.window_should_close():
player_input(w)
update(w)
# Drawing
RL.begin_drawing()
RL.clear_background(RL.WHITE)
draw_2d(w)
RL.end_drawing()
w.frame_count += 1