Graphics-Penplotter-GcodeXY
view release on metacpan or search on metacpan
# 8. t1 is slightly negative but t2 is positive => returns t2
# E=(0.5, 0, 0), D=(1,0,0): t1 = -(0.5+1)/1=-1.5 (neg), t2=0.5 (pos)
my $t7 = ana('_ana_ray_cylinder', 0.5,0,0, 1,0,0, 0,0, 1);
ok defined $t7, 'ray_cylinder: exits cylinder when inside on axis';
ok abs($t7 - 0.5) < 1e-9, 'ray_cylinder: exit t=0.5 for inside shot';
}
# ==========================================================================
# SECTION 2: _ana_reflect
# ==========================================================================
{
# 1. Normal incidence on x+ face: D=(-1,0,0), N=(1,0,0) => reflect=(1,0,0)
my ($rx, $ry, $rz) = ana('_ana_reflect', -1,0,0, 1,0,0);
ok abs($rx - 1.0) < 1e-9, 'reflect: normal incidence rx=1';
ok abs($ry - 0.0) < 1e-9, 'reflect: normal incidence ry=0';
ok abs($rz - 0.0) < 1e-9, 'reflect: normal incidence rz=0';
# 2. Tangent ray unchanged: D=(0,1,0), N=(1,0,0) => D.N=0, reflect=(0,1,0)
my ($rx2,$ry2,$rz2) = ana('_ana_reflect', 0,1,0, 1,0,0);
ok abs($rx2 - 0.0) < 1e-9, 'reflect: tangent ray unchanged x';
ok abs($ry2 - 1.0) < 1e-9, 'reflect: tangent ray unchanged y';
# 3. 45-degree incidence: D=(-1,-1,0)/sqrt2, N=(1,0,0)
# D.N = -1/sqrt2; reflect = D - 2*(-1/sqrt2)*(1,0,0) = D + (sqrt2,0,0)
# = (-1/sqrt2 + sqrt2, -1/sqrt2, 0) = (1/sqrt2, -1/sqrt2, 0)
my $s2 = 1.0/sqrt(2);
my ($rx3,$ry3,$rz3) = ana('_ana_reflect', -$s2,-$s2,0, 1,0,0);
ok abs($rx3 - $s2) < 1e-9, 'reflect: 45-degree rx=1/sqrt2';
ok abs($ry3 - (-$s2)) < 1e-9, 'reflect: 45-degree ry=-1/sqrt2';
ok abs($rz3 - 0.0) < 1e-9, 'reflect: 45-degree rz=0';
# 4. Reflected vector has unit length
my $len = sqrt($rx3**2 + $ry3**2 + $rz3**2);
ok abs($len - 1.0) < 1e-9, 'reflect: output is unit length';
# 5. 3-D case: D=(-1,0,-1)/sqrt2 (pointing down and inward), N=(1,0,0)
# D.N = -1/sqrt2; reflect = (-1/sqrt2+sqrt2, 0, -1/sqrt2) = (1/sqrt2,0,-1/sqrt2)
my ($rx4,$ry4,$rz4) = ana('_ana_reflect', -$s2,0,-$s2, 1,0,0);
ok abs($rx4 - $s2) < 1e-9, 'reflect: 3-D case rx=1/sqrt2';
ok abs($rz4 - (-$s2)) < 1e-9, 'reflect: 3-D case rz=-1/sqrt2 (still going down)';
}
# ==========================================================================
# SECTION 3: _ana_build_config
# ==========================================================================
{
# Default observer: obs_angle=0, obs_dist=5*R, obs_height=5*R
my %cfg = ana('_ana_build_config', 0,0, 1);
ok abs($cfg{ex} - 5.0) < 1e-9, 'build_config: default ex=5';
ok abs($cfg{ey} - 0.0) < 1e-9, 'build_config: default ey=0';
ok abs($cfg{ez} - 5.0) < 1e-9, 'build_config: default ez=5';
# phi_fwd should point from (5,0) toward (0,0): that is angle pi
ok abs($cfg{phi_fwd} - $PI) < 1e-9, 'build_config: phi_fwd=pi for default obs';
# ang_rad: 2*asin(1/5)*0.9 ~ 2*0.20136*0.9 ~ 0.36245
my $expected_ang = 2.0 * atan2(0.2, sqrt(1-0.04)) * 0.90;
ok abs($cfg{ang_rad} - $expected_ang) < 1e-6,
'build_config: default ang_rad matches formula';
# beta0 = atan2(H, D) = atan2(5,5) = pi/4
ok abs($cfg{beta0} - $PI/4) < 1e-9, 'build_config: beta0=pi/4 for H=D';
# elev_rad = beta0 * 0.8
ok abs($cfg{elev_rad} - $PI/4 * 0.8) < 1e-9,
'build_config: elev_rad=beta0*0.8';
# Custom obs_angle=90 (observer from +y direction)
my %cfg2 = ana('_ana_build_config', 0,0, 1, obs_angle => 90);
ok abs($cfg2{ex} - 0.0) < 1e-6, 'build_config: obs_angle=90 ex~0';
ok abs($cfg2{ey} - 5.0) < 1e-6, 'build_config: obs_angle=90 ey=5';
# phi_fwd from (0,5) toward (0,0) = -pi/2
ok abs($cfg2{phi_fwd} - (-$PI/2)) < 1e-6,
'build_config: phi_fwd=-pi/2 for obs_angle=90';
# Custom obs_dist
my %cfg3 = ana('_ana_build_config', 0,0, 1, obs_dist => 10);
ok abs($cfg3{ex} - 10.0) < 1e-9, 'build_config: custom obs_dist=10';
# Non-origin cylinder centre
my %cfg4 = ana('_ana_build_config', 3,4, 2);
ok abs($cfg4{ex} - (3 + 10*cos(0))) < 1e-9,
'build_config: shifted cylinder ex=cx+obs_dist';
ok abs($cfg4{ey} - 4.0) < 1e-9, 'build_config: shifted cylinder ey=cy';
# Custom angle_range and elev_range in degrees
my %cfg5 = ana('_ana_build_config', 0,0, 1,
angle_range => 20, elev_range => 30);
ok abs($cfg5{ang_rad} - 20*$PI/180) < 1e-9, 'build_config: custom angle_range=20 deg';
ok abs($cfg5{elev_rad} - 30*$PI/180) < 1e-9, 'build_config: custom elev_range=30 deg';
# Croak when obs_dist <= R
eval { ana('_ana_build_config', 0,0, 5, obs_dist => 5) };
like $@, qr/obs_dist/i, 'build_config: croaks when obs_dist==R';
eval { ana('_ana_build_config', 0,0, 5, obs_dist => 3) };
like $@, qr/obs_dist/i, 'build_config: croaks when obs_dist<R';
# Croak for R<=0
eval { ana('_ana_build_config', 0,0, 0) };
like $@, qr/radius/i, 'build_config: croaks for R=0';
}
# ==========================================================================
# SECTION 4: _ana_transform_point
# ==========================================================================
# Use a fixed, analytically verified configuration:
# cylinder at origin, R=1, observer at (5,0,5), phi_fwd=pi
# Centre of image (s=0, t=0): looking at phi=pi, beta=pi/4
# Ray from (5,0,5) in dir (-cos45,0,-cos45) = (-s2,0,-s2)
# Cylinder hit at t=4*sqrt2: M=(1,0,1)
# Normal at M = (1,0,0)
# Reflected: (-s2,0,-s2) -> (s2,0,-s2) [z unchanged for vertical cylinder]
# Paper hit: t_paper = -1/(-s2) = sqrt2; P=(1+1,0,0) = (2,0)
{
my %cfg = ana('_ana_build_config', 0,0, 1);
# Image bounding box 0..100 x 0..100; centre maps to (s=0,t=0)
my ($px, $py) = ana('_ana_transform_point',
( run in 0.801 second using v1.01-cache-2.11-cpan-483215c6ad5 )