Triangulate a convex hull

This commit is contained in:
Joseph Ferano 2024-08-09 13:55:34 +07:00
parent 8df43af3e6
commit 8fe7b273a5
5 changed files with 195 additions and 8 deletions

View File

@ -12,6 +12,8 @@ Several things can be created with this;
** Resources
[[http://ericandrewlewis.github.io/how-a-quadtree-works/][Visualize a Quadtree]]
[[http://donar.umiacs.umd.edu/quadtree/][Academic Interactive Demo]]
[[https://codepen.io/_bm/pen/ExPBMrW][Codepen (Js example)]]
[[https://gist.github.com/patricksurry/6478178][D3.js Example]]
** Notes
// 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) {

View File

@ -3,5 +3,10 @@
* Triangulation
Generate triangles from points
** Convex Hull Jarvis March (Gift wrapping) :algorithm:
[[file:~/Development/gaming-pads/convexhull-jarvismarch.py][Python Code]]
** Resources
https://www.youtube.com/watch?v=Vu84lmMzP2o
https://www.habrador.com/tutorials/math/8-convex-hull/
https://www.youtube.com/watch?v=B2AJoQSZf4M
https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
** Notes

View File

@ -65,7 +65,6 @@ def player_input(w: World):
w.dragging_vert.x = collision.point.x
w.dragging_vert.z = collision.point.z
if RL.is_mouse_button_released(0):
print('no')
w.dragging_vert = None
def update(w: World):

View File

@ -1,13 +1,23 @@
import pyray as RL
from pyray import (Rectangle as Rect, Vector2 as Vec2, Vector3 as Vec3, Camera3D)
from pyray import (Rectangle as Rect, Vector2 as Vec2, Vector3 as Vec3, Camera3D, BoundingBox)
import math
import pdb
import random
from typing import Optional, Tuple, List
from dataclasses import dataclass, field
screen_width = 1280
screen_height = 1024
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 = 768
@dataclass
class World:
@ -15,7 +25,8 @@ class World:
frame_count: int = 0
def init() -> World:
pass
cam = Camera3D(Vec3(0, 10, 10), Vec3(0, 0, 0), Vec3(0, 1, 0), 45, RL.CAMERA_PERSPECTIVE)
return World(cam)
def player_input(w: World):
pass
@ -32,9 +43,7 @@ def draw_2d(w: World):
RL.init_window(screen_width, screen_height, "Starter");
RL.set_target_fps(60)
init()
cam = Camera3D(Vec3(0, 0, 10), RL.vector3_zero(), Vec3(0, 1, 0), 60, 1)
w = World(cam)
w = init()
while not RL.window_should_close():
player_input(w)
update(w)

View File

@ -0,0 +1,172 @@
import pyray as RL
from pyray import (
ffi,
Rectangle as Rect,
Vector2 as Vec2,
Vector3 as Vec3,
Camera3D,
BoundingBox,
Mesh,
Model
)
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 = 768
grid_slices = 100
grid_spacing = 0.2
vertex_radius = 0.12
@dataclass
class World:
cam: Camera3D
floor_bb: BoundingBox
rotate_cam: bool = True
draw_wireframe: bool = RL.ffi.new('bool *', False)
frame_count: int = 0
vertices: List[Vec3] = field(default_factory=list)
dragging_vert: Vec3 = None
convex_hull_points: List[Vec3] = field(default_factory=list)
mesh: Mesh = None
model: Model = None
def get_convex_hull(vertices: List[Vec3]):
convex_hull_points = []
pending = sorted(vertices, key=lambda v: v.x)
convex_hull_points.append(pending[0])
idx = 0
while True:
v1 = convex_hull_points[-1]
left_most = pending[(idx + 1) % len(pending)]
idx += 1
if v1 == left_most:
continue
for v2 in pending:
x1 = v1.x - left_most.x
x2 = v1.x - v2.x
y1 = v1.z - left_most.z
y2 = v1.z - v2.z
sign = y2*x1 - y1*x2
if v1 == v2:
continue
if sign > 0:
left_most = v2
if left_most == convex_hull_points[0]:
break
convex_hull_points.append(left_most)
return convex_hull_points
def init() -> World:
cam = Camera3D(Vec3(0, 10, 10), Vec3(0, 0, 0), Vec3(0, 1, 0), 45, RL.CAMERA_PERSPECTIVE)
half_grid = grid_slices * grid_spacing * 0.5
floor_bb = BoundingBox(Vec3(-half_grid, -0.01, -half_grid), Vec3(half_grid, 0.01, half_grid))
w = World(cam, floor_bb)
num_starting_verts = 10
radius = 4
w.vertices = [
Vec3(math.cos(math.pi * 2 / num_starting_verts * i) * radius,
0,
math.sin(math.pi * 2 / num_starting_verts * i) * radius)
for i in range(num_starting_verts)
]
vs = get_convex_hull(w.vertices)
def get_points(v: Vec3):
return v.x, v.y, v.z
vertices = ffi.new("float[]", len(vs) * 3 * 3)
# for i,v in enumerate([0,0,0 1,0,2, 2,0,0, 1,0,2, 3,0,2, 2,0,0]):
for i,v in enumerate(vs):
if i < 2:
continue
for j,vv in enumerate([*get_points(vs[0]), *get_points(vs[i - 1]), *get_points(vs[i])]):
vertices[(i-2)*9+j] = vv
texcoords = ffi.new("float[]", 0)
# for i,c in enumerate([0, 0, 0.5, 1, 1, 0]):
# texcoords[i] = c
normals = ffi.new("float[]", 0)
# for i,c in enumerate([0, 1, 0, 0, 1, 0, 0, 1, 0]):
# normals[i] = c
texcoords2 = ffi.new("float[]", 0)
# for i in range(6):
# texcoords[i] = 0
# mesh = Mesh(6, 2, vertices, texcoords, texcoords2, normals)
mesh = Mesh(len(vs) * 3, len(vs), vertices, texcoords, texcoords2, normals)
RL.upload_mesh(mesh, False)
w.mesh = mesh
w.model = RL.load_model_from_mesh(mesh)
return w
def player_input(w: World):
if RL.is_key_pressed(RL.KEY_SPACE):
w.rotate_cam = not w.rotate_cam
if RL.is_key_pressed(RL.KEY_W):
w.draw_wireframe = not w.draw_wireframe
def update(w: World):
pass
# mesh = Mesh(
# vertexCount = len(w.vertices),
# triangleCount = len(w.vertices) - 2,
# vertices = w.vertices
# )
# new_mesh = RL.gen_
def draw_3d(w: World):
RL.draw_grid(grid_slices, grid_spacing)
RL.draw_bounding_box(w.floor_bb, RL.GREEN)
for vert in w.vertices:
RL.draw_sphere(vert, vertex_radius, RL.GREEN)
if w.draw_wireframe[0]:
RL.rl_enable_wire_mode()
else:
RL.rl_disable_wire_mode()
RL.draw_model(w.model, RL.vector3_zero(), 1, RL.GREEN)
def draw_2d(w: World):
val = RL.gui_check_box(Rect(10, 30, 50, 50), "Draw Wireframe", w.draw_wireframe)
if val != 0:
print(val)
if val:
print('hello')
w.draw_wireframe = True
RL.init_window(screen_width, screen_height, "Starter");
RL.set_target_fps(60)
w = init()
while not RL.window_should_close():
player_input(w)
update(w)
# Drawing
if w.rotate_cam:
RL.update_camera(w.cam, RL.CameraMode.CAMERA_ORBITAL)
RL.begin_drawing()
RL.clear_background(RL.WHITE)
RL.begin_mode_3d(w.cam)
draw_3d(w)
RL.end_mode_3d()
draw_2d(w)
RL.end_drawing()
w.frame_count += 1