NAME

Graphics::Penplotter::GcodeXY::Swirl - Whirl (pursuit-curve) polygon generation for GcodeXY

SYNOPSIS

use Graphics::Penplotter::GcodeXY;

my $g = Graphics::Penplotter::GcodeXY->new(
    xsize => 200, ysize => 200, units => 'mm',
);

# Simple square whirl, 100 iterations, 20% advance per edge
$g->swirl(
    points     => [ 10,10,  190,10,  190,190,  10,190 ],
    d          => [ 20, 20, 20, 20 ],
    iterations => 100,
);

# Triangular whirl, varying d, stops when first edge reaches 1% of original
$g->swirl(
    points   => [ 100,10,  190,170,  10,170 ],
    d        => [ 15, 25, 10 ],
    min_size => 1,
);

# Counter-clockwise hexagonal whirl, two edges suppressed for visual effect
$g->swirl(
    points    => [ 100,10, 162,50, 162,130, 100,170, 38,130, 38,50 ],
    d         => [ (20) x 6 ],
    direction => $Graphics::Penplotter::GcodeXY::Swirl::SWIRL_CCW,
    draw      => [ 1, 0, 1, 1, 0, 1 ],
    min_size  => 0.5,
);

$g->output('whirl.gcode');

DESCRIPTION

A Role::Tiny role providing whirl (also called pursuit-curve polygon) generation for Graphics::Penplotter::GcodeXY.

A whirl is produced by iteratively constructing a series of nested polygons where each new vertex lies a fixed fractional distance along an edge of the enclosing polygon. The corners of successive polygons trace discrete approximations to logarithmic spirals, as described in:

Kerry Mitchell, "Fun with Whirls", Bridges 2015, pp. 175-182.

The role is composed automatically when Graphics::Penplotter::GcodeXY is loaded; no extra use statement is required in user code.

CONSTRUCTION ALGORITHM

Given a base polygon with vertices V[0] to V[n-1] and per-edge advance fractions f[0] to f[n-1] (converted from the supplied percentages):

  • For clockwise mode (direction => 0), new vertex i is placed at fraction f[i] of the way from V[i] towards V[(i+1) mod n].

  • For counter-clockwise mode (direction => 1), new vertex i is placed at fraction f[i] of the way from V[(i+1) mod n] towards V[i].

This produces a mirror-image whirl. The resulting nested polygon is drawn after each iteration. The base polygon is always drawn first.

TERMINATION

Iteration stops when the first condition that has been configured is met:

  1. Fixed iteration count (iterations => $n): exactly $n nested polygons are drawn inside the base (highest precedence).

  2. Size threshold (min_size => $pct): iteration stops when the length of the first edge of the current polygon falls to or below $pct percent of the corresponding edge of the base polygon. The default is 1.0 (one percent).

If neither is given, the size threshold of 1% is used.

METHODS

swirl(%args)

Draw a whirl from the given polygon. Named arguments:

points => \@pts (compulsory)

A reference to a flat array of vertex coordinates in alternating X, Y order: [x0,y0, x1,y1, ...] At least 3 vertices are required. Coordinates are in the current drawing units.

d => \@d (compulsory)

A reference to an array of advance percentages, one per edge (same count as vertices). Each value specifies how far along the corresponding edge the next polygon's vertex is placed. Values are percentages in the range 0 to 100. For example, 20 means 20% of the way along the edge.

When all values are equal to 50, consecutive polygons degenerate to straight lines (no visible spiral). Values close to 0 or 100 produce densely packed spirals; values close to 50 produce loosely spaced ones.

direction => 0|1 (optional, default 0)

Spiral direction. 0 ($SWIRL_CW) gives a clockwise whirl; 1 ($SWIRL_CCW) gives a counter-clockwise whirl.

draw => \@bool (optional, default all 1)

A reference to an array of boolean flags, one per edge, that controls whether each edge of every nested polygon is drawn. When all flags are true the implementation uses polygon() for efficiency; otherwise individual line() calls are made for the enabled edges. Setting some flags to false can produce striking visual effects (see Mitchell, Figures 6 and 7).

iterations => $n (optional)

Draw exactly $n nested polygons (not counting the base polygon). When given, this takes precedence over min_size.

min_size => $pct (optional, default 1.0)

Stop iterating once the length of the first edge of the current polygon has shrunk to $pct percent of the original first-edge length. Ignored when iterations is also given.

Returns 1 on success. Croaks on invalid input.

PACKAGE VARIABLES

$Graphics::Penplotter::GcodeXY::Swirl::SWIRL_CW

Constant 0: clockwise direction (the default).

$Graphics::Penplotter::GcodeXY::Swirl::SWIRL_CCW

Constant 1: counter-clockwise direction.

REQUIRED METHODS

_croak, line, polygon.

All of these are provided by the host class Graphics::Penplotter::GcodeXY.

SEE ALSO

Graphics::Penplotter::GcodeXY, Graphics::Penplotter::GcodeXY::Hatch

Kerry Mitchell, "Fun with Whirls", Bridges Conference 2015, pp. 175-182. https://archive.bridgesmathart.org/2015/bridges2015-175.html

AUTHOR

Albert Koelmans (albert.koelmans@googlemail.com)

LICENSE

Same terms as Perl itself.