Graphics-Penplotter-GcodeXY
view release on metacpan or search on metacpan
lib/Graphics/Penplotter/GcodeXY.pm view on Meta::CPAN
else {
# Collinear case
$da = $dx * $dx + $dy * $dy;
if ( $da == 0 ) {
$d = _calc_sq_distance( $x1, $y1, $x2, $y2 );
}
else {
$d = ( ( $x2 - $x1 ) * $dx + ( $y2 - $y1 ) * $dy ) / $da;
if ( $d > 0 && $d < 1 ) {
# Simple collinear case, 1---2---3
# We can leave just two endpoints
return;
}
if ( $d <= 0 ) { $d = _calc_sq_distance( $x2, $y2, $x1, $y1 ) }
elsif ( $d >= 1 ) { $d = _calc_sq_distance( $x2, $y2, $x3, $y3 ) }
else {
$d = _calc_sq_distance( $x2, $y2, $x1 + $d * $dx, $y1 + $d * $dy );
}
}
if ( $d < $m_disttolscale ) {
push @m_points, ( $x2, $y2 );
lib/Graphics/Penplotter/GcodeXY.pm view on Meta::CPAN
else {
$k = 1.0 / $k;
$da1 = $x2 - $x1;
$da2 = $y2 - $y1;
$d2 = $k * ( $da1 * $dx + $da2 * $dy );
$da1 = $x3 - $x1;
$da2 = $y3 - $y1;
$d3 = $k * ( $da1 * $dx + $da2 * $dy );
if ( $d2 > 0 && $d2 < 1 && $d3 > 0 && $d3 < 1 ) {
# Simple collinear case, 1---2---3---4
# We can leave just two endpoints
return;
}
if ( $d2 <= 0 ) { $d2 = _calc_sq_distance( $x2, $y2, $x1, $y1 ) }
elsif ( $d2 >= 1 ) { $d2 = _calc_sq_distance( $x2, $y2, $x4, $y4 ) }
else {
$d2 = _calc_sq_distance(
$x2, $y2,
$x1 + $d2 * $dx,
$y1 + $d2 * $dy
);
lib/Graphics/Penplotter/GcodeXY.pm view on Meta::CPAN
my $self = shift;
@{ $self->{hsegments} } = ();
return 1;
}
# optimize the plotting of hatch lines
# a good strategy will significantly reduce the plotter's work
sub _hoptimize { # TODO
my $self = shift;
# See vecsort.c for a general procedure:
# - compare start and end points of all remaining L segments to endpoint of current segment
# and locate the nearest one
# - if it's the endpoint that's closest, reverse the nearest segment
# - first add M segment to new list, then nearest segment, remove from the normal list
# - flush the sorted list, empty the normal list
return 1;
}
#
# render the generated list of hatching segments
# we have only 'l' and 'm' segments here.
# We're working in device coordinates here
sub _flushHsegments {
lib/Graphics/Penplotter/GcodeXY.pm view on Meta::CPAN
}
#
# Warn if the pen ends up outside the page boundary
# We need DEVICE coordinates here for obvious reasons
#
sub _warn {
my ( $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;
}
lib/Graphics/Penplotter/GcodeXY.pm view on Meta::CPAN
# https://www.skytopia.com/project/articles/compsci/clipping.html
# This code was modified to remove a bug, then translated into Perl.
# use:
# ($x1, $y1, $x2, $y2, $info) = $obj->_LiangBarsky($botx, $boty, $topx, $topy, $x0src, $y0src, $x1src, $y1src);
# The first four parameters are the coordinates of the bottom left and
# top right corners of the rectangle.
# The last four parameters are the coordinates of the start and end of the line segment.
# Meaning of the 'info' return value:
# 1 entire line segment is inside boundary
# 2 line segment is completely outside boundary
# 3 starting point inside, but not endpoint
# 4 endpoint inside, but not starting point
# 5 neither startpoint nor endpoint inside, but other parts are
# The function returns the clipped line segment in the other variables, unless info
# is 2, in which case -1 is returned.
# This code is self contained, so suitable for inclusion elsewhere, and well tested
# (remove $self if necessary).
sub _LiangBarsky {
my ( $self, $botx, $boty, $topx, $topy, $x0src, $y0src, $x1src, $y1src ) = @_;
my $t0 = 0.0;
my $t1 = 1.0;
my $xdelta = $x1src - $x0src;
my $ydelta = $y1src - $y0src;
lib/Graphics/Penplotter/GcodeXY.pm view on Meta::CPAN
#----------------------------------------------------------------------------------
# SVG path: arc implementation
# converted from a javascript module found on github ('svgpath')
# Convert an arc to a sequence of cubic bézier curves
# the call was: new_segments = _a2c(x, y, nextX, nextY, s[4], s[5], s[1], s[2], s[3]);
# s = array of path values, 7 elements, s[0] = 'a' or 'A'
# everything converted to absolute
# (x,y) is current point, (nextx,nexty) is endpoint.
# Calculate an angle between two unit vectors.
# Since we measure angle between radii of circular arcs,
# we can use simplified math (without length normalization)
#
sub _unit_vector_angle {
my ( $ux, $uy, $vx, $vy ) = @_;
my $sign = ( $ux * $vy - $uy * $vx < 0 ) ? -1 : 1;
my $dot = $ux * $vx + $uy * $vy;
# Add this to work with arbitrary vectors:
$dot /= sqrt( $ux * $ux + $uy * $uy ) * sqrt( $vx * $vx + $vy * $vy );
# rounding errors, e.g. -1.0000000000000002 can screw up this
if ( $dot > 1.0 ) { $dot = 1.0; }
if ( $dot < -1.0 ) { $dot = -1.0; }
return $sign * acos($dot);
}
# Convert from endpoint to center parameterization,
# see http:#www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
# Return [cx, cy, theta1, delta_theta]
sub _get_arc_center {
my ( $x1, $y1, $x2, $y2, $fa, $fs, $rx, $ry, $sin_phi, $cos_phi ) = @_;
# Step 1.
#
# Moving an ellipse so origin will be the middlepoint between our two
# points. After that, rotate it to line up ellipse axes with coordinate
# axes.
my $x1p = $cos_phi * ( $x1 - $x2 ) / 2 + $sin_phi * ( $y1 - $y2 ) / 2;
( run in 0.203 second using v1.01-cache-2.11-cpan-0ffa90cfd1c )