Introduction
This is a C# 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 C# class library GeoProc
. The main test program CSLASProc
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 algorithm class library, construct a polygon instance first like this:
GeoPolygon polygonInst = new GeoPolygon(CubeVertices);
Then, construct the main process instance:
GeoPolygonProc procInst = new GeoPolygonProc(polygonInst);
Main procedure to check if a point (ax, ay, az
) is inside the CubeVertices
:
procInst.PointInside3DPolygon(ax, ay, az)
Here are the classes in the GeoProc
class library:
GeoPoint.cs
public class GeoPoint
{
public double x {get;set;}
public double y {get;set;}
public double z {get;set;}
public GeoPoint(){}
public GeoPoint(double x, double y, double z)
{
this.x=x;
this.y=y;
this.z=z;
}
public static GeoPoint operator +(GeoPoint p0, GeoPoint p1)
{
return new GeoPoint(p0.x + p1.x, p0.y + p1.y, p0.z + p1.z);
}
}
GeoVector.cs
class GeoVector
{
GeoPoint p0;
GeoPoint p1;
public double x {get{return (this.p1.x - this.p0.x);}}
public double y {get{return (this.p1.y - this.p0.y);}}
public double z {get{return (this.p1.z - this.p0.z);}}
public GeoVector() {}
public GeoVector(GeoPoint p0, GeoPoint p1)
{
this.p0 = p0;
this.p1 = p1;
}
public static GeoVector operator *(GeoVector u, GeoVector v)
{
double x = u.y * v.z - u.z * v.y;
double y = u.z * v.x - u.x * v.z;
double z = u.x * v.y - u.y * v.x;
GeoPoint p0 = v.p0;
GeoPoint p1 = p0 + new GeoPoint(x, y, z);
return new GeoVector(p0, p1);
}
}
GeoPlane.cs
class GeoPlane
{
double a;
double b;
double c;
double d;
public GeoPlane() {}
public GeoPlane(double a, double b, double c, double d)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public GeoPlane(GeoPoint p0, GeoPoint p1, GeoPoint p2)
{
GeoVector v = new GeoVector(p0, p1);
GeoVector u = new GeoVector(p0, p2);
GeoVector n = u * v;
double a = n.x;
double b = n.y;
double c = n.z;
double d = -(a * p0.x + b * p0.y + c * p0.z);
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public double A { get { return this.a; } }
public double B { get { return this.b; } }
public double C { get { return this.c; } }
public double D { get { return this.d; } }
public static GeoPlane operator -(GeoPlane pl)
{
return new GeoPlane(-pl.a, -pl.b, -pl.c, -pl.d);
}
public static double operator *(GeoPoint pt, GeoPlane pl)
{
return (pt.x * pl.a + pt.y * pl.b + pt.z * pl.c + pl.d);
}
}
GeoFace.cs
class GeoFace
{
List<GeoPoint> v;
List<int> idx;
public int N { get { return this.v.Count; } }
public List<GeoPoint> V { get { return this.v; } }
public List<int> I { get { return this.idx; } }
public GeoFace(){}
public GeoFace(List<GeoPoint> p, List<int> idx)
{
this.v = new List<GeoPoint>();
this.idx = new List<int>();
for(int i=0;i<p.Count;i++)
{
this.v.Add(p[i]);
this.idx.Add(idx[i]);
}
}
}
GeoPolygon.cs
public class GeoPolygon
{
List<GeoPoint> v;
List<int> idx;
public int N { get { return this.v.Count; } }
public List<GeoPoint> V { get { return this.v; } }
public List<int> I { get { return this.idx; } }
public GeoPolygon(){}
public GeoPolygon(List<GeoPoint> p)
{
this.v = new List<GeoPoint>();
this.idx = new List<int>();
for(int i=0;i<p.Count;i++)
{
this.v.Add(p[i]);
this.idx.Add(i);
}
}
}
GeoPolygonProc.cs
public class GeoPolygonProc
{
double MaxUnitMeasureError = 0.001;
double X0, X1, Y0, Y1, Z0, Z1;
List<GeoFace> Faces;
List<GeoPlane> FacePlanes;
int NumberOfFaces;
double MaxDisError;
public GeoPolygonProc(){}
#region public methods
public GeoPolygonProc(GeoPolygon polygonInst)
{
List<GeoFace> faces = new List<GeoFace>();
List<GeoPlane> facePlanes = new List<GeoPlane>();
int numberOfFaces = 0;
double x0 = 0, x1 = 0, y0 = 0, y1 = 0, z0 = 0, z1 = 0;
this.Get3DPolygonBoundary(polygonInst, ref x0, ref x1, ref y0, ref y1, ref z0, ref z1);
double maxDisError = this.Get3DPolygonUnitError(polygonInst);
this.GetConvex3DFaces(polygonInst, maxDisError, faces, facePlanes, ref numberOfFaces);
this.X0 = x0;
this.X1 = x1;
this.Y0 = y0;
this.Y1 = y1;
this.Z0 = z0;
this.Z1 = z1;
this.Faces = faces;
this.FacePlanes = facePlanes;
this.NumberOfFaces = numberOfFaces;
this.MaxDisError = maxDisError;
}
public void GetBoundary(ref double xmin, ref double xmax,
ref double ymin, ref double ymax,
ref double zmin, ref double zmax)
{
xmin = this.X0;
xmax = this.X1;
ymin = this.Y0;
ymax = this.Y1;
zmin = this.Z0;
zmax = this.Z1;
}
public bool PointInside3DPolygon(double x, double y, double z)
{
GeoPoint P = new GeoPoint(x, y, z);
return this.PointInside3DPolygon(P, this.FacePlanes, this.NumberOfFaces);
}
#endregion
#region private methods
double Get3DPolygonUnitError(GeoPolygon polygon)
{
List<GeoPoint> vertices = polygon.V;
int n = polygon.N;
double measureError = 0;
double xmin = 0, xmax = 0, ymin = 0, ymax = 0, zmin = 0, zmax =0;
this.Get3DPolygonBoundary(polygon,
ref xmin, ref xmax, ref ymin, ref ymax, ref zmin, ref zmax);
measureError = ((Math.Abs(xmax) + Math.Abs(xmin) + Math.Abs(ymax) + Math.Abs(ymin) +
Math.Abs(zmax) + Math.Abs(zmin)) / 6 * MaxUnitMeasureError);
return measureError;
}
void Get3DPolygonBoundary(GeoPolygon polygon,
ref double xmin, ref double xmax,
ref double ymin, ref double ymax,
ref double zmin, ref double zmax)
{
List<GeoPoint> vertices = polygon.V;
int n = polygon.N;
xmin = xmax = vertices[0].x;
ymin = ymax = vertices[0].y;
zmin = zmax = vertices[0].z;
for (int i = 1; i < n; i++)
{
if (vertices[i].x < xmin) xmin = vertices[i].x;
if (vertices[i].y < ymin) ymin = vertices[i].y;
if (vertices[i].z < zmin) zmin = vertices[i].z;
if (vertices[i].x > xmax) xmax = vertices[i].x;
if (vertices[i].y > ymax) ymax = vertices[i].y;
if (vertices[i].z > zmax) zmax = vertices[i].z;
}
}
bool PointInside3DPolygon(double x, double y, double z,
List<GeoPlane> facePlanes, int numberOfFaces)
{
GeoPoint P = new GeoPoint(x, y, z);
return this.PointInside3DPolygon(P, facePlanes, numberOfFaces);
}
bool PointInside3DPolygon(GeoPoint P, List<GeoPlane> facePlanes, int numberOfFaces)
{
for (int i = 0; i < numberOfFaces; i++)
{
double dis = P * facePlanes[i];
if (dis > 0)
{
return false;
}
}
return true;
}
void GetConvex3DFaces(GeoPolygon polygon, double maxError,
List<GeoFace> faces, List<GeoPlane> facePlanes, ref int numberOfFaces)
{
List<GeoPoint> vertices = polygon.V;
int n = polygon.N;
List<List<int>> faceVerticeIndex = new List<List<int>>();
List<GeoPlane> fpOutward = new List<GeoPlane>();
for(int i=0; i< n; i++)
{
GeoPoint p0 = vertices[i];
for(int j= i+1; j< n; j++)
{
GeoPoint p1 = vertices[j];
for(int k = j + 1; k<n; k++)
{
GeoPoint p2 = vertices[k];
GeoPlane trianglePlane = new GeoPlane(p0, p1, p2);
int onLeftCount = 0;
int onRightCount = 0;
List<int> pointInSamePlaneIndex = new List<int>();
for(int l = 0; l < n ; l ++)
{
if(l != i && l != j && l != k)
{
GeoPoint p = vertices[l];
double dis = p * trianglePlane;
if(Math.Abs(dis) < maxError)
{
pointInSamePlaneIndex.Add(l);
}
else
{
if(dis < 0)
{
onLeftCount ++;
}
else
{
onRightCount ++;
}
}
}
}
if(onLeftCount == 0 || onRightCount == 0)
{
List<int> verticeIndexInOneFace = new List<int>();
verticeIndexInOneFace.Add(i);
verticeIndexInOneFace.Add(j);
verticeIndexInOneFace.Add(k);
int m = pointInSamePlaneIndex.Count;
if(m > 0)
{
for(int p = 0; p < m; p ++)
{
verticeIndexInOneFace.Add(pointInSamePlaneIndex[p]);
}
}
if (!Utility.ContainsList(faceVerticeIndex, verticeIndexInOneFace))
{
faceVerticeIndex.Add(verticeIndexInOneFace);
if (onRightCount == 0)
{
fpOutward.Add(trianglePlane);
}
else if (onLeftCount == 0)
{
fpOutward.Add(-trianglePlane);
}
}
}
else
{
}
}
}
}
numberOfFaces = faceVerticeIndex.Count;
for (int i = 0; i < numberOfFaces; i++)
{
facePlanes.Add(new GeoPlane(fpOutward[i].A, fpOutward[i].B, fpOutward[i].C,
fpOutward[i].D));
List<GeoPoint> gp = new List<GeoPoint>();
List<int> vi = new List<int>();
for (int j = 0; j < faceVerticeIndex[i].Count; j++)
{
vi.Add(faceVerticeIndex[i][j]);
gp.Add( new GeoPoint(vertices[vi[j]].x,
vertices[vi[j]].y,
vertices[vi[j]].z));
}
faces.Add(new GeoFace(gp, vi));
}
}
#endregion
}
Points of Interest
The C# version is faster than the C++ version.
History
- January 09, 2016: Initial version