Hyperbolic Asteroids, first commit. Rendering Poincare disk, circles, and lines.

This commit is contained in:
2025-08-20 20:32:43 -05:00
commit 87965cafb5
8 changed files with 1478 additions and 0 deletions

213
src/lib.rs Normal file
View File

@@ -0,0 +1,213 @@
use std::f64;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
//use web_sys::console;
mod cga;
use cga::CGA;
const PI: f64 = f64::consts::PI;
const TWO_PI: f64 = 2.*PI;
const ERROR: f64 = 0.0001;
const WIDTH: f64 = 600.;
const HEIGHT: f64 = 600.;
const CENTER_X: f64 = 300.;
const CENTER_Y: f64 = 300.;
const RADIUS: f64 = 250.;
const BACKGROUND: &str = "#ffffff";
const BLACK: &str = "#000000";
const RED: &str = "#ff0000";
const GREEN: &str = "#00ff00";
const _BLUE: &str = "#0000ff";
const SCALAR: usize = 0;
const E1: usize = 1;
const E2: usize = 2;
//const E3: usize = 3;
//const E4: usize = 4;
//const E5: usize = 5;
//const E12: usize = 6;
//const E13: usize = 7;
//const E14: usize = 8;
//const E15: usize = 9;
//const E23: usize = 10;
//const E24: usize = 11;
//const E25: usize = 12;
//const E34: usize = 13;
//const E35: usize = 14;
//const E45: usize = 15;
//const E123: usize = 16;
//const E124: usize = 17;
//const E125: usize = 18;
//const E134: usize = 19;
//const E135: usize = 20;
//const E145: usize = 21;
//const E234: usize = 22;
//const E235: usize = 23;
//const E245: usize = 24;
//const E345: usize = 25;
//const E1234: usize = 26;
//const E1235: usize = 27;
//const E1245: usize = 28;
//const E1345: usize = 29;
//const E2345: usize = 30;
//const E12345: usize = 31;
struct Ship {
_pos: CGA,
_vel: CGA,
_orientation: CGA,
}
fn point_to_cga(x: f64, y: f64) -> CGA {
let x_vec = CGA::e1();
let y_vec = CGA::e2();
let _z_vec =CGA::e3();
let e_vec = CGA::e4();
let e_bar = CGA::e5();
let n_vec = &e_vec + &e_bar;
let n_bar = &e_vec - &e_bar;
let mut ux = x;
let mut uy = y;
let mut mag_sqr = ux*ux + uy*uy;
if mag_sqr > 1. {
ux /= mag_sqr.sqrt();
uy /= mag_sqr.sqrt();
mag_sqr = 1.;
}
0.5*mag_sqr*n_vec + ux*x_vec + uy*y_vec - 0.5*n_bar
}
fn draw_point(context: &CanvasRenderingContext2d, point: &CGA) {
//Naively assume we are given a valid point
let x = point[1]*RADIUS + CENTER_X;
let y = point[2]*RADIUS + CENTER_Y;
context.begin_path();
context.set_fill_style_str(RED);
context.arc(x, y, 2., 0., TWO_PI).unwrap();
context.fill();
}
fn draw_line(context: &CanvasRenderingContext2d, line: &CGA) {
context.begin_path();
context.set_stroke_style_str(GREEN);
//Naively assume we are given a valid line/circle
let n_bar = CGA::e4() - CGA::e5();
let n_vec = CGA::e4() + CGA::e5();
let dual = line.Dual();
let lambda = -(&dual | &n_vec)[SCALAR];
let mu = (&dual | &n_bar)[SCALAR];
let x = dual[E1];
let y = dual[E2];
if lambda.abs() < ERROR {
//Line
let norm_sqr = x*x+y*y;
if norm_sqr < ERROR {
return;
} else {
let norm = norm_sqr.sqrt();
let px = mu * 0.5 * x / norm_sqr;
let py = mu * 0.5 * y / norm_sqr;
let tx = -y / norm;
let ty = x / norm;
let p1x = px + tx * 1000.;
let p1y = py + ty * 1000.;
let p2x = px - tx * 1000.;
let p2y = py - ty * 1000.;
context.move_to(p1x*RADIUS + CENTER_X, p1y*RADIUS + CENTER_Y);
context.line_to(p2x*RADIUS + CENTER_X, p2y*RADIUS + CENTER_Y);
}
} else {
//Extract Center and Radius
let c_x = x/lambda;
let c_y = y/lambda;
let r_sqr = c_x*c_x + c_y*c_y - mu/lambda;
let r = r_sqr.sqrt();
//draw_point(context, &point_to_cga(c_x, c_y));
//Convert into Canvas Space
let canvas_cx = c_x*RADIUS + CENTER_X;
let canvas_cy = c_y*RADIUS + CENTER_Y;
let canvas_r = r*RADIUS;
//Draw
context.arc(canvas_cx, canvas_cy, canvas_r, 0., TWO_PI).unwrap();
}
context.stroke();
}
// Start entry point
#[wasm_bindgen(start)]
pub fn start() -> Result<(), JsValue> {
let _ship = Ship { _pos: point_to_cga(0.,0.), _vel: CGA::zero(), _orientation: CGA::zero() };
// Grab document and canvas
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let canvas = document
.get_element_by_id("game-canvas")
.unwrap()
.dyn_into::<HtmlCanvasElement>()?;
canvas.set_width(WIDTH as u32);
canvas.set_height(HEIGHT as u32);
let context = canvas
.get_context("2d")?
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()?;
//Set up play area.
context.begin_path();
context.set_fill_style_str(BACKGROUND);
context.fill_rect(0.0, 0.0, WIDTH, HEIGHT);
context.fill();
context.set_stroke_style_str(BLACK);
context.arc(CENTER_X, CENTER_Y, RADIUS, 0., TWO_PI).unwrap();
context.stroke();
let point = point_to_cga(0.,0.);
draw_point(&context, &point);
let p1 = point_to_cga(0.9, 0.0);
draw_point(&context, &p1);
let p2 = point_to_cga(0.0, 0.9);
draw_point(&context, &p2);
let p3 = point_to_cga(0.7, 0.6);
draw_point(&context, &p3);
let circle = &p1 ^ &p2 ^ &p3 ^ &CGA::e3();
draw_line(&context, &circle);
let p1 = point_to_cga(0.9, 0.2);
draw_point(&context, &p1);
let p2 = point_to_cga(-0.9, -0.);
draw_point(&context, &p2);
let p3 = point_to_cga(0., 0.1);
draw_point(&context, &p3);
let circle = &p1 ^ &p2 ^ &p3 ^ &CGA::e3();
draw_line(&context, &circle);
let p1 = point_to_cga(0.9, 0.1);
draw_point(&context, &p1);
let p2 = point_to_cga(-0.9, -0.1);
draw_point(&context, &p2);
let p3 = point_to_cga(0., 0.0);
draw_point(&context, &p3);
let circle = &p1 ^ &p2 ^ &p3 ^ &CGA::e3();
draw_line(&context, &circle);
Ok(())
}