NAME

Graphics::Penplotter::GcodeXY::Anamorphic - cylindrical mirror anamorphic projection for GcodeXY

SYNOPSIS

use Graphics::Penplotter::GcodeXY;

my $g = Graphics::Penplotter::GcodeXY->new(
    papersize => 'A4',
    units     => 'mm',
    outfile   => 'plot.gcode',
);

# Build any path -- lines, arcs, imported SVG, etc.
$g->importsvg('face.svg');           # or $g->line(...), etc.

# Distort the current segment path for a cylindrical mirror at (100,100)
# with radius 20 mm.  The path is replaced in-place and flushed.
$g->anamorphic(
    100, 100,            # cylinder centre (mm)
    20,                  # cylinder radius (mm)
    obs_dist   => 120,   # observer distance from axis (mm)
    obs_height => 120,   # observer height above paper (mm)
    obs_angle  => 0,     # observer azimuth (degrees, default 0 = from +x)
);

$g->output();

DESCRIPTION

A Role::Tiny role that adds the anamorphic method to Graphics::Penplotter::GcodeXY.

An anamorphic image is a distorted drawing which, when viewed from a specific vantage point via a curved mirror, appears undistorted. This role implements the cylindrical convex mirror variant.

The caller first populates the GcodeXY segment path using any drawing primitives (line, arc, importsvg, etc.). The anamorphic method then reads the existing path, uses its bounding box as the image space, projects every sample point through the cylindrical mirror model, writes the transformed path back into the segment buffer, and calls stroke to flush it to the output stream.

Physical model

The cylinder stands vertically (axis along z) with its base on the paper (z = 0). The observer is at world position

E = ( cx + D*cos(alpha), cy + D*sin(alpha), H )

where D = obs_dist, H = obs_height, and alpha = obs_angle converted to radians.

For every sample point of the source path the algorithm:

  1. Computes the 3-D viewing direction from E corresponding to the point's normalised position within the image bounding box.

  2. Finds where that ray first intersects the cylinder surface (the mirror point M).

  3. Reflects the incident direction off the cylinder's outward normal at M using the law of reflection.

  4. Follows the reflected ray until it hits the paper plane (z = 0).

  5. Records the resulting paper coordinate as a segment in the new path.

Image coordinate convention

The bounding box of the incoming segment path is treated as the image canvas:

  • Horizontally, the left edge (min x) maps to the observer's right side of the mirror and the right edge (max x) maps to the observer's left side. (A convex mirror reverses left and right.)

  • Vertically, the top edge (min y) maps to the upper part of the mirror and the bottom edge (max y) maps to the part closest to the paper.

METHODS

anamorphic($cx, $cy, $R [, %opts])

Replace the current segment path with its anamorphic distortion for a cylindrical mirror of radius $R centred at ($cx, $cy), then flush via stroke.

The bounding box of the existing path is used as the image extent. Segments whose sample points cannot be projected (ray misses the cylinder, or the reflected ray travels away from the paper) are silently dropped; path continuity is restored automatically before the next valid point.

Recognised options:

obs_dist (default 5 * R)

Horizontal distance from the observer to the cylinder axis, in drawing units. Must be greater than $R.

obs_height (default 5 * R)

Height of the observer's eye above the paper, in drawing units.

obs_angle (default 0)

Azimuthal direction from which the observer views the mirror, in degrees. 0 means the observer stands to the right of the cylinder (+x); 90 means from the top (+y), etc.

angle_range (default: 90% of the visible horizontal cone)

Total horizontal angular range of the image in degrees. Reducing this crops the image toward the centre of the mirror.

elev_range (default: 80% of the base elevation angle)

Total vertical angular range of the image in degrees.

step (default 1.0)

Maximum distance (in drawing units) between consecutive sample points along a segment. Smaller values give smoother distorted curves at the cost of more output moves.

Internal helpers (callable from tests)

The following subroutines are plain functions (not methods) and can be called as Graphics::Penplotter::GcodeXY::Anamorphic::_ana_foo(...):

_ana_ray_cylinder($ex,$ey,$ez, $dx,$dy,$dz, $cx,$cy,$R)

Find the smallest positive t where the ray E + t*D intersects the vertical cylinder of radius R centred at (cx, cy). Returns t or undef.

_ana_reflect($dx,$dy,$dz, $nx,$ny,$nz)

Reflect unit vector D off the surface with outward unit normal N. Returns (rx, ry, rz).

_ana_build_config($cx, $cy, $R, %opts)

Build and return the configuration hash used by _ana_transform_point.

_ana_transform_point($u,$v, $u0,$v0,$u1,$v1, %cfg)

Map one image-space point to paper coordinates. Returns (px, py) or ().

_ana_resample_segment($x0,$y0, $x1,$y1, $step)

Subdivide the segment from (x0,y0) to (x1,y1) into intervals of at most $step. Returns a list of [x,y] arrayrefs; start point NOT included, end point IS included.

SEE ALSO

Graphics::Penplotter::GcodeXY

AUTHOR

Albert Koelmans (albert.koelmans@googlemail.com)

LICENSE

Same terms as Perl itself.