Graphics-Penplotter-GcodeXY
view release on metacpan or search on metacpan
lib/Graphics/Penplotter/GcodeXY.pm view on Meta::CPAN
my ( $px, $py );
if ( $mode eq 'slow' ) { $opcode = 'l' }
# get the current point (user coords) - soon to be updated
my ( $cx, $cy ) = $self->currentpoint();
# get the paper coords of the current point and save them
@pointold = $self->_u_to_p( $cx, $cy );
#debugging
$px = $pointold[0];
$py = $pointold[1];
# get the device coords of the current point
( $pointold[0], $pointold[1] ) = $self->_p_to_d(@pointold);
# set the new current point (the destination)
$self->currentpoint( $x, $y );
# get the paper coords of the new current point
@point = $self->_u_to_p( $x, $y );
$px = $point[0];
$py = $point[1];
# get the device coords of the new current point
( $point[0], $point[1] ) = $self->_p_to_d(@point);
# add up the distance
# warn if out of device bounds for reporting and postscript generation
$self->_warn( $point[0], $point[1] );
if ( $self->{check} ) {
if ( $point[0] > $self->{maxx} ) { $self->{maxx} = $point[0] }
if ( $point[1] > $self->{maxy} ) { $self->{maxy} = $point[1] }
if ( $point[0] < $self->{minx} && $point[0] > 0.0 ) {
$self->{minx} = $point[0];
}
if ( $point[1] < $self->{miny} && $point[1] > 0.0 ) {
$self->{miny} = $point[1];
}
}
# finally, generate the instruction
$self->_addpath( $opcode, $pointold[0], $pointold[1], $point[0], $point[1] );
return 1;
}
#
# generate a slow move (pen on paper)
#
sub _genslowmove ($self, $x, $y) {
$self->_genmove( 'slow', $x, $y );
return 1;
}
#
# generate a fast move (pen off paper)
#
sub _genfastmove ($self, $x, $y) {
$self->_genmove( 'fast', $x, $y );
return 1;
}
#
# Warn if the pen ends up outside the page boundary
# We need DEVICE coordinates here for obvious reasons
#
sub _warn ($self, $x, $y) {
my ( $x0clip, $y0clip, $x1clip, $y1clip, $info );
if ( !$self->{warn} ) { return 0 }
# we check only the endpoint for now.
# just assume the line started at (0.1, 0.1)
if ( ( $x < 0 ) || ( $y < 0 ) ) {
print STDOUT "Out of bound: ($x,$y)" . $EOL;
return 0;
}
( $x0clip, $y0clip, $x1clip, $y1clip, $info ) =
$self->_LiangBarsky( 0, 0, $self->{xsize}, $self->{ysize}, 0.1, 0.1, $x, $y );
if ( $info != 1 ) {
print STDOUT "Out of bound: ($x,$y)" . $EOL;
}
return 1;
}
# sizes in postscript units (pt)
my @a_sizes = (
{ name => 'A4', width => 595, height => 842 },
{ name => 'A3', width => 842, height => 1191 },
{ name => 'A2', width => 1191, height => 1684 },
{ name => 'A1', width => 1684, height => 2384 },
{ name => 'A0', width => 2384, height => 3370 },
{ name => '2A0', width => 3370, height => 4768 },
{ name => '4A0', width => 4768, height => 6741 },
);
# check if a design fits landscape
sub _checkl ($self) {
my $y = $self->{maxx} * $I2P; # swap for landscape
my $x = $self->{maxy} * $I2P;
my $best_fit = 'size too big for 4A0 landscape!';
foreach my $size (@a_sizes) {
# find the smallest size that fits
if (($x <= $size->{width} && $y <= $size->{height}) ||
($y <= $size->{width} && $x <= $size->{height})) {
$best_fit = $size->{name};
last;
}
}
print STDOUT "best fit landscape: $best_fit" . $EOL;
return 1;
}
# check if a design fits portrait
sub _checkp ($self) {
my $x = $self->{maxx} * $I2P;
my $y = $self->{maxy} * $I2P;
my $best_fit = 'size too big for 4A0 portrait!';
foreach my $size (@a_sizes) {
# find the smallest size that fits
if (($x <= $size->{width} && $y <= $size->{height}) ||
($y <= $size->{width} && $x <= $size->{height})) {
$best_fit = $size->{name};
last;
}
}
print STDOUT "best fit portrait: $best_fit" . $EOL;
return 1;
}
# parsing of instruction
sub _parse ($self, $ss) {
lib/Graphics/Penplotter/GcodeXY.pm view on Meta::CPAN
=item get_projection()
Return the 4x4 projection matrix stored by the most recent
C<set_perspective()> or C<set_frustum()> call, or C<undef> if none has been
set. The matrix is an arrayref of four arrayrefs (row-major).
=back
=head2 Numeric configuration
=over 4
=item set_tolerance($eps), get_tolerance()
Set/get the floating-point equality tolerance (default 1e-9).
=item set_units($units)
Store a units tag (e.g. C<'mm'>); no automatic scaling is applied.
=item set_coordinate_convention(handedness =E<gt> ..., euler_order =E<gt> ...)
Store convention tags for downstream use.
=back
=head2 Mesh representation
All solid primitives that return a mesh use the structure:
{ verts => \@v, faces => \@f }
where C<@v> is an array of C<[$x,$y,$z]> position arrayrefs and C<@f> is
an array of C<[$i0,$i1,$i2]> triangle index arrayrefs. Winding order is
counter-clockwise when viewed from the outside (right-hand normal pointing
outward).
=head1 ANAMORPHIC METHODS
An I<anamorphic image> is a distorted drawing which, when viewed from a
specific vantage point via a curved mirror, appears undistorted. The
methods in this section implement the cylindrical convex mirror variant.
The caller first builds a segment path using any drawing primitives (including
C<importsvg>), then calls C<anamorphic> to replace that path with its
distorted counterpart such that an observer at the configured viewpoint sees
the original image when looking at the mirror.
See L<Graphics::Penplotter::GcodeXY::Anamorphic> for the full description
of the physical model and image coordinate convention.
=over 4
=item anamorphic($cx, $cy, $R [, %opts])
Replace the current segment path with its anamorphic distortion for a
cylindrical mirror of radius C<$R> centred at C<($cx, $cy)>, then flush the
path via C<stroke>.
The intended image is whatever is already in the segment path when this method
is called. The bounding box of the existing drawable segment endpoints is
used as the image extent. Each endpoint is independently projected onto the
paper via the cylindrical mirror model; segments whose endpoints cannot be
projected are dropped, and path continuity is maintained automatically.
The C<$cx>, C<$cy>, C<$R>, and observer parameters must be expressed in the
same coordinate space as the segment path (device coordinates as used
internally by GcodeXY). For typical plots with no active transform this is
equivalent to the drawing unit.
Options:
=over 4
=item C<obs_dist> (default 5*R)
Horizontal distance from the observer to the cylinder axis. Must exceed C<$R>.
=item C<obs_height> (default 5*R)
Height of the observer's eye above the paper.
=item C<obs_angle> (default 0)
Azimuthal viewing direction in degrees. 0 = observer stands to the right of
the mirror (+x direction); 90 = from the top (+y), etc.
=item C<angle_range> (default: 90% of visible cone)
Total horizontal angular span of the image in degrees.
=item C<elev_range> (default: 80% of base elevation)
Total vertical angular span of the image in degrees.
=item C<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.
=back
=back
=head1 SWIRL METHODS
A I<swirl> (also called I<pursuit-curve polygon>) 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.
The role is composed automatically when L<Graphics::Penplotter::GcodeXY> is
loaded; no extra C<use> statement is required in user code.
See L<Graphics::Penplotter::GcodeXY::Swirl> for the construction algorithm
and termination conditions.
=over 4
=item swirl(%args)
Draw a whirl from the given polygon. Named arguments:
( run in 0.629 second using v1.01-cache-2.11-cpan-5735350b133 )