Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Perl

Point Inside 3D Convex Polygon in Perl

5.00/5 (1 vote)
6 Feb 2016CPOL 16.2K   60  
An algorithm to determine if a point is inside a 3D convex polygon for a given polygon vertices in Perl

Introduction

This is a Perl version of the original C++ algorithm which can be found here.

Background

Please refer to the original C++ algorithm here.

Using the Code

The algorithm is wrapped into a Perl module library folder GeoProc. The main test program LASProc reads point cloud data from a LAS file, then writes all inside points into a new LAS file. A LAS file is able to be displayed with a freeware FugroViewer.

To consume the GeoProc module library, prepare a GeoPolygonProc object first like this:

PERL
my $ptsArray = [$p1, $p2, $p3, $p4, $p5, $p6, $p7, $p8];
my $polygon  = new GeoPolygon($ptsArray);
my $procObj  = new GeoPolygonProc($polygon);

Here is how to check if a point is inside the polygon:

PERL
if ($x > $procObj->{x0} && $x < $procObj->{x1} &&
    $y > $procObj->{y0} && $x < $procObj->{y1} &&
    $z > $procObj->{z0} && $x < $procObj->{z1} )
    {
        if ($procObj->PointInside3DPolygon($x, $y, $z))

Here are all GeoProc modules:

Please refer to PHP version for the module explanation and comments.

GeoPoint.pm

PERL
#!/usr/bin/perl

package GeoPoint;

 use strict;
 use warning;

sub new
{
    my $class = shift;
    my $self = {
        x => shift,
        y => shift,
        z => shift,
    };   
    bless $self, $class;
    return $self;
}
    
use overload "+" => \&Add, "-" => \&Subtract;

sub Add
{
    my $p0 = shift;
    my $p1 = shift;
    return new GeoPoint($p0->{x} + $p1->{x}, $p0->{y} + $p1->{y}, $p0->{z} + $p1->{z});
}

sub Subtract
{
    my $p0 = shift;
    my $p1 = shift;
    return new GeoPoint($p0->{x} - $p1->{x}, $p0->{y} - $p1->{y}, $p0->{z} - $p1->{z});
}

1;

GeoVector.pm

PERL
#!/usr/bin/perl

package GeoVector;

 use strict;
 use warnings;

sub new
{
    my $class = shift;
    my $self = {
        p0 => shift,
        p1 => shift,        
    };   
    bless $self, $class;
    return $self;
}

sub getX
{
    my $self = shift;    
    return $self->{p1}->{x} - $self->{p0}->{x};
}

sub getY
{
    my $self = shift;        
    return $self->{p1}->{y} - $self->{p0}->{y};
}

sub getZ
{
    my $self = shift;    
    return $self->{p1}->{z} - $self->{p0}->{z};
}
    
use overload "*" => \&Multiple;

sub Multiple
{
    my $u = shift;
    my $v = shift;        
        
    my $x = $u->getY() * $v->getZ() - $u->getZ() * $v->getY();
    my $y = $u->getZ() * $v->getX() - $u->getX() * $v->getZ();
    my $z = $u->getX() * $v->getY() - $u->getY() * $v->getX();                
        
    my $p0 = $v->{p0};
    my $p1 = $p0 + new GeoPoint($x, $y, $z);
    
    return new GeoVector($p0, $p1);    
}

1;

GeoPlane.pm

PERL
#!/usr/bin/perl

package GeoPlane;

 use strict;
 use warnings;

 use GeoVector;

sub new
{
    my $class = shift;
    my $self = {
        a => shift,
        b => shift,
        c => shift,
        d => shift,
    };   
    bless $self, $class;
    return $self;
}   

# Create a GeoPlane from 3 GeoPoint.
# Static method to simulate constructor overloading.
# Usage: GeoPlane->Create($p0, $p1, $p2);
sub Create
{
    my $self = shift; # current object, GeoPlane

    my $p0 = shift; # parameter 1, GeoPoint
    my $p1 = shift; # parameter 2, GeoPoint
    my $p2 = shift; # parameter 3, GeoPoint        
    
    my $v = new GeoVector($p0, $p1);
    my $u = new GeoVector($p0, $p2);
    my $n = $u * $v;
    
    my $a = $n->getX();
    my $b = $n->getY();
    my $c = $n->getZ();                    
    my $d = -($a * $p0->{x} + $b * $p0->{y} + $c * $p0->{z});

    return new GeoPlane($a, $b, $c, $d);
}
    
use overload "neg" => \&Negative;

# Unary operator '-' overloading
sub Negative
{
    my $self = shift; # current object, GeoPlane
    return new GeoPlane(-$self->{a}, -$self->{b}, -$self->{c}, -$self->{d});    
}

# Instance method to simulate (GeoPlane * GeoPoint)
# Usage: $dis = $pl->Multiple(new GeoPoint($p0, $p1, $p2));
sub Multiple
{
    my $pl = shift; # current object, GeoPlane
    my $pt = shift; # parameter 1, GeoPoint
    return $pt->{x} * $pl->{a} + $pt->{y} * $pl->{b} + $pt->{z} * $pl->{c} + $pl->{d};    
}

1;

GeoPolygon.pm

PERL
#!/usr/bin/perl

package GeoPolygon;

 use strict;
 use warnings;

sub new
{
    my ($class, $pts) = @_;
                
    my $this = {};    
    
    my @pts_ref = @$pts;
    
    $this->{n} = scalar @pts_ref;
    
    $this->{v} = [];
    
    $this->{idx} = [];
            
    for(my $i=0; $i<$this->{n}; $i++)
    {        
        $this->{v}[$i] = $pts_ref[$i];        
        $this->{idx}[$i] = $i;        
    }
    
    bless $this, $class;        
    return $this;
}

1;

GeoFace.pm

PERL
#!/usr/bin/perl

package GeoFace;

 use strict;
 use warnings;

sub new
{
    my ($class, $pts, $ptsIdx) = @_;
                
    my $this = {};    
    
    my @pts_ref = @$pts;
    my @ptsIdx_ref = @$ptsIdx;
    
    $this->{n} = scalar @pts_ref;
    
    $this->{v} = [];
    
    $this->{idx} = [];
            
    for(my $i=0; $i<$this->{n}; $i++)
    {                
        push(@{$this->{v}}, $pts_ref[$i]);
        push(@{$this->{idx}}, $ptsIdx_ref[$i]);    
    }
    
    bless $this, $class;        
    return $this;
}

1;

Utility.pm

PERL
#!/usr/bin/perl

package Utility;

 use strict;
 use warnings;
 use experimental 'smartmatch';

 use constant false => 0;
 use constant true  => 1;

# check if 2D array [[]] arr2d contains 1d array [] arr1d.
sub ContainsArray
{
    my ($this, $arr2d, $arr1d) = @_;
                      
    my @arr2d_ref = @$arr2d;
    my @arr1d_ref = @$arr1d;
    
    my $count = scalar @arr2d_ref;        
    
    my @sortedItem = sort { $a <=> $b } @arr1d_ref;
    
    for (my $i = 0; $i < $count; $i++)
    {    
        my $temp = $arr2d_ref[$i];        
        my @currItem = sort { $a <=> $b } @$temp;                
        if(@currItem ~~ \@sortedItem)
        {            
            return true;
        }        
    }
    
    return false;
}

1;

GeoPolygonProc.pm

PERL
#!/usr/bin/perl

package GeoPolygonProc;

 use strict;
 use warnings;
 use Data::Dumper;

 use constant false => 0;
 use constant true  => 1;

my $MaxUnitMeasureError = 0.001;

sub new
{
    my ($class, $polygon) = @_;
                
    my $this = {};    
                        
    bless $this, $class;    

    $this->{polygon} = $polygon;
    $this->SetBoundary();    
    $this->SetMaxError();
    $this->SetFacePlanes();
    
    return $this;
}

sub SetBoundary
{
    my ($this) = @_;
    
    my $n = $this->{polygon}->{n};

    my $v = $this->{polygon}->{v};
    
    $this->{x0} = @$v[0]->{x};
    $this->{y0} = @$v[0]->{y};
    $this->{z0} = @$v[0]->{z};
    $this->{x1} = @$v[0]->{x};
    $this->{y1} = @$v[0]->{y};
    $this->{z1} = @$v[0]->{z};    
    
    for (my $i = 1; $i < $n; $i++)
    {
        if (@$v[$i]->{x} < $this->{x0}) {$this->{x0} = @$v[$i]->{x};}
        if (@$v[$i]->{y} < $this->{y0}) {$this->{y0} = @$v[$i]->{y};}
        if (@$v[$i]->{z} < $this->{z0}) {$this->{z0} = @$v[$i]->{z};}
        if (@$v[$i]->{x} > $this->{x1}) {$this->{x1} = @$v[$i]->{x};}
        if (@$v[$i]->{y} > $this->{y1}) {$this->{y1} = @$v[$i]->{y};}
        if (@$v[$i]->{z} > $this->{z1}) {$this->{z1} = @$v[$i]->{z};}        
    }       
}

sub SetMaxError
{    
    my ($this) = @_;
    $this->{maxDisError} = (abs($this->{x0}) + abs($this->{x1}) +
                            abs($this->{y0}) + abs($this->{y1}) +
                            abs($this->{z0}) + abs($this->{z1})) / 6 * $MaxUnitMeasureError; 
}

sub SetFacePlanes
{
    my ($this) = @_;
    
    # GeoFace array
    $this->{Faces} = [];

    # GeoPlane array
    $this->{FacePlanes} = [];
        
    $this->{NumberOfFaces} = 0;            
            
    # Polygon vertices array
    my $vertices = $this->{polygon}->{v};

    # number of vertices of polygon    
    my $n = $this->{polygon}->{n};    

    # vertices indexes 2d array for all faces,
    # face index is major dimension, face vertice index is minor dimension
    # vertices index is the original index value in the input polygon
    my @faceVerticeIndex = ();
        
    # face planes for all faces
    my $fpOutward = [];
    
    for(my $i=0; $i< $n; $i++)
    {     
        # triangle point #1
        my $p0 = @$vertices[$i];

        for(my $j= $i+1; $j< $n; $j++)
        {
            # triangle point #2
            my $p1 = @$vertices[$j];

            for(my $k = $j + 1; $k<$n; $k++)
            {            
                # triangle point #3
                my $p2 = @$vertices[$k];

                my $trianglePlane = GeoPlane->Create($p0, $p1, $p2);
            
                my $onLeftCount = 0;
                my $onRightCount = 0;

                # Indexes of points that are in same plane with the face triangle plane
                # Integer array
                my $pointInSamePlaneIndex = [];
    
                for(my $l = 0; $l < $n ; $l++)
                {                
                    # any points other than the 3 triangle points
                    if($l != $i && $l != $j && $l != $k)
                    {
                        my $pt = @$vertices[$l];

                        my $dis = $trianglePlane->Multiple($pt);
                                                
                        # next point is in the triangle plane
                        if(abs($dis) < $this->{maxDisError})
                        {                            
                            push(@$pointInSamePlaneIndex, $l);                                    
                        }
                        else
                        {
                            if($dis < 0)
                            {
                                $onLeftCount++;                                
                            }
                            else
                            {
                                $onRightCount++;
                            }
                        }
                    }
                }
        
                # This is a face for a CONVEX 3d polygon.
                # For a CONCAVE 3d polygon, this maybe not a face.
                if($onLeftCount == 0 || $onRightCount == 0)
                {
                    # Integer array
                    my $verticeIndexInOneFace = [];
                   
                    # triangle plane
                    push(@$verticeIndexInOneFace, $i);
                    push(@$verticeIndexInOneFace, $j);
                    push(@$verticeIndexInOneFace, $k);            
                    
                    my $m = scalar @$pointInSamePlaneIndex;

                    # there are other vertices in this triangle plane
                    if($m > 0)
                    {
                        for(my $p = 0; $p < $m; $p++)
                        {
                            push(@$verticeIndexInOneFace, @$pointInSamePlaneIndex[$p]);
                        }                        
                    }
                    
                    # if verticeIndexInOneFace is a new face,
                    # add it in the faceVerticeIndex list,
                    # add the trianglePlane in the face plane list fpOutward
                    if (!Utility->ContainsArray(\@faceVerticeIndex, $verticeIndexInOneFace))
                    {                    
                        push(@faceVerticeIndex, $verticeIndexInOneFace);

                        if ($onRightCount == 0)
                        {
                            push(@$fpOutward, $trianglePlane);
                        }
                        elsif ($onLeftCount == 0)
                        {
                            push(@$fpOutward, -$trianglePlane);
                        }
                    }
                }
                else
                {                    
                    # possible reasons:
                    # 1. the plane is not a face of a convex 3d polygon,
                    #    it is a plane crossing the convex 3d polygon.
                    # 2. the plane is a face of a concave 3d polygon
                }

            } # k loop
        } # j loop        
    } # i loop                        

    $this->{NumberOfFaces} = scalar @faceVerticeIndex;    
    
    for (my $i = 0; $i < $this->{NumberOfFaces}; $i++)
    {                        
        my $facePlane = new GeoPlane(@$fpOutward[$i]->{a}, @$fpOutward[$i]->{b},
                                     @$fpOutward[$i]->{c}, @$fpOutward[$i]->{d});
                                    
        #set FacePlanes
        push(@{$this->{FacePlanes}}, $facePlane);
        
        #GeoPoint array
        my $v = [];

        #Integer array
        my $idx = [];
        
        my $faceIdx = $faceVerticeIndex[$i];
        my $count = scalar @$faceIdx;
                
        for (my $j = 0; $j < $count; $j++)
        {                        
            push(@$idx, @$faceIdx[$j]);
            my $k = @$idx[$j];
            my $pt = new GeoPoint(@$vertices[$k]->{x}, @$vertices[$k]->{y}, @$vertices[$k]->{z});
            push(@$v, $pt);                                            
        }

        #set Faces
        my $face = new GeoFace($v, $idx);
        push(@{$this->{Faces}}, $face);
    }        
}

# public method to update max error
sub UpdateMaxError
{    
    my ($this, $maxError) = @_;
    $this->{maxDisError} = $maxError;       
}

# public method to check if point is inside 3d polygon
sub PointInside3DPolygon
{
    my ($this, $x, $y, $z) = @_;
    
    my $pt = new GeoPoint($x, $y, $z);
         
    for (my $i = 0; $i < $this->{NumberOfFaces}; $i++)
    {        
        my $pl = $this->{FacePlanes}[$i];
    
        my $dis = $pl->Multiple($pt);

        # If the point is in the same half space with normal vector for any face of the cube,
        # then it is outside of the 3D polygon        
        if ($dis > 0)
        {
            return false;
        }
    }

    # If the point is in the opposite half space with normal vector for all 6 faces,
    # then it is inside of the 3D polygon
    return true;            
}
    
1;

Here is the main test program.

LASProc.pl

PERL
#!/usr/bin/perl

 use strict;
 use warnings;
 use Data::Dumper;
 use Fcntl qw(:seek);    

 use lib '../GeoProc';
 use GeoPoint;
 use GeoVector;
 use GeoPlane;
 use GeoPolygon;
 use GeoFace;
 use Utility;
 use GeoPolygonProc;

# Create GeoPolygonProc object
my $p1 = new GeoPoint( - 27.28046,  37.11775,  - 39.03485);
my $p2 = new GeoPoint( - 44.40014,  38.50727,  - 28.78860);
my $p3 = new GeoPoint( - 49.63065,  20.24757,  - 35.05160);
my $p4 = new GeoPoint( - 32.51096,  18.85805,  - 45.29785);
my $p5 = new GeoPoint( - 23.59142,  10.81737,  - 29.30445);
my $p6 = new GeoPoint( - 18.36091,  29.07707,  - 23.04144);
my $p7 = new GeoPoint( - 35.48060,  30.46659,  - 12.79519);
my $p8 = new GeoPoint( - 40.71110,  12.20689,  - 19.05819);
my $ptsArray = [$p1, $p2, $p3, $p4, $p5, $p6, $p7, $p8];
my $polygon = new GeoPolygon($ptsArray);
my $procObj = new GeoPolygonProc($polygon);

# LAS File Process

my $rb;
my $wb;
my $wt;
my $value;

my $rbFileName = "../LASInput/cube.las";
my $wbFileName = "../LASOutput/cube_inside.las";
my $wtFileName = "../LASOutput/cube_inside.txt";

open($rb, "<", $rbFileName)
  || die "can't open $rbFileName: $!";
binmode($rb);

open($wb, ">", $wbFileName)
  || die "can't open $wbFileName: $!";
binmode($wb);

open($wt, ">", $wtFileName)
  || die "can't open $wtFileName: $!";

# read LAS header info

sysseek($rb, 96, 0);

sysread($rb, $value, 4);
my $offset_to_point_data_value = unpack('I', $value);
sysread($rb, $value, 4);
sysread($rb, $value, 1);
sysread($rb, $value, 2);
my $record_len = unpack('v', $value);
sysread($rb, $value, 4);
my $record_num = unpack('L', $value);

sysseek($rb, 131, 0);

sysread($rb, $value, 8);
my $x_scale = unpack('d', $value);
sysread($rb, $value, 8);
my $y_scale = unpack('d', $value);
sysread($rb, $value, 8);
my $z_scale = unpack('d', $value);
sysread($rb, $value, 8);
my $x_offset = unpack('d', $value);
sysread($rb, $value, 8);
my $y_offset = unpack('d', $value);
sysread($rb, $value, 8);
my $z_offset = unpack('d', $value);

# read LAS header
sysseek($rb, 0, 0);
sysread($rb, $value, $offset_to_point_data_value);

# write LAS header
syswrite($wb, $value);

my $insideCount = 0;

my ($i, $record_loc, $xi, $yi, $zi, $x, $y, $z);

for($i=0; $i<$record_num; $i++)
{                            
    $record_loc = $offset_to_point_data_value + $record_len * $i;
    sysseek($rb, $record_loc, 0);   
    sysread($rb, $value, 4);
    $xi = unpack('l', $value);
    sysread($rb, $value, 4);
    $yi = unpack('l', $value);
    sysread($rb, $value, 4);
    $zi = unpack('l', $value);
    $x = $xi * $x_scale + $x_offset;
    $y = $yi * $y_scale + $y_offset;
    $z = $zi * $z_scale + $z_offset;
    
    if ($x > $procObj->{x0} && $x < $procObj->{x1} &&
        $y > $procObj->{y0} && $x < $procObj->{y1} &&
        $z > $procObj->{z0} && $x < $procObj->{z1} )
    {
        if ($procObj->PointInside3DPolygon($x, $y, $z))
        {
            # read LAS Point record
            sysseek($rb, $record_loc, 0);   
            sysread($rb, $value, $record_len);
            
            # write LAS Point record
            syswrite($wb, $value);
                            
            # write text LAS Point record
            $wt->printf("%15.6f %15.6f %15.6f\n", $x, $y, $z);

            $insideCount++;
        }
    }    
}    

# update record_num in LAS header with actual writing point records number    
sysseek($wb, 107, 0);  
syswrite($wb, pack('I', $insideCount));
    
$rb->close();
$wb->close();
$wt->close();

Here is a module test program for all GeoProc module library functions and usages.

Test.pl

PERL
#!/usr/bin/perl

 use strict;
 use warnings;
 use Data::Dumper;

 use lib '../GeoProc';
 use GeoPoint;
 use GeoVector;
 use GeoPlane;
 use GeoPolygon;
 use GeoFace;
 use Utility;
 use GeoPolygonProc;

my $p1 = new GeoPoint( - 27.28046,  37.11775,  - 39.03485);
my $p2 = new GeoPoint( - 44.40014,  38.50727,  - 28.78860);
my $p3 = new GeoPoint( - 49.63065,  20.24757,  - 35.05160);
my $p4 = new GeoPoint( - 32.51096,  18.85805,  - 45.29785);
my $p5 = new GeoPoint( - 23.59142,  10.81737,  - 29.30445);
my $p6 = new GeoPoint( - 18.36091,  29.07707,  - 23.04144);
my $p7 = new GeoPoint( - 35.48060,  30.46659,  - 12.79519);
my $p8 = new GeoPoint( - 40.71110,  12.20689,  - 19.05819);
my $ptsArray = [$p1, $p2, $p3, $p4, $p5, $p6, $p7, $p8];
my $polygon = new GeoPolygon($ptsArray);

print "GeoPoint Test:\n";
my $pt = $p1 + $p2;
print(Dumper($pt));
print $pt->{x} .",". $pt->{y} .",". $pt->{z} ."\n";

print "GeoVector Test:\n";
my $v1 = new GeoVector($p1, $p2);
my $v2 = new GeoVector($p1, $p3);
my $v3 = $v2 * $v1;
printf("%.3f, %.3f, %.3f\n", $v3->getX(), $v3->getY(), $v3->getZ());

print "GeoPlane Test:\n";
my $pl = GeoPlane->Create($p1, $p2, $p3);
printf("%.3f, %.3f, %.3f, %.3f\n", $pl->{a}, $pl->{b}, $pl->{c}, $pl->{d});
$pl = new GeoPlane(1.0, 2.0, 3.0, 4.0);
printf("%.3f, %.3f, %.3f, %.3f\n", $pl->{a}, $pl->{b}, $pl->{c}, $pl->{d});
$pl = -$pl;
printf("%.3f, %.3f, %.3f, %.3f\n", $pl->{a}, $pl->{b}, $pl->{c}, $pl->{d});
my $dis = $pl->Multiple(new GeoPoint(1.0, 2.0, 3.0));
printf("%.3f\n", $dis);

print "GeoPolygon Test:\n";
for(my $i=0; $i<$polygon->{n}; $i++)
{
    print $polygon->{idx}[$i] .": (". $polygon->{v}[$i]->{x} .", ".
          $polygon->{v}[$i]->{y} .", ". $polygon->{v}[$i]->{z} .")\n";
}

print "GeoFace Test:\n";
my $faceVerticesArray = [$p1, $p2, $p3, $p4];
my $faceVerticeIndexArray = [1, 2, 3, 4];
my $face = new GeoFace($faceVerticesArray, $faceVerticeIndexArray);
for(my $i=0; $i<$face->{n}; $i++)
{
    print $face->{idx}[$i] .": (". $face->{v}[$i]->{x} .", ".
          $face->{v}[$i]->{y} .", ". $face->{v}[$i]->{z} .")\n";
}

print "Utility Test:\n";
my $arr1 = [1, 2, 3, 4];    
my $arr2 = [4, 5, 6, 7];    
my $arr2d = [];
push(@$arr2d, $arr1);
push(@$arr2d, $arr2);
my $arr3 = [2, 3, 1, 4];
my $arr4 = [2, 3, 1, 5];
my $b1 = Utility->ContainsArray($arr2d, $arr3);
my $b2 = Utility->ContainsArray($arr2d, $arr4);
print $b1 .", ". $b2 ."\n";
    
print "GeoPolygonProc Test:\n";
my $procObj = new GeoPolygonProc($polygon);
print $procObj->{x0} .", ". $procObj->{x1} .", ". $procObj->{y0} .", ".
      $procObj->{y1} .", ". $procObj->{z0} .", ". $procObj->{z1} ."\n";
printf("%.6f\n", $procObj->{maxDisError});
$procObj->UpdateMaxError(0.0125);
printf("%.6f\n", $procObj->{maxDisError});
print $procObj->{NumberOfFaces}."\n";
print "Face Planes:\n";
for(my $i=0; $i<$procObj->{NumberOfFaces}; $i++)
{
    my $facePlane = $procObj->{FacePlanes}[$i];
    printf("%d%s\n", $i+1, ": ".$facePlane->{a}.", ".$facePlane->{b}.", ".
                                $facePlane->{c}.", ".$facePlane->{d});
}
print "Faces:\n";
for(my $j=0; $j<$procObj->{NumberOfFaces}; $j++)
{
    printf("%s%d:\n", "Face ", $j + 1);
    my $face = $procObj->{Faces}[$j];            
    for(my $i=0; $i<$face->{n}; $i++)
    {
        printf("%d%s\n", $face->{idx}[$i], ": (". $face->{v}[$i]->{x} .", ".
                         $face->{v}[$i]->{y} .", ". $face->{v}[$i]->{z} .")");    
    }
}
my $insidePoint = new GeoPoint(-28.411750, 25.794500, -37.969000);
my $outsidePoint = new GeoPoint(-28.411750, 25.794500, -50.969000);
$b1 = $procObj->PointInside3DPolygon($insidePoint->{x}, $insidePoint->{y}, $insidePoint->{z});
$b2 = $procObj->PointInside3DPolygon($outsidePoint->{x}, $outsidePoint->{y}, $outsidePoint->{z});
print $b1.", ".$b2;

Here is the output of the module test program:

PERL
GeoPoint Test:
$VAR1 = bless( {
                 'x' => '-71.6806',
                 'z' => '-67.82345',
                 'y' => '75.62502'
               }, 'GeoPoint' );
-71.6806,75.62502,-67.82345
GeoVector Test:
-178.391, 160.814, -319.868
GeoPlane Test:
-178.391, 160.814, -319.868, -23321.631
1.000, 2.000, 3.000, 4.000
-1.000, -2.000, -3.000, -4.000
-18.000
GeoPolygon Test:
0: (-27.28046, 37.11775, -39.03485)
1: (-44.40014, 38.50727, -28.7886)
2: (-49.63065, 20.24757, -35.0516)
3: (-32.51096, 18.85805, -45.29785)
4: (-23.59142, 10.81737, -29.30445)
5: (-18.36091, 29.07707, -23.04144)
6: (-35.4806, 30.46659, -12.79519)
7: (-40.7111, 12.20689, -19.05819)
GeoFace Test:
1: (-27.28046, 37.11775, -39.03485)
2: (-44.40014, 38.50727, -28.7886)
3: (-49.63065, 20.24757, -35.0516)
4: (-32.51096, 18.85805, -45.29785)
Utility Test:
1, 0
GeoPolygonProc Test:
-49.63065, -18.36091, 10.81737, 38.50727, -45.29785, -12.79519
0.029235
0.012500
6
Face Planes:
1: -178.390887365, 160.8136689275, -319.8681191512, -23321.6310778083
2: 104.6099805132, 365.1940004963, 125.2599754664, -5811.8668695958
3: 342.39346482, -27.7903996799999, -204.924901278, 2372.9555463542
4: -342.393647417, 27.7906119191, 204.9249816848, -10372.9631493284
5: -104.609966618, -365.193886771, -125.2600697684, -2188.13771525579
6: 178.3908734698, -160.8139027544, 319.8683017482, 15321.6421625963
Faces:
Face 1:
0: (-27.28046, 37.11775, -39.03485)
1: (-44.40014, 38.50727, -28.7886)
2: (-49.63065, 20.24757, -35.0516)
3: (-32.51096, 18.85805, -45.29785)
Face 2:
0: (-27.28046, 37.11775, -39.03485)
1: (-44.40014, 38.50727, -28.7886)
5: (-18.36091, 29.07707, -23.04144)
6: (-35.4806, 30.46659, -12.79519)
Face 3:
0: (-27.28046, 37.11775, -39.03485)
3: (-32.51096, 18.85805, -45.29785)
4: (-23.59142, 10.81737, -29.30445)
5: (-18.36091, 29.07707, -23.04144)
Face 4:
1: (-44.40014, 38.50727, -28.7886)
2: (-49.63065, 20.24757, -35.0516)
6: (-35.4806, 30.46659, -12.79519)
7: (-40.7111, 12.20689, -19.05819)
Face 5:
2: (-49.63065, 20.24757, -35.0516)
3: (-32.51096, 18.85805, -45.29785)
4: (-23.59142, 10.81737, -29.30445)
7: (-40.7111, 12.20689, -19.05819)
Face 6:
4: (-23.59142, 10.81737, -29.30445)
5: (-18.36091, 29.07707, -23.04144)
6: (-35.4806, 30.46659, -12.79519)
7: (-40.7111, 12.20689, -19.05819)
1, 0

Points of Interest

TMTOWTDI means "There's more than one way to do it" according to Wall.

History

  • February 06, 2016: Initial date

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)