gaming-pads/convexhull-jarvismarch.py

136 lines
4.0 KiB
Python

import pyray as RL
from pyray import (Rectangle as Rect, Vector2 as Vec2, Vector3 as Vec3, Camera3D, BoundingBox)
import math
import pdb
import random
from ctypes import Structure, c_float
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
frame_count: int = 0
vertices: List[Vec3] = field(default_factory=list)
dragging_vert: Vec3 = None
convex_hull_points: List[Vec3] = field(default_factory=list)
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))
return World(cam, floor_bb)
def player_input(w: World):
if RL.is_key_pressed(RL.KEY_SPACE):
w.rotate_cam = not w.rotate_cam
if RL.is_mouse_button_pressed(0):
ray = RL.get_mouse_ray(RL.get_mouse_position(), w.cam)
# First try to collide with existing points
for v in w.vertices:
collision = RL.get_ray_collision_sphere(ray, v, vertex_radius)
if collision.hit:
w.dragging_vert = v
found_collision = True
return
w.dragging_vert = None
collision = RL.get_ray_collision_box(ray, w.floor_bb)
if collision.hit:
p = collision.point
w.vertices.append(Vec3(p.x, 0, p.z))
if RL.is_mouse_button_down(0):
if w.dragging_vert is not None:
ray = RL.get_mouse_ray(RL.get_mouse_position(), w.cam)
collision = RL.get_ray_collision_box(ray, w.floor_bb)
if collision.hit:
w.dragging_vert.x = collision.point.x
w.dragging_vert.z = collision.point.z
if RL.is_mouse_button_released(0):
w.dragging_vert = None
def update(w: World):
if len(w.vertices) <= 2:
return
w.convex_hull_points.clear()
pending = sorted(w.vertices, key=lambda v: v.x)
w.convex_hull_points.append(pending[0])
idx = 0
# TODO:
while True:
v1 = w.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 == w.convex_hull_points[0]:
break
w.convex_hull_points.append(left_most)
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 len(w.convex_hull_points) > 2:
for i,p in enumerate(w.convex_hull_points[:-1]):
RL.draw_line_3d(p, w.convex_hull_points[i+1], RL.GREEN)
RL.draw_line_3d(w.convex_hull_points[-1], w.convex_hull_points[0], RL.GREEN)
# exit(0)
def draw_2d(w: World):
RL.draw_fps(10, 10)
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