use crate::gfx::{depth::DepthQueue, camera::Camera3D};
use std::f32::consts::TAU;
const DEPTH_BIAS: f32 = -0.06;
#[inline]
fn p2w(
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
u:f32, v:f32,
) -> (f32,f32,f32) {
(cx + ux*u + vx*v,
cy + uy*u + vy*v,
cz + uz*u + vz*v)
}
#[inline]
fn push_seg(
q: &mut DepthQueue, cam: &Camera3D, color: u32,
mut ax:f32, mut ay:f32, mut az:f32,
mut bx:f32, mut by:f32, mut bz:f32,
) {
let near = -cam.zdist + 0.05;
let da = cam.depth(ax, ay, az);
let db = cam.depth(bx, by, bz);
if da <= near && db <= near { return; }
if da <= near {
let t = (near - da) / (db - da);
ax += t * (bx - ax);
ay += t * (by - ay);
az += t * (bz - az);
}
else if db <= near {
let t = (near - da) / (db - da);
bx = ax + t * (bx - ax);
by = ay + t * (by - ay);
bz = az + t * (bz - az);
}
let (sax, say, da2) = cam.project(ax, ay, az);
let (sbx, sby, db2) = cam.project(bx, by, bz);
q.push_line((da2 + db2) * 0.5 + DEPTH_BIAS, color, sax, say, sbx, sby);
}
#[inline]
pub fn cycle(phase: f32) -> u32 {
let r = (phase.sin() * 127.0 + 128.0) as u32;
let g = ((phase+2.094).sin() * 127.0 + 128.0) as u32;
let b = ((phase+4.189).sin() * 127.0 + 128.0) as u32;
(r<<16)|(g<<8)|b
}
pub fn draw_grid(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
cols:usize, rows:usize, cw:f32, ch:f32,
fr:f32, hue:f32,
) {
let hw = cols as f32 * cw * 0.5;
let hh = rows as f32 * ch * 0.5;
for i in 0..=cols {
let u = i as f32 * cw - hw;
let color = cycle(fr*0.03 + hue + u*0.15);
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u,-hh);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u, hh);
push_seg(q,cam,color, ax,ay,az, bx,by,bz);
}
for j in 0..=rows {
let v = j as f32 * ch - hh;
let color = cycle(fr*0.03 + hue + v*0.15);
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,-hw,v);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, hw,v);
push_seg(q,cam,color, ax,ay,az, bx,by,bz);
}
}
pub fn draw_rings(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
n_rings:usize, n_sides:usize, max_r:f32, twist:f32,
fr:f32, hue:f32,
) {
let step = max_r / n_rings as f32;
for ring in 0..n_rings {
let r = (ring+1) as f32 * step;
let rot = fr*0.012 + ring as f32 * twist;
let color = cycle(fr*0.025 + hue + ring as f32 * 0.52);
for side in 0..n_sides {
let a0 = rot + side as f32 * TAU / n_sides as f32;
let a1 = rot + (side+1) as f32 * TAU / n_sides as f32;
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a0.cos()*r, a0.sin()*r);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a1.cos()*r, a1.sin()*r);
push_seg(q,cam,color, ax,ay,az, bx,by,bz);
}
}
}
pub fn draw_star(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
n_points:usize, r_outer:f32, r_inner:f32, rot_speed:f32,
fr:f32, hue:f32,
) {
let total = n_points * 2;
let rot = fr * rot_speed;
let color = cycle(fr*0.025 + hue);
let mut pu = 0_f32; let mut pv = 0_f32; let mut first = true;
for i in 0..=total {
let a = rot + i as f32 * TAU / total as f32;
let r = if i % 2 == 0 { r_outer } else { r_inner };
let u = a.cos() * r;
let v = a.sin() * r;
if !first {
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, pu,pv);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u,v);
push_seg(q,cam,color, ax,ay,az, bx,by,bz);
}
pu=u; pv=v; first=false;
}
}
pub fn draw_spiral(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
n_turns:f32, max_r:f32, n_steps:usize,
fr:f32, hue:f32,
) {
let base_rot = fr * 0.008;
let mut prev: Option<(f32,f32,f32)> = None;
for step in 0..=n_steps {
let t = step as f32 / n_steps as f32;
let a = base_rot + t * n_turns * TAU;
let r = t * max_r;
let (wx,wy,wz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a.cos()*r, a.sin()*r);
let color = cycle(fr*0.02 + hue + t*5.0);
if let Some((px,py,pz)) = prev {
push_seg(q,cam,color, px,py,pz, wx,wy,wz);
}
prev = Some((wx,wy,wz));
}
}
pub fn draw_flower(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
radius:f32, n_sides:usize,
fr:f32, hue:f32,
) {
draw_ngon(q,cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, 0.0,0.0, radius, n_sides, fr, hue);
for i in 0..6 {
let a = i as f32 * TAU / 6.0;
let ou = a.cos() * radius;
let ov = a.sin() * radius;
draw_ngon(q,cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, ou,ov, radius, n_sides, fr, hue + i as f32*0.45);
}
}
fn draw_ngon(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
ou:f32, ov:f32, r:f32, n_sides:usize,
fr:f32, hue:f32,
) {
let color = cycle(fr*0.022 + hue);
let rot = fr * 0.009;
for i in 0..n_sides {
let a0 = rot + i as f32 * TAU / n_sides as f32;
let a1 = rot + (i+1) as f32 * TAU / n_sides as f32;
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, ou+a0.cos()*r, ov+a0.sin()*r);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, ou+a1.cos()*r, ov+a1.sin()*r);
push_seg(q,cam,color, ax,ay,az, bx,by,bz);
}
}
pub fn draw_halftone(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
cols:usize, rows:usize, cell_w:f32, cell_h:f32,
density:f32,
fr:f32, hue:f32,
) {
let hw = cols as f32 * cell_w * 0.5;
let hh = rows as f32 * cell_h * 0.5;
let half = density * 0.5;
for row in 0..rows {
let v_c = (row as f32 + 0.5) * cell_h - hh; for col in 0..cols {
let u_c = (col as f32 + 0.5) * cell_w - hw;
let phase = fr * 0.022 + hue
+ (col as f32 * 0.31 + row as f32 * 0.19); let color = cycle(phase);
let hu = cell_w * half;
let hv = cell_h * half;
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u_c-hu, v_c-hv);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u_c+hu, v_c+hv);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u_c+hu, v_c-hv);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u_c-hu, v_c+hv);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
}
}
}
pub fn draw_tessellated(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
cols:usize, rows:usize, cell:f32,
amplitude:f32, freq:f32,
fr:f32, hue:f32,
) {
let hw = cols as f32 * cell * 0.5;
let hh = rows as f32 * cell * 0.5;
let time = fr * 0.016;
let vert = |ci: usize, ri: usize| -> (f32, f32) {
let u0 = ci as f32 * cell - hw;
let v0 = ri as f32 * cell - hh;
let d = amplitude * cell
* (freq * (u0 * 0.5 + time)).sin()
* (freq * (v0 * 0.5 + time * 0.7)).cos();
(u0 + d, v0 - d)
};
for ri in 0..rows {
for ci in 0..cols {
let (u00,v00) = vert(ci, ri );
let (u10,v10) = vert(ci+1, ri );
let (u01,v01) = vert(ci, ri+1);
let (u11,v11) = vert(ci+1, ri+1);
let phase = time + hue + (ci as f32 * 0.28 + ri as f32 * 0.37);
let c0 = cycle(phase);
let c1 = cycle(phase + 1.047);
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u00,v00);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u10,v10);
let (ex,ey,ez) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u01,v01);
push_seg(q, cam, c0, ax,ay,az, bx,by,bz);
push_seg(q, cam, c0, bx,by,bz, ex,ey,ez);
push_seg(q, cam, c0, ex,ey,ez, ax,ay,az);
let (fx,fy,fz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u11,v11);
push_seg(q, cam, c1, bx,by,bz, fx,fy,fz);
push_seg(q, cam, c1, fx,fy,fz, ex,ey,ez);
}
}
}
pub fn draw_lotus(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
r_inner:f32, r_outer:f32, n_petals:usize,
fr:f32, hue:f32,
) {
let rot = fr * 0.006;
let spread = TAU / (n_petals as f32 * 2.0);
for i in 0..n_petals {
let a_mid = rot + i as f32 * TAU / n_petals as f32;
let a_left = a_mid - spread * 0.55;
let a_right = a_mid + spread * 0.55;
let color = cycle(fr * 0.020 + hue + i as f32 * (TAU / n_petals as f32));
let (alx,aly,alz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a_left.cos()*r_inner, a_left.sin()*r_inner);
let (arx,ary,arz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a_right.cos()*r_inner, a_right.sin()*r_inner);
let (tipx,tipy,tipz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a_mid.cos()*r_outer, a_mid.sin()*r_outer);
push_seg(q, cam, color, alx,aly,alz, tipx,tipy,tipz);
push_seg(q, cam, color, tipx,tipy,tipz, arx,ary,arz);
push_seg(q, cam, color, arx,ary,arz, alx,aly,alz);
}
let hub_color = cycle(fr * 0.020 + hue + 2.1);
for i in 0..24_usize {
let a0 = rot + i as f32 * TAU / 24.0;
let a1 = rot + (i+1) as f32 * TAU / 24.0;
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a0.cos()*r_inner, a0.sin()*r_inner);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a1.cos()*r_inner, a1.sin()*r_inner);
push_seg(q, cam, hub_color, ax,ay,az, bx,by,bz);
}
}
pub fn draw_chakra(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
r:f32, n_spokes:usize,
fr:f32, hue:f32,
) {
let rot = fr * 0.009;
let r_hub = r * 0.18;
let c_rim = cycle(fr * 0.018 + hue);
for i in 0..36_usize {
let a0 = rot + i as f32 * TAU / 36.0;
let a1 = rot + (i+1) as f32 * TAU / 36.0;
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a0.cos()*r, a0.sin()*r);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a1.cos()*r, a1.sin()*r);
push_seg(q, cam, c_rim, ax,ay,az, bx,by,bz);
}
let c_hub = cycle(fr * 0.018 + hue + 1.8);
for i in 0..12_usize {
let a0 = rot + i as f32 * TAU / 12.0;
let a1 = rot + (i+1) as f32 * TAU / 12.0;
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a0.cos()*r_hub, a0.sin()*r_hub);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a1.cos()*r_hub, a1.sin()*r_hub);
push_seg(q, cam, c_hub, ax,ay,az, bx,by,bz);
}
for i in 0..n_spokes {
let a_spoke = rot + i as f32 * TAU / n_spokes as f32;
let c_spoke = cycle(fr * 0.018 + hue + i as f32 * 0.72);
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a_spoke.cos()*r_hub, a_spoke.sin()*r_hub);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a_spoke.cos()*r, a_spoke.sin()*r);
push_seg(q, cam, c_spoke, ax,ay,az, bx,by,bz);
let a_tick = rot + (i as f32 + 0.5) * TAU / n_spokes as f32;
let r_tick0 = r * 0.87;
let (tx0,ty0,tz0) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a_tick.cos()*r_tick0, a_tick.sin()*r_tick0);
let (tx1,ty1,tz1) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a_tick.cos()*r, a_tick.sin()*r);
push_seg(q, cam, c_rim, tx0,ty0,tz0, tx1,ty1,tz1);
}
}
pub fn draw_yantra(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
n_layers:usize, max_r:f32,
fr:f32, hue:f32,
) {
let rot = fr * 0.003;
for layer in 0..n_layers {
let scale = max_r * (1.0 - layer as f32 * 0.20);
let c_up = cycle(fr * 0.015 + hue + layer as f32 * 0.65);
let c_down = cycle(fr * 0.015 + hue + layer as f32 * 0.65 + 3.14);
draw_equi_tri(q, cam, cx,cy,cz, ux,uy,uz, vx,vy,vz,
rot + 3.14159 * 0.5, scale, c_up);
draw_equi_tri(q, cam, cx,cy,cz, ux,uy,uz, vx,vy,vz,
rot + 3.14159 * 0.5 + TAU / 6.0,
scale * 0.92, c_down);
}
let sq = max_r * 1.40;
let sq2 = max_r * 1.18;
let gate = max_r * 0.28;
let c_sq = cycle(fr * 0.015 + hue + 5.5);
draw_bhupura(q, cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, sq, sq2, gate, c_sq);
}
fn draw_equi_tri(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
apex_rot:f32, r:f32, color:u32,
) {
for i in 0..3 {
let a0 = apex_rot + i as f32 * TAU / 3.0;
let a1 = apex_rot + (i+1) as f32 * TAU / 3.0;
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a0.cos()*r, a0.sin()*r);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, a1.cos()*r, a1.sin()*r);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
}
}
fn draw_bhupura(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
sq:f32, sq2:f32, gate:f32, color:u32,
) {
let oc = [(-sq,-sq),(sq,-sq),(sq,sq),(-sq,sq)];
for i in 0..4 {
let (u0,v0) = oc[i]; let (u1,v1) = oc[(i+1)%4];
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u0,v0);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u1,v1);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
}
let ic = [(-sq2,-sq2),(sq2,-sq2),(sq2,sq2),(-sq2,sq2)];
for i in 0..4 {
let (u0,v0) = ic[i]; let (u1,v1) = ic[(i+1)%4];
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u0,v0);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u1,v1);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
}
let posts: &[(f32,f32,f32,f32)] = &[
(-gate, -sq2, -gate, -sq ), ( gate, -sq2, gate, -sq ), (-gate, sq2, -gate, sq ), ( gate, sq2, gate, sq ), (-sq, -gate, -sq2, -gate), (-sq, gate, -sq2, gate), ( sq2, -gate, sq, -gate), ( sq2, gate, sq, gate), ];
for &(u0,v0,u1,v1) in posts {
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u0,v0);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz, u1,v1);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
}
}
static GLYPHS: &[&[(f32,f32,f32,f32)]] = &[
&[(-0.38, 0.12, 0.38, 0.12), ( 0.38, 0.12, 0.38,-0.30), (-0.08,-0.30, 0.38,-0.30), (-0.38, 0.12, -0.38, 0.44), (-0.38, 0.44, 0.06, 0.44)],
&[(-0.30, 0.30, 0.18, 0.30),
( 0.18, 0.30, 0.38, 0.00),
( 0.38, 0.00, 0.18,-0.30),
( 0.18,-0.30, -0.30,-0.30),
(-0.30,-0.30, -0.30, 0.30),
(-0.38, 0.44, 0.02, 0.44)],
&[(-0.32, 0.00, 0.00, 0.32),
( 0.00, 0.32, 0.32, 0.00),
( 0.32, 0.00, 0.00,-0.32),
( 0.00,-0.32, -0.32, 0.00),
( 0.00,-0.32, 0.00,-0.44)],
&[(-0.10, 0.44, 0.34, 0.22),
( 0.34, 0.22, 0.00, 0.00),
( 0.00, 0.00, -0.34,-0.22),
(-0.34,-0.22, 0.10,-0.44)],
&[( 0.34, 0.32, -0.18, 0.32),
(-0.18, 0.32, -0.36, 0.00),
(-0.36, 0.00, -0.18,-0.30),
(-0.18,-0.30, 0.18,-0.30),
( 0.18,-0.30, 0.18,-0.44)],
&[(-0.32, 0.30, 0.32, 0.30),
( 0.32, 0.30, 0.32, 0.00),
(-0.32, 0.00, 0.32, 0.00),
(-0.32, 0.30, -0.32, 0.00),
( 0.00, 0.00, 0.00,-0.44),
( 0.00,-0.44, 0.28,-0.44)],
&[(-0.26, 0.32, 0.26, 0.32),
( 0.26, 0.32, 0.26, 0.02),
( 0.26, 0.02, -0.26, 0.02),
(-0.26, 0.02, -0.26, 0.32),
( 0.00, 0.02, 0.00,-0.44)],
&[(-0.36, 0.32, 0.36, 0.32),
( 0.36, 0.32, 0.44, 0.00),
( 0.44, 0.00, 0.36,-0.30),
( 0.36,-0.30, 0.00,-0.44)],
];
pub fn draw_letter_rain(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
n_cols: usize, n_visible: usize,
col_w: f32, row_h: f32,
speed: f32,
fr: f32, hue: f32,
) {
let half_w = (n_cols as f32 * col_w) * 0.5;
let half_h = (n_visible as f32 * row_h) * 0.5;
let total_h = n_visible as f32 * row_h;
let lw = col_w * 0.36; let lh = row_h * 0.38;
for ci in 0..n_cols {
let u = ci as f32 * col_w - half_w + col_w * 0.5;
let speed_k = speed * (1.0 + 0.45 * (ci as f32 * 1.618_f32).sin());
let phase_k = ci as f32 * 2.73;
let hex_offset = if ci % 2 == 1 { row_h * 0.5 } else { 0.0 };
let scroll = (fr * speed_k + phase_k * 11.3 + hex_offset)
.rem_euclid(total_h);
for ri in 0..n_visible {
let v_from_top = (ri as f32 * row_h + scroll).rem_euclid(total_h);
let v = half_h - v_from_top;
if v < -half_h - row_h { continue; }
let brightness = 1.0 - v_from_top / total_h;
if brightness < 0.07 { continue; }
let time_slot = (fr * 0.028 + phase_k) as usize;
let glyph_idx = ci.wrapping_mul(7)
.wrapping_add(ri.wrapping_mul(13))
.wrapping_add(time_slot) % GLYPHS.len();
let color = cycle_dark(
fr * 0.018 + hue + ci as f32 * 0.33 + brightness * 1.8,
brightness,
);
for &(du0, dv0, du1, dv1) in GLYPHS[glyph_idx] {
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
u + du0*lw, v + dv0*lh);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
u + du1*lw, v + dv1*lh);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
}
}
}
}
#[inline]
fn cycle_dark(phase: f32, brightness: f32) -> u32 {
let b = brightness.clamp(0.0, 1.0);
let r = ((phase.sin() * 127.0 + 128.0) * b) as u32;
let g = (((phase+2.094).sin() * 127.0 + 128.0) * b) as u32;
let b_ch = (((phase+4.189).sin() * 127.0 + 128.0) * b) as u32;
(r.min(255)<<16)|(g.min(255)<<8)|b_ch.min(255)
}
pub fn draw_hyperbolic_uv(
q: &mut DepthQueue, cam: &Camera3D,
cx:f32,cy:f32,cz:f32,
ux:f32,uy:f32,uz:f32,
vx:f32,vy:f32,vz:f32,
max_r: f32, n_circles: usize, n_rays: usize,
fr: f32, hue: f32,
) {
let rot = fr * 0.004;
let c_ramp = 2.8_f32;
for k in 1..=n_circles {
let t = k as f32 / (n_circles + 1) as f32;
let r = max_r * (c_ramp * t).tanh() / c_ramp.tanh();
let color = cycle(fr * 0.014 + hue + k as f32 * 0.35);
let n_seg = 48usize;
for i in 0..n_seg {
let a0 = rot + i as f32 * TAU / n_seg as f32;
let a1 = rot + (i+1) as f32 * TAU / n_seg as f32;
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a0.cos()*r, a0.sin()*r);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a1.cos()*r, a1.sin()*r);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
}
}
let wobble = 0.07_f32;
for k in 0..n_rays {
let base_angle = rot * 0.5 + k as f32 * TAU / n_rays as f32;
let color = cycle(fr * 0.014 + hue + 1.8 + k as f32 * TAU / n_rays as f32);
let n_seg = 18usize;
for i in 0..n_seg {
let t0 = i as f32 / n_seg as f32;
let t1 = (i+1) as f32 / n_seg as f32;
let r0 = max_r * (c_ramp * t0).tanh() / c_ramp.tanh();
let r1 = max_r * (c_ramp * t1).tanh() / c_ramp.tanh();
let a0 = base_angle + wobble * (t0 * TAU).sin();
let a1 = base_angle + wobble * (t1 * TAU).sin();
let (ax,ay,az) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a0.cos()*r0, a0.sin()*r0);
let (bx,by,bz) = p2w(cx,cy,cz, ux,uy,uz, vx,vy,vz,
a1.cos()*r1, a1.sin()*r1);
push_seg(q, cam, color, ax,ay,az, bx,by,bz);
}
}
}