Commit 4096b7a7 authored by Nguyeng Hoang Giang's avatar Nguyeng Hoang Giang

new

parents a2498efb 5979d65f
Pipeline #1481 canceled with stages
......@@ -8,29 +8,29 @@ version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
ext{
ext {
version = [
'javacv' : '1.5.3',
'opencv' : '4.3.0',
'javacv': '1.5.3',
'opencv': '4.3.0',
]
}
dependencies {
List axisExGroups = [ 'org.bytedeco.javacpp-presets']
List axisExModules = [ 'ffmpeg' , 'artoolkitplus', 'ffmpeg-platform']
implementation('org.bytedeco:opencv:4.5.1-1.5.5:android-x86')
implementation('org.bytedeco:opencv:4.3.0-1.5.3:android-x86_64')
implementation(group :'org.bytedeco', name: 'javacv-platform', version: '1.5.5'){
axisExGroups.each { exclude group: "$it" }
axisExModules.each {exclude module: "$it"}
List axisExGroups = ['org.bytedeco.javacpp-presets']
List axisExModules = ['ffmpeg', 'artoolkitplus', 'ffmpeg-platform', 'artoolkitplus-platform',
'flandmark', 'flandmark-platform', 'flycapture', 'flycapture-platform',
'leptonica', 'leptonica-platform', 'libdc1394', 'libdc1394-platform',
'libfreenect2', 'libfreenect2-platform', 'libfreenect', 'libfreenect-platform',
'librealsense2', 'librealsense2-platform', 'librealsense', 'librealsense-platform',
'tesseract', 'tesseract-platform', 'videoinput', 'videoinput-platform','javacv', 'javacv-platform']
implementation(group: 'org.bytedeco', name: 'javacv-platform', version: '1.5.5') {
axisExGroups.each { exclude group: "$it" }
axisExModules.each { exclude module: "$it" }
}
implementation('org.apache.commons:commons-lang3:3.12.0')
// testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
// testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
test {
......
......@@ -2,42 +2,67 @@ package Entity;
public class Face {
protected static final int DELETED = 3;
protected static final int NON_CONVEX = 2;
protected static final int VISIBLE = 1;
protected double area;
protected HalfEdge he0;
protected int mask = VISIBLE;
protected Face next;
public static final int DELETED = 3;
public static final int NON_CONVEX = 2;
public static final int VISIBLE = 1;
public double area;
public HalfEdge he0;
public int mark = VISIBLE;
public Face next;
protected int numVerts;
protected Vertex outside;
public Vertex outside;
protected double planeOffset;
private Point3D centroid;
private Vector3D normal;
private Point3d centroid;
private Vector3d normal;
public Face() {
normal = new Vector3D();
centroid = new Point3D();
mask = VISIBLE;
normal = new Vector3d();
centroid = new Point3d();
mark = VISIBLE;
}
public static Face create(Vertex[] vtxArray, int[] indices) {
return null;
Face face = new Face();
HalfEdge hePrev = null;
for (int i = 0; i < indices.length; i++) {
HalfEdge he = new HalfEdge(vtxArray[indices[i]], face);
if (hePrev != null) {
he.setPrev(hePrev);
hePrev.setNext(he);
} else {
face.he0 = he;
}
hePrev = he;
}
face.he0.setPrev(hePrev);
hePrev.setNext(face.he0);
// compute the normal and offset
face.computeNormalAndCentroid();
return face;
}
public static Face createTriangle(Vertex v0, Vertex v1, Vertex v2) {
return null;
return createTriangle(v0, v1, v2, 0);
}
/**
* Constructs a triangule Face from vertices v0, v1, and v2.
*
* @param v0 first vertex
* @param v1 second vertex
* @param v2 third vertex
* @param minArea
* @return
* @param v0 first vertex
* @param v1 second vertex
* @param v2 third vertex
*/
public static Face createTriangle(Vertex v0, Vertex v1, Vertex v2, double minArea) {
Face face = new Face();
......@@ -54,15 +79,12 @@ public class Face {
face.he0 = he0;
//compute the normal and offset
face.computeNormalAndCentroid();
// compute the normal and offset
face.computeNormalAndCentroid(minArea);
return face;
}
/**
* Compute Centroid
*/
public void computeCentroid(Point3D centroid) {
public void computeCentroid(Point3d centroid) {
centroid.setZero();
HalfEdge he = he0;
do {
......@@ -72,12 +94,12 @@ public class Face {
centroid.scale(1 / (double) numVerts);
}
public void computeNormal(Vector3D normal) {
public void computeNormal(Vector3d normal) {
HalfEdge he1 = he0.next;
HalfEdge he2 = he1.next;
Point3D p0 = he0.head().pnt;
Point3D p2 = he1.head().pnt;
Point3d p0 = he0.head().pnt;
Point3d p2 = he1.head().pnt;
double d2x = p2.x - p0.x;
double d2y = p2.y - p0.y;
......@@ -105,15 +127,16 @@ public class Face {
he2 = he2.next;
numVerts++;
}
area = normal.norm();
area = normal.norm()*0.5;
normal.scale(1 / area);
}
public void computeNormal(Vector3D normal, double minArea) {
public void computeNormal(Vector3d normal, double minArea) {
computeNormal(normal);
if (area < minArea) {
//make the normal more robust by remove components parallel to the longest edge
// make the normal more robust by removing
// components parallel to the longest edge
HalfEdge hedgeMax = null;
double lenSqrMax = 0;
......@@ -127,8 +150,8 @@ public class Face {
hedge = hedge.next;
} while (hedge != he0);
Point3D p2 = hedgeMax.head().pnt;
Point3D p1 = hedgeMax.tail().pnt;
Point3d p2 = hedgeMax.head().pnt;
Point3d p1 = hedgeMax.tail().pnt;
double lenMax = Math.sqrt(lenSqrMax);
double ux = (p2.x - p1.x) / lenMax;
double uy = (p2.y - p1.y) / lenMax;
......@@ -143,20 +166,21 @@ public class Face {
}
/**
* Compute the distance from a pont p to the plane of this face
* Computes the distance from a point p to the plane of this face.
*
* @param p the poin
* @param p the point
* @return distance from the point to the plane
*/
public double distanceToPlane(Point3D p) {
public double distanceToPlane(Point3d p) {
return normal.x * p.x + normal.y * p.y + normal.z * p.z - planeOffset;
}
/**
* Finds the half-edge within this face which have tail vt and head vh
* Finds the half-edge within this face which has tail <code>vt</code> and
* head <code>vh</code>.
*
* @param vt tail point
* @param vh heat point
* @param vh head point
* @return the half-edge, or null if none is found.
*/
public HalfEdge findEdge(Vertex vt, Vertex vh) {
......@@ -170,14 +194,14 @@ public class Face {
return null;
}
public Point3D getCentroid() {
public Point3d getCentroid() {
return centroid;
}
/**
* Gets the i-th half-edge associated with the face
* Gets the i-th half-edge associated with the face.
*
* @param i the half-edge index, in the range 0-2
* @param i the half-edge index, in the range 0-2.
* @return the half-edge
*/
public HalfEdge getEdge(int i) {
......@@ -197,7 +221,12 @@ public class Face {
return he0;
}
public Vector3D getNormal() {
/**
* Returns the normal of the plane associated with this face.
*
* @return the planar normal
*/
public Vector3d getNormal() {
return normal;
}
......@@ -229,7 +258,8 @@ public class Face {
int numDiscarded = 0;
discarded[numDiscarded++] = oppFace;
oppFace.mask = DELETED;
oppFace.mark = DELETED;
HalfEdge hedgeOpp = hedgeAdj.getOpposite();
HalfEdge hedgeAdjPrev = hedgeAdj.prev;
......@@ -257,7 +287,7 @@ public class Face {
he0 = hedgeAdjNext;
}
//handle the half edges at the head
// handle the half edges at the head
Face discardedFace;
discardedFace = connectHalfEdges(hedgeOppPrev, hedgeAdjNext);
......@@ -265,7 +295,7 @@ public class Face {
discarded[numDiscarded++] = discardedFace;
}
//handle the half edges at the tail
// handle the half edges at the tail
discardedFace = connectHalfEdges(hedgeAdjPrev, hedgeOppNext);
if (discardedFace != null) {
discarded[numDiscarded++] = discardedFace;
......@@ -273,31 +303,31 @@ public class Face {
computeNormalAndCentroid();
checkConsistency();
return numDiscarded;
}
public int getNumVerts() {
public int numVertices() {
return numVerts;
}
public void triangulate(FaceList newFaces, double minArea) {
HalfEdge hedge;
if (getNumVerts() < 4) {
if (numVertices() < 4) {
return;
}
Vertex v0 = he0.head();
hedge = he0.next;
HalfEdge oppPrev = hedge.opposite;
Face face0 = null;
for (hedge = hedge.next; hedge != he0.prev; hedge = hedge.next) {
Face face = createTriangle(v0, hedge.prev.head(), hedge.head(), minArea);
face.he0.next.setOpposite(oppPrev);
face.he0.prev.setPrev(hedge.opposite);
face.he0.prev.setOpposite(hedge.opposite);
oppPrev = face.he0;
newFaces.add(face);
if (face0 == null) {
......@@ -319,19 +349,21 @@ public class Face {
for (Face face = face0; face != null; face = face.next) {
face.checkConsistency();
}
}
/**
* return the squared area of the triangle defined by the half edge hedge- and the point at the head of hedge1
* return the squared area of the triangle defined by the half edge hedge0
* and the point at the head of hedge1.
*
* @param hedge0
* @param hedge1
* @return
*/
public double areaSquare(HalfEdge hedge0, HalfEdge hedge1) {
Point3D p0 = hedge0.tail().pnt;
Point3D p1 = hedge0.head().pnt;
Point3D p2 = hedge1.head().pnt;
public double areaSquared(HalfEdge hedge0, HalfEdge hedge1) {
Point3d p0 = hedge0.tail().pnt;
Point3d p1 = hedge0.head().pnt;
Point3d p2 = hedge1.head().pnt;
double dx1 = p1.x - p0.x;
double dy1 = p1.y - p0.y;
......@@ -344,6 +376,7 @@ public class Face {
double x = dy1 * dz2 - dz1 * dy2;
double y = dz1 * dx2 - dx1 * dz2;
double z = dx1 * dy2 - dy1 * dx2;
return x * x + y * y + z * z;
}
......@@ -358,8 +391,7 @@ public class Face {
he = he.next;
} while (he != he0);
if (numv != numVerts) {
throw new RuntimeException("face " + getVertexString() + " numVerts" +
numVerts + " shoule be " + numv);
throw new InternalErrorException("face " + getVertexString() + " numVerts=" + numVerts + " should be " + numv);
}
}
......@@ -370,29 +402,32 @@ public class Face {
}
private Face connectHalfEdges(HalfEdge hedgePrev, HalfEdge hedge) {
Face discardFace = null;
Face discardedFace = null;
if (hedgePrev.oppositeFace() == hedge.oppositeFace()) { // then there is
// a redundant
// edge that we
// can get rid
// off
//have a redundant edge that can get rid off
if (hedgePrev.oppositeFace() == hedge.oppositeFace()) {
Face oppFace = hedge.oppositeFace();
HalfEdge hedgeOpp;
if (hedgePrev == he0) {
he0 = hedge;
}
if (oppFace.getNumVerts() == 3) {
//can get rid of the apposite face altogether
if (oppFace.numVertices() == 3) { // then we can get rid of the
// opposite face altogether
hedgeOpp = hedge.getOpposite().prev.getOpposite();
oppFace.mask = DELETED;
discardFace = oppFace;
oppFace.mark = DELETED;
discardedFace = oppFace;
} else {
hedgeOpp = hedge.getOpposite().next;
if (oppFace.he0 == hedgeOpp.prev) {
oppFace.he0 = hedgeOpp;
}
hedgeOpp.prev = hedgeOpp.prev.prev;
hedgeOpp.prev.next = hedgeOpp;
}
......@@ -402,40 +437,40 @@ public class Face {
hedge.opposite = hedgeOpp;
hedgeOpp.opposite = hedge;
//oppFace was modified, so need to recompute
// oppFace was modified, so need to recompute
oppFace.computeNormalAndCentroid();
} else {
hedgePrev.next = hedge;
hedge.prev = hedgePrev;
}
return discardFace;
return discardedFace;
}
void checkConsistency() {
//do a santiy check on the face
public void checkConsistency() {
// do a sanity check on the face
HalfEdge hedge = he0;
double maxd = 0;
int numv = 0;
if (numVerts < 3) {
throw new RuntimeException("degenerate face: " + getVertexString());
throw new InternalErrorException("degenerate face: " + getVertexString());
}
do {
HalfEdge hedgeOpp = hedge.getOpposite();
if (hedgeOpp == null) {
throw new RuntimeException("face " + getVertexString() + ": " + "unreflected half edge " + hedge.getVertexString());
throw new InternalErrorException("face " + getVertexString() + ": " + "unreflected half edge " + hedge.getVertexString());
} else if (hedgeOpp.getOpposite() != hedge) {
throw new RuntimeException("face " + getVertexString() + ": " + "opposite half edge " + hedgeOpp.getVertexString() + " has opposite "
throw new InternalErrorException("face " + getVertexString() + ": " + "opposite half edge " + hedgeOpp.getVertexString() + " has opposite "
+ hedgeOpp.getOpposite().getVertexString());
}
if (hedgeOpp.head() != hedge.tail() || hedge.head() != hedgeOpp.tail()) {
throw new RuntimeException("face " + getVertexString() + ": " + "half edge " + hedge.getVertexString() + " reflected by " + hedgeOpp.getVertexString());
throw new InternalErrorException("face " + getVertexString() + ": " + "half edge " + hedge.getVertexString() + " reflected by " + hedgeOpp.getVertexString());
}
Face oppFace = hedgeOpp.face;
if (oppFace == null) {
throw new RuntimeException("face " + getVertexString() + ": " + "no face on half edge " + hedgeOpp.getVertexString());
} else if (oppFace.mask == DELETED) {
throw new RuntimeException("face " + getVertexString() + ": " + "opposite face " + oppFace.getVertexString() + " not on hull");
throw new InternalErrorException("face " + getVertexString() + ": " + "no face on half edge " + hedgeOpp.getVertexString());
} else if (oppFace.mark == DELETED) {
throw new InternalErrorException("face " + getVertexString() + ": " + "opposite face " + oppFace.getVertexString() + " not on hull");
}
double d = Math.abs(distanceToPlane(hedge.head().pnt));
if (d > maxd) {
......@@ -446,7 +481,8 @@ public class Face {
} while (hedge != he0);
if (numv != numVerts) {
throw new RuntimeException("face " + getVertexString() + " numVerts=" + numVerts + " should be " + numv);
throw new InternalErrorException("face " + getVertexString() + " numVerts=" + numVerts + " should be " + numv);
}
}
}
......@@ -6,14 +6,14 @@ public class FaceList {
private Face tail;
/**
* Clears this list
* Clears this list.
*/
public void clear() {
head = tail = null;
}
/**
* Adds a vertex to the end of this list
* Adds a vertex to the end of this list.
*/
public void add(Face vtx) {
if (head == null) {
......@@ -30,7 +30,7 @@ public class FaceList {
}
/**
* Returns true if this list is empty
* Returns true if this list is empty.
*/
public boolean isEmpty() {
return head == null;
......
......@@ -2,56 +2,56 @@ package Entity;
public class HalfEdge {
/**
* The vertex associated with the heat of this half-edge
*/
protected Vertex vertex;
/**
* Triangular face associated with this half-edge
* Triangular face associated with this half-edge.
*/
protected Face face;
public Face face;
/**
* Next haft-edge in the triangle
* Next half-edge in the triangle.
*/
protected HalfEdge next;
public HalfEdge next;
/**
* Previous half-edge in the reiangle
* Previous half-edge in the triangle.
*/
protected HalfEdge prev;
public HalfEdge prev;
/**
* Half-edge associated with the opposite triangle adjacent to this edge
* Half-edge associated with the opposite triangle adjacent to this edge.
*/
protected HalfEdge opposite;
public HalfEdge() {
}
public HalfEdge opposite;
/**
* Constructs a HalfEdge with head vertex and left-hand triangular face
* Constructs a HalfEdge with head vertex <code>v</code> and left-hand
* triangular face <code>f</code>.
*
* @param vertex head vertex
* @param face left-hand triangular face
* @param v head vertex
* @param f left-hand triangular face
*/
public HalfEdge(Vertex vertex, Face face) {
this.vertex = vertex;
this.face = face;
public HalfEdge(Vertex v, Face f) {
vertex = v;
face = f;
}
public HalfEdge() {
}
/**
* Gets the triangular face located to the left of this half-edge
* Sets the value of the next edge adjacent (counter-clockwise) to this one
* within the triangle.
*
* @return left-hand triangular face
* @param edge next adjacent edge
*/
public Face getFace() {
return face;
public void setNext(HalfEdge edge) {
next = edge;
}
/**
* Gets the value of the next edge adjacent (counter-clockwise) to this one within the triangle
* Gets the value of the next edge adjacent (counter-clockwise) to this one
* within the triangle.
*
* @return next adjacent edge
*/
......@@ -60,16 +60,18 @@ public class HalfEdge {
}
/**
* Sets the value of the next edge adjacent (counter-clockwise) to this one within the triangle
* Sets the value of the previous edge adjacent (clockwise) to this one
* within the triangle.
*
* @param next next adjacent edge
* @param edge previous adjacent edge
*/
public void setNext(HalfEdge next) {
this.next = next;
public void setPrev(HalfEdge edge) {
prev = edge;
}
/**
* Gets the value of the previous edge adjacent (clockwise) to this one within the triangle
* Gets the value of the previous edge adjacent (clockwise) to this one
* within the triangle.
*
* @return previous adjacent edge
*/
......@@ -78,16 +80,16 @@ public class HalfEdge {
}
/**
* Sets the value of the previous edge adjacent (clockwise) to this one within the triangle
* Returns the triangular face located to the left of this half-edge.
*
* @param prev previous adjacent edge
* @return left-hand triangular face
*/
public void setPrev(HalfEdge prev) {
this.prev = prev;
public Face getFace() {
return face;
}
/**
* Returns the half-edge opposite to this half-edge
* Returns the half-edge opposite to this half-edge.
*
* @return opposite half-edge
*/
......@@ -96,16 +98,17 @@ public class HalfEdge {
}
/**
* Sets the half-edge opposite to this half-edge
* Sets the half-edge opposite to this half-edge.
*
* @param opposite opposite half-edge
* @param edge opposite half-edge
*/
public void setOpposite(HalfEdge opposite) {
this.opposite = opposite;
public void setOpposite(HalfEdge edge) {
opposite = edge;
edge.opposite = this;
}
/**
* Returns the head vertex associated with this half-edge
* Returns the head vertex associated with this half-edge.
*
* @return head vertex
*/
......@@ -114,16 +117,16 @@ public class HalfEdge {
}
/**
* Returns the tail vertex associate with this half-edge
* Returns the tail vertex associated with this half-edge.
*
* @return
* @return tail vertex
*/
public Vertex tail() {
return prev != null ? prev.vertex : null;
}
/**
* Returns the opposite triangular face associate with this half-edge
* Returns the opposite triangular face associated with this half-edge.
*
* @return opposite triangular face
*/
......@@ -132,7 +135,8 @@ public class HalfEdge {
}
/**
* Produces a string identifying this half-edge by the point index values of its tail and head vertices
* Produces a string identifying this half-edge by the point index values of
* its tail and head vertices.
*
* @return identifying string
*/
......@@ -145,7 +149,7 @@ public class HalfEdge {
}
/**
* Returns the length of this half-edge
* Returns the length of this half-edge.
*
* @return half-edge length
*/
......@@ -158,9 +162,9 @@ public class HalfEdge {
}
/**
* Returns the length squared of this half-edge
* Returns the length squared of this half-edge.
*
* @return half-edge squared length
* @return half-edge length squared
*/
public double lengthSquared() {
if (tail() != null) {
......@@ -170,4 +174,5 @@ public class HalfEdge {
}
}
}
package Entity;
public class InternalErrorException extends RuntimeException {
public InternalErrorException(String msg) {
super(msg);
}
}
package Entity;
public class Point3D extends Vector3D{
/**
* Create a Point3D amd initializes it to zero
*/
public Point3D() {
}
/**
* Creates a Point3D by copy a vector
* @param v
*/
public Point3D(Vector3D v) {
super(v);
}
/**
* Create a Point3D with the supplied element values
* @param x
* first element
* @param y
* second element
* @param z
* third element
*/
public Point3D(double x, double y, double z) {
super(x, y, z);
}
}
package Entity;
public class Point3d extends Vector3d {
/**
* Creates a Point3d and initializes it to zero.
*/
public Point3d() {
}
/**
* Creates a Point3d by copying a vector
*
* @param v vector to be copied
*/
public Point3d(Vector3d v) {
set(v);
}
/**
* Creates a Point3d with the supplied element values.
*
* @param x first element
* @param y second element
* @param z third element
*/
public Point3d(double x, double y, double z) {
set(x, y, z);
}
public Point3d clone() {
return new Point3d(x, y, z);
}
}
package Entity;
public class Vector3D {
public class Vector3d {
static private final double DOUBLE_PREC = 2.2204460492503131e-16;//Precision of a double
/**
* Precision of a double.
*/
static private final double DOUBLE_PREC = 2.2204460492503131e-16;
/**
* First element
*/
public double x;
public double x;//first element
public double y;//second element
public double z;//third element
/**
* Second element
*/
public double y;
//Creates a 3-vector and initializes its elements to 0
public Vector3D() {
/**
* Third element
*/
public double z;
/**
* Creates a 3-vector and initializes its elements to 0.
*/
public Vector3d() {
}
/**
* Creates a 3-vector by copy existing one
* Creates a 3-vector by copying an existing one.
*
* @param v vector to be copied
*/
public Vector3D(Vector3D v) {
public Vector3d(Vector3d v) {
set(v);
}
/**
* Creates a 3-vector with the supplied element values
* Creates a 3-vector with the supplied element values.
*
* @param x first element
* @param y second element
* @param z third element
*/
public Vector3D(double x, double y, double z) {
public Vector3d(double x, double y, double z) {
set(x, y, z);
}
/**
* Gets a single element of this vector. Element 0, 1, and 2 correspond to x,y and z
* Gets a single element of this vector. Elements 0, 1, and 2 correspond to
* x, y, and z.
*
* @param i element index
* @return element value throws ArrayIndexOutOfBoundsException if i is not in range 0 to 2
* @return element value throws ArrayIndexOutOfBoundsException if i is not
* in the range 0 to 2.
*/
public double get(int i) {
switch (i) {
......@@ -57,11 +75,12 @@ public class Vector3D {
/**
* Sets a single element of this vector. Elements 0, 1, and 2 correspond to
* x, y, and z
* x, y, and z.
*
* @param i element index
* @param value element value
* @return element value throws ArrayIndexOutOfBoundsException if i is not in the range 0 to 2
* @return element value throws ArrayIndexOutOfBoundsException if i is not
* in the range 0 to 2.
*/
public void set(int i, double value) {
switch (i) {
......@@ -84,86 +103,64 @@ public class Vector3D {
}
/**
* Sets the values of this vector to those of v1
* Sets the values of this vector to those of v1.
*
* @param v1 vector whose values are copied
*/
public void set(Vector3D v1) {
this.x = v1.x;
this.y = v1.y;
this.z = v1.z;
public void set(Vector3d v1) {
x = v1.x;
y = v1.y;
z = v1.z;
}
/**
* Sets the values of the vector to the prescribed values
*
* @param x value for first element
* @param y value for second element
* @param z value for third element
*/
public void set(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Sets the elements of this vector to zero
*/
public void setZero() {
x = 0;
y = 0;
z = 0;
}
/**
* Adds vector v1 to v2 and places the result in this vector
* Adds vector v1 to v2 and places the result in this vector.
*
* @param v1 left-hand vector
* @param v2 right-hand vector
*/
public void add(Vector3D v1, Vector3D v2) {
public void add(Vector3d v1, Vector3d v2) {
x = v1.x + v2.x;
y = v1.y + v2.y;
z = v1.z + v2.z;
}
/**
* Adds this vector to v and places the result in this vector
* Adds this vector to v1 and places the result in this vector.
*
* @param v right-hand vector
* @param v1 right-hand vector
*/
public void add(Vector3D v) {
x += v.x;
y += v.y;
z += v.z;
public void add(Vector3d v1) {
x += v1.x;
y += v1.y;
z += v1.z;
}
/**
* Subs vector v1 from v2 and places the result in this vector
* Subtracts vector v1 from v2 and places the result in this vector.
*
* @param v1 left-hand vector
* @param v2 right-hand vector
*/
public void sub(Vector3D v1, Vector3D v2) {
public void sub(Vector3d v1, Vector3d v2) {
x = v1.x - v2.x;
y = v1.y - v2.y;
z = v1.z - v2.z;
}
/**
* Subs this vector from v and places the result in this vector
* Subtracts v1 from this vector and places the result in this vector.
*
* @param v right-hand vector
* @param v1 right-hand vector
*/
public void sub(Vector3D v) {
x -= v.x;
y -= v.y;
z -= v.z;
public void sub(Vector3d v1) {
x -= v1.x;
y -= v1.y;
z -= v1.z;
}
/**
* Scales the elements of this vector by s
* Scales the elements of this vector by <code>s</code>.
*
* @param s scaling factor
*/
......@@ -174,19 +171,21 @@ public class Vector3D {
}
/**
* Scales the elements of vector v by s and place the results in this vector
* Scales the elements of vector v1 by <code>s</code> and places the results
* in this vector.
*
* @param s scaling vector
* @param v vector to be scaled
* @param s scaling factor
* @param v1 vector to be scaled
*/
public void scale(double s, Vector3D v) {
x = s * v.x;
y = s * v.y;
z = s * v.z;
public void scale(double s, Vector3d v1) {
x = s * v1.x;
y = s * v1.y;
z = s * v1.z;
}
/**
* Returns the 2 norm of this vector. This is the square root of the sum of the squares of the elements
* Returns the 2 norm of this vector. This is the square root of the sum of
* the squares of the elements.
*
* @return vector 2 norm
*/
......@@ -195,7 +194,8 @@ public class Vector3D {
}
/**
* Retruns the square of the 2 norm of this vector. this is the sum of the squares of the element
* Returns the square of the 2 norm of this vector. This is the sum of the
* squares of the elements.
*
* @return square of the 2 norm
*/
......@@ -204,12 +204,11 @@ public class Vector3D {
}
/**
* Returns the Euclidean distance between this vector and vector v
* Returns the Euclidean distance between this vector and vector v.
*
* @param v vector destination
* @return distance between this vector and v
*/
public double distance(Vector3D v) {
public double distance(Vector3d v) {
double dx = x - v.x;
double dy = y - v.y;
double dz = z - v.z;
......@@ -218,12 +217,12 @@ public class Vector3D {
}
/**
* Returns the squared of Euclidean distance between this vector and vector v
* Returns the squared of the Euclidean distance between this vector and
* vector v.
*
* @param v vector destination
* @return squared distance between this vector and v
*/
public double distanceSquared(Vector3D v) {
public double distanceSquared(Vector3d v) {
double dx = x - v.x;
double dy = y - v.y;
double dz = z - v.z;
......@@ -232,17 +231,17 @@ public class Vector3D {
}
/**
* Returns the dot product of this vector and v1
* Returns the dot product of this vector and v1.
*
* @param v right-hand vector
* @param v1 right-hand vector
* @return dot product
*/
public double dot(Vector3D v) {
return x * v.x + y * v.y + z * v.z;
public double dot(Vector3d v1) {
return x * v1.x + y * v1.y + z * v1.z;
}
/**
* Normalizes this vector in place
* Normalizes this vector in place.
*/
public void normalize() {
double lenSqr = x * x + y * y + z * z;
......@@ -256,27 +255,57 @@ public class Vector3D {
}
/**
* Computes the cross product of v1 and v2 and places the result in this vector
* Sets the elements of this vector to zero.
*/
public void setZero() {
x = 0;
y = 0;
z = 0;
}
/**
* Sets the elements of this vector to the prescribed values.
*
* @param x value for first element
* @param y value for second element
* @param z value for third element
*/
public void set(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Computes the cross product of v1 and v2 and places the result in this
* vector.
*
* @param v1 left-hand vector
* @param v2 right-hand vector
*/
public void cross(Vector3D v1, Vector3D v2) {
public void cross(Vector3d v1, Vector3d v2) {
double tmpx = v1.y * v2.z - v1.z * v2.y;
double tmpy = v1.z * v2.x - v1.x * v2.z;
double tmpz = v1.x * v2.y - v1.y - v2.x;
double tmpz = v1.x * v2.y - v1.y * v2.x;
x = tmpx;
y = tmpy;
z = tmpz;
}
@Override
/**
* Returns a string representation of this vector, consisting of the x, y,
* and z coordinates.
*
* @return string representation
*/
public String toString() {
return "Vector3D{" +
"x=" + x +
", y=" + y +
", z=" + z +
'}';
return x + " " + y + " " + z;
}
public double scalarProjection(Vector3d normal) {
return this.dot(normal)/normal.norm();
}
}
......@@ -3,41 +3,42 @@ package Entity;
public class Vertex {
/**
* Spatial point associated with this vertex
* Spatial point associated with this vertex.
*/
public Point3D pnt;
public Point3d pnt;
/**
* Back index into an array
* Back index into an array.
*/
public int index;
/**
* List forward link
* List forward link.
*/
public Vertex prev;
/**
* List backward link
* List backward link.
*/
public Vertex next;
/**
* Current face that this vertex is outside of
* Current face that this vertex is outside of.
*/
public Face face;
/**
* Constructs a vertex and sets its coordinates to 0
* Constructs a vertex and sets its coordinates to 0.
*/
public Vertex() {
pnt = new Point3d();
}
/**
* Constructs a vertex with the specified coordinates and index
* Constructs a vertex with the specified coordinates and index.
*/
public Vertex(double x, double y, double z, int idx) {
pnt = new Point3D(x, y, z);
pnt = new Point3d(x, y, z);
index = idx;
}
}
import Entity.Point3D;
import Entity.Point3d;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
......@@ -17,7 +18,7 @@ public class Fleet_single_cam {
private int num_y;
private int num_z;
private boolean show;
private Point3D[] points;
private Point3d[] points;
// private
HashMap<String, intArray> side;
......@@ -143,7 +144,7 @@ public class Fleet_single_cam {
*/
public void initPoint3d() {
//init point list
List<Point3D> point3dList = new ArrayList<>();
List<Point3d> point3dList = new ArrayList<>();
//temp pixel
int xx = 0;
......@@ -164,7 +165,7 @@ public class Fleet_single_cam {
xx += 1;
//add if point == 0
if (front[i][j] == 0) {
point3dList.add(new Point3D(xx, yy, zz));
point3dList.add(new Point3d(xx, yy, zz));
}
}
}
......@@ -179,7 +180,7 @@ public class Fleet_single_cam {
yy += 1;
//add if point == 0
if (side[i][j] == 0) {
point3dList.add(new Point3D(xx, yy, zz));
point3dList.add(new Point3d(xx, yy, zz));
}
}
}
......@@ -194,18 +195,26 @@ public class Fleet_single_cam {
xx += 1;
//add if point == 0
if (bottom[i][j] == 0) {
point3dList.add(new Point3D(xx, yy, zz));
point3dList.add(new Point3d(xx, yy, zz));
}
}
}
points = point3dList.toArray(new Point3D[point3dList.size()]);
points = point3dList.toArray(new Point3d[point3dList.size()]);
}
public Point3D[] getPoints() {
public Point3d[] getPoints() {
return points;
}
/**
* Adding 0 into matrix inorder to make the matrix fit the require height and width
* @param matrix
* @param require_height
* @param require_width
* @return
*/
private int[][] padding(int[][] matrix, int require_height, int require_width) {
//get width, height
int height = 0;
......
import Entity.Point3d;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.opencv.opencv_java;
import utils.QuickHull3D;
import java.util.Arrays;
......@@ -8,8 +11,11 @@ public class main {
Loader.load(opencv_java.class);
//Khoi tạo 1 cam, nhận ảnh tĩnh vào
Fleet_single_cam cam1 = new Fleet_single_cam("a", "1da36bd1c4aa0bf452bb24.jpg", 0.5, 5, 5, 4, false);
int x = 5;
int y = 5;
int z = 4;
//Khoi tạo cam 1
Fleet_single_cam cam1 = new Fleet_single_cam("1", "1da36bd1c4aa0bf452bb24.jpg", 0.5, x, y, z, false);
//create front side, add 4 đỉnh của side front vào
cam1.add_side("front", new float[][]{
......@@ -35,10 +41,12 @@ public class main {
new float[]{288, 545}
});
//tính matrix thể tích vật
cam1.refine_matrix();
//lấy những point chứa vật
cam1.initPoint3d();
//Khoi tạo 1 cam, nhận ảnh tĩnh vào
//Khoi tạo cam2
Fleet_single_cam cam2 = new Fleet_single_cam("a", "5b8ab6b219c9d6978fd827.jpg", 0.5, 5, 5, 4, false);
//create front side, add 4 đỉnh của side front vào
......@@ -65,32 +73,26 @@ public class main {
new float[]{275, 497}
});
//tính matrix thể tích vật
cam2.refine_matrix();
//lấy những point của vật thể
cam2.initPoint3d();
// tổng hợp lại những point chứa vật trong 2 cam
Point3d[] poly = Arrays.copyOf(cam1.getPoints(), cam1.getPoints().length+cam2.getPoints().length);
System.arraycopy(cam2.getPoints(), 0, poly, cam1.getPoints().length, cam2.getPoints().length);
//dùng quick hull algorithm để tính thể tích
QuickHull3D hull = new QuickHull3D();
hull.build(poly);
//lấy thể tích của vật
double volume = hull.calVolume();
// Point3d[] poly = Arrays.copyOf(cam1.getPoints(), cam1.getPoints().length+cam2.getPoints().length);
// System.arraycopy(cam2.getPoints(), 0, poly, cam1.getPoints().length, cam2.getPoints().length);
//
// QuickHull3D hull = new QuickHull3D();
// hull.build(poly);
// System.out.println("Vertices:");
// Point3d[] vertices = hull.getVertices();
// for (int i = 0; i < vertices.length; i++) {
// Point3d pnt = vertices[i];
// System.out.println(pnt.x + " " + pnt.y + " " + pnt.z);
// }
//
// System.out.println("Faces:");
// int[][] faceIndices = hull.getFaces();
// for (int i = 0; i < vertices.length; i++) {
// for (int k = 0; k < faceIndices[i].length; k++) {
// System.out.print(faceIndices[i][k] + " ");
// }
// System.out.println("");
// }
//Kết quả trả ra: tỉ lệ thể tích
double result = volume/((x+1)*(y+1)*(z+1));
}
......
package utils;
import Entity.*;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.Vector;
public class QuickHull3D {
/**
* Specifies that (on output) vertex indices for a face should be listed in
* clockwise order.
*/
public static final int CLOCKWISE = 0x1;
/**
* Specifies that (on output) the vertex indices for a face should be
* numbered starting from 1.
*/
public static final int INDEXED_FROM_ONE = 0x2;
/**
* Specifies that (on output) the vertex indices for a face should be
* numbered starting from 0.
*/
public static final int INDEXED_FROM_ZERO = 0x4;
/**
* Specifies that (on output) the vertex indices for a face should be
* numbered with respect to the original input points.
*/
public static final int POINT_RELATIVE = 0x8;
/**
* Specifies that the distance tolerance should be computed automatically
* from the input point data.
*/
public static final double AUTOMATIC_TOLERANCE = -1;
protected int findIndex = -1;
// estimated size of the point set
protected double charLength;
protected Vertex[] pointBuffer = new Vertex[0];
protected int[] vertexPointIndices = new int[0];
private Face[] discardedFaces = new Face[3];
private Vertex[] maxVtxs = new Vertex[3];
private Vertex[] minVtxs = new Vertex[3];
protected Vector faces = new Vector(16);
protected Vector horizon = new Vector(16);
private FaceList newFaces = new FaceList();
private VertexList unclaimed = new VertexList();
private VertexList claimed = new VertexList();
protected int numVertices;
protected int numFaces;
protected int numPoints;
protected double explicitTolerance = AUTOMATIC_TOLERANCE;
protected double tolerance;
/**
* Precision of a double.
*/
private static final double DOUBLE_PREC = 2.2204460492503131e-16;
/**
* Returns the distance tolerance that was used for the most recently
* computed hull. The distance tolerance is used to determine when faces are
* unambiguously convex with respect to each other, and when points are
* unambiguously above or below a face plane, in the presence of <a
* href=#distTol>numerical imprecision</a>. Normally, this tolerance is
* computed automatically for each set of input points, but it can be set
* explicitly by the application.
*
* @return distance tolerance
*/
public double getDistanceTolerance() {
return tolerance;
}
private void addPointToFace(Vertex vtx, Face face) {
vtx.face = face;
if (face.outside == null) {
claimed.add(vtx);
} else {
claimed.insertBefore(vtx, face.outside);
}
face.outside = vtx;
}
private void removePointFromFace(Vertex vtx, Face face) {
if (vtx == face.outside) {
if (vtx.next != null && vtx.next.face == face) {
face.outside = vtx.next;
} else {
face.outside = null;
}
}
claimed.delete(vtx);
}
private Vertex removeAllPointsFromFace(Face face) {
if (face.outside != null) {
Vertex end = face.outside;
while (end.next != null && end.next.face == face) {
end = end.next;
}
claimed.delete(face.outside, end);
end.next = null;
return face.outside;
} else {
return null;
}
}
/**
* Creates an empty convex hull object.
*/
public QuickHull3D() {
}
/**
* Creates a convex hull object and initializes it to the convex hull of a
* set of points whose coordinates are given by an array of doubles.
*
* @param coords x, y, and z coordinates of each input point. The length of
* this array will be three times the the number of input points.
* @throws IllegalArgumentException the number of input points is less than four, or the points
* appear to be coincident, colinear, or coplanar.
*/
public QuickHull3D(double[] coords) throws IllegalArgumentException {
build(coords, coords.length / 3);
}
/**
* Creates a convex hull object and initializes it to the convex hull of a
* set of points.
*
* @param points input points.
* @throws IllegalArgumentException the number of input points is less than four, or the points
* appear to be coincident, colinear, or coplanar.
*/
public QuickHull3D(Point3d[] points) throws IllegalArgumentException {
build(points, points.length);
}
private HalfEdge findHalfEdge(Vertex tail, Vertex head) {
// brute force ... OK, since setHull is not used much
for (Iterator it = faces.iterator(); it.hasNext(); ) {
HalfEdge he = ((Face) it.next()).findEdge(tail, head);
if (he != null) {
return he;
}
}
return null;
}
/**
* print all points to the print stream (very point a line)
*
* @param ps the print stream to write to
*/
public void printPoints(PrintStream ps) {
for (int i = 0; i < numPoints; i++) {
Point3d pnt = pointBuffer[i].pnt;
ps.println(pnt.x + ", " + pnt.y + ", " + pnt.z + ",");
}
}
/**
* Constructs the convex hull of a set of points whose coordinates are given
* by an array of doubles.
*
* @param coords x, y, and z coordinates of each input point. The length of
* this array must be at least three times <code>nump</code>.
* @param nump number of input points
* @throws IllegalArgumentException the number of input points is less than four or greater than
* 1/3 the length of <code>coords</code>, or the points appear
* to be coincident, colinear, or coplanar.
*/
public void build(double[] coords, int nump) throws IllegalArgumentException {
if (nump < 4) {
throw new IllegalArgumentException("Less than four input points specified");
}
if (coords.length / 3 < nump) {
throw new IllegalArgumentException("Coordinate array too small for specified number of points");
}
initBuffers(nump);
setPoints(coords, nump);
buildHull();
}
/**
* Constructs the convex hull of a set of points.
*
* @param points input points
* @throws IllegalArgumentException the number of input points is less than four, or the points
* appear to be coincident, colinear, or coplanar.
*/
public void build(Point3d[] points) throws IllegalArgumentException {
build(points, points.length);
}
/**
* Constructs the convex hull of a set of points.
*
* @param points input points
* @param nump number of input points
* @throws IllegalArgumentException the number of input points is less than four or greater then
* the length of <code>points</code>, or the points appear to be
* coincident, colinear, or coplanar.
*/
public void build(Point3d[] points, int nump) throws IllegalArgumentException {
if (nump < 4) {
throw new IllegalArgumentException("Less than four input points specified");
}
if (points.length < nump) {
throw new IllegalArgumentException("Point array too small for specified number of points");
}
initBuffers(nump);
setPoints(points, nump);
buildHull();
}
/**
* Triangulates any non-triangular hull faces. In some cases, due to
* precision issues, the resulting triangles may be very thin or small, and
* hence appear to be non-convex (this same limitation is present in <a
* href=http://www.qhull.org>qhull</a>).
*/
public void triangulate() {
double minArea = 1000 * charLength * DOUBLE_PREC;
newFaces.clear();
for (Iterator it = faces.iterator(); it.hasNext(); ) {
Face face = (Face) it.next();
if (face.mark == Face.VISIBLE) {
face.triangulate(newFaces, minArea);
// splitFace (face);
}
}
for (Face face = newFaces.first(); face != null; face = face.next) {
faces.add(face);
}
}
protected void initBuffers(int nump) {
if (pointBuffer.length < nump) {
Vertex[] newBuffer = new Vertex[nump];
vertexPointIndices = new int[nump];
for (int i = 0; i < pointBuffer.length; i++) {
newBuffer[i] = pointBuffer[i];
}
for (int i = pointBuffer.length; i < nump; i++) {
newBuffer[i] = new Vertex();
}
pointBuffer = newBuffer;
}
faces.clear();
claimed.clear();
numFaces = 0;
numPoints = nump;
}
protected void setPoints(double[] coords, int nump) {
for (int i = 0; i < nump; i++) {
Vertex vtx = pointBuffer[i];
vtx.pnt.set(coords[i * 3 + 0], coords[i * 3 + 1], coords[i * 3 + 2]);
vtx.index = i;
}
}
protected void setPoints(Point3d[] pnts, int nump) {
for (int i = 0; i < nump; i++) {
Vertex vtx = pointBuffer[i];
vtx.pnt.set(pnts[i]);
vtx.index = i;
}
}
protected void computeMaxAndMin() {
Vector3d max = new Vector3d();
Vector3d min = new Vector3d();
for (int i = 0; i < 3; i++) {
maxVtxs[i] = minVtxs[i] = pointBuffer[0];
}
max.set(pointBuffer[0].pnt);
min.set(pointBuffer[0].pnt);
for (int i = 1; i < numPoints; i++) {
Point3d pnt = pointBuffer[i].pnt;
if (pnt.x > max.x) {
max.x = pnt.x;
maxVtxs[0] = pointBuffer[i];
} else if (pnt.x < min.x) {
min.x = pnt.x;
minVtxs[0] = pointBuffer[i];
}
if (pnt.y > max.y) {
max.y = pnt.y;
maxVtxs[1] = pointBuffer[i];
} else if (pnt.y < min.y) {
min.y = pnt.y;
minVtxs[1] = pointBuffer[i];
}
if (pnt.z > max.z) {
max.z = pnt.z;
maxVtxs[2] = pointBuffer[i];
} else if (pnt.z < min.z) {
min.z = pnt.z;
minVtxs[2] = pointBuffer[i];
}
}
// this epsilon formula comes from QuickHull, and I'm
// not about to quibble.
charLength = Math.max(max.x - min.x, max.y - min.y);
charLength = Math.max(max.z - min.z, charLength);
if (explicitTolerance == AUTOMATIC_TOLERANCE) {
tolerance =
3 * DOUBLE_PREC * (Math.max(Math.abs(max.x), Math.abs(min.x)) + Math.max(Math.abs(max.y), Math.abs(min.y)) + Math.max(Math.abs(max.z), Math.abs(min.z)));
} else {
tolerance = explicitTolerance;
}
}
/**
* Creates the initial simplex from which the hull will be built.
*/
protected void createInitialSimplex() throws IllegalArgumentException {
double max = 0;
int imax = 0;
for (int i = 0; i < 3; i++) {
double diff = maxVtxs[i].pnt.get(i) - minVtxs[i].pnt.get(i);
if (diff > max) {
max = diff;
imax = i;
}
}
if (max <= tolerance) {
throw new IllegalArgumentException("Input points appear to be coincident");
}
Vertex[] vtx = new Vertex[4];
// set first two vertices to be those with the greatest
// one dimensional separation
vtx[0] = maxVtxs[imax];
vtx[1] = minVtxs[imax];
// set third vertex to be the vertex farthest from
// the line between vtx0 and vtx1
Vector3d u01 = new Vector3d();
Vector3d diff02 = new Vector3d();
Vector3d nrml = new Vector3d();
Vector3d xprod = new Vector3d();
double maxSqr = 0;
u01.sub(vtx[1].pnt, vtx[0].pnt);
u01.normalize();
for (int i = 0; i < numPoints; i++) {
diff02.sub(pointBuffer[i].pnt, vtx[0].pnt);
xprod.cross(u01, diff02);
double lenSqr = xprod.normSquared();
if (lenSqr > maxSqr && pointBuffer[i] != vtx[0] && // paranoid
pointBuffer[i] != vtx[1]) {
maxSqr = lenSqr;
vtx[2] = pointBuffer[i];
nrml.set(xprod);
}
}
if (Math.sqrt(maxSqr) <= 100 * tolerance) {
throw new IllegalArgumentException("Input points appear to be colinear");
}
nrml.normalize();
double maxDist = 0;
double d0 = vtx[2].pnt.dot(nrml);
for (int i = 0; i < numPoints; i++) {
double dist = Math.abs(pointBuffer[i].pnt.dot(nrml) - d0);
if (dist > maxDist && pointBuffer[i] != vtx[0] && // paranoid
pointBuffer[i] != vtx[1] && pointBuffer[i] != vtx[2]) {
maxDist = dist;
vtx[3] = pointBuffer[i];
}
}
if (Math.abs(maxDist) <= 100 * tolerance) {
throw new IllegalArgumentException("Input points appear to be coplanar");
}
Face[] tris = new Face[4];
if (vtx[3].pnt.dot(nrml) - d0 < 0) {
tris[0] = Face.createTriangle(vtx[0], vtx[1], vtx[2]);
tris[1] = Face.createTriangle(vtx[3], vtx[1], vtx[0]);
tris[2] = Face.createTriangle(vtx[3], vtx[2], vtx[1]);
tris[3] = Face.createTriangle(vtx[3], vtx[0], vtx[2]);
for (int i = 0; i < 3; i++) {
int k = (i + 1) % 3;
tris[i + 1].getEdge(1).setOpposite(tris[k + 1].getEdge(0));
tris[i + 1].getEdge(2).setOpposite(tris[0].getEdge(k));
}
} else {
tris[0] = Face.createTriangle(vtx[0], vtx[2], vtx[1]);
tris[1] = Face.createTriangle(vtx[3], vtx[0], vtx[1]);
tris[2] = Face.createTriangle(vtx[3], vtx[1], vtx[2]);
tris[3] = Face.createTriangle(vtx[3], vtx[2], vtx[0]);
for (int i = 0; i < 3; i++) {
int k = (i + 1) % 3;
tris[i + 1].getEdge(0).setOpposite(tris[k + 1].getEdge(1));
tris[i + 1].getEdge(2).setOpposite(tris[0].getEdge((3 - i) % 3));
}
}
for (int i = 0; i < 4; i++) {
faces.add(tris[i]);
}
for (int i = 0; i < numPoints; i++) {
Vertex v = pointBuffer[i];
if (v == vtx[0] || v == vtx[1] || v == vtx[2] || v == vtx[3]) {
continue;
}
maxDist = tolerance;
Face maxFace = null;
for (int k = 0; k < 4; k++) {
double dist = tris[k].distanceToPlane(v.pnt);
if (dist > maxDist) {
maxFace = tris[k];
maxDist = dist;
}
}
if (maxFace != null) {
addPointToFace(v, maxFace);
}
}
}
/**
* Returns the number of vertices in this hull.
*
* @return number of vertices
*/
public int getNumVertices() {
return numVertices;
}
/**
* Returns the vertex points in this hull.
*
* @return array of vertex points
* @see QuickHull3D#getVertices(double[])
* @see QuickHull3D#getFaces()
*/
public Point3d[] getVertices() {
Point3d[] vtxs = new Point3d[numVertices];
for (int i = 0; i < numVertices; i++) {
vtxs[i] = pointBuffer[vertexPointIndices[i]].pnt;
}
return vtxs;
}
/**
* Returns the coordinates of the vertex points of this hull.
*
* @param coords returns the x, y, z coordinates of each vertex. This length of
* this array must be at least three times the number of
* vertices.
* @return the number of vertices
* @see QuickHull3D#getVertices()
* @see QuickHull3D#getFaces()
*/
public int getVertices(double[] coords) {
for (int i = 0; i < numVertices; i++) {
Point3d pnt = pointBuffer[vertexPointIndices[i]].pnt;
coords[i * 3 + 0] = pnt.x;
coords[i * 3 + 1] = pnt.y;
coords[i * 3 + 2] = pnt.z;
}
return numVertices;
}
/**
* Returns an array specifing the index of each hull vertex with respect to
* the original input points.
*
* @return vertex indices with respect to the original points
*/
public int[] getVertexPointIndices() {
int[] indices = new int[numVertices];
for (int i = 0; i < numVertices; i++) {
indices[i] = vertexPointIndices[i];
}
return indices;
}
/**
* Returns the number of faces in this hull.
*
* @return number of faces
*/
public int getNumFaces() {
return faces.size();
}
/**
* Returns the faces associated with this hull.
* <p>
* Each face is represented by an integer array which gives the indices of
* the vertices. These indices are numbered relative to the hull vertices,
* are zero-based, and are arranged counter-clockwise. More control over the
* index format can be obtained using {@link #getFaces(int)
* getFaces(indexFlags)}.
*
* @return array of integer arrays, giving the vertex indices for each face.
* @see QuickHull3D#getVertices()
* @see QuickHull3D#getFaces(int)
*/
public int[][] getFaces() {
return getFaces(0);
}
/**
* Returns the faces associated with this hull.
* <p>
* Each face is represented by an integer array which gives the indices of
* the vertices. By default, these indices are numbered with respect to the
* hull vertices (as opposed to the input points), are zero-based, and are
* arranged counter-clockwise. However, this can be changed by setting
* {@link #POINT_RELATIVE POINT_RELATIVE}, {@link #INDEXED_FROM_ONE
* INDEXED_FROM_ONE}, or {@link #CLOCKWISE CLOCKWISE} in the indexFlags
* parameter.
*
* @param indexFlags specifies index characteristics (0 results in the default)
* @return array of integer arrays, giving the vertex indices for each face.
* @see QuickHull3D#getVertices()
*/
public int[][] getFaces(int indexFlags) {
int[][] allFaces = new int[faces.size()][];
int k = 0;
for (Iterator it = faces.iterator(); it.hasNext(); ) {
Face face = (Face) it.next();
allFaces[k] = new int[face.numVertices()];
getFaceIndices(allFaces[k], face, indexFlags);
k++;
}
return allFaces;
}
private void getFaceIndices(int[] indices, Face face, int flags) {
boolean ccw = (flags & CLOCKWISE) == 0;
boolean indexedFromOne = (flags & INDEXED_FROM_ONE) != 0;
boolean pointRelative = (flags & POINT_RELATIVE) != 0;
HalfEdge hedge = face.he0;
int k = 0;
do {
int idx = hedge.head().index;
if (pointRelative) {
idx = vertexPointIndices[idx];
}
if (indexedFromOne) {
idx++;
}
indices[k++] = idx;
hedge = (ccw ? hedge.next : hedge.prev);
} while (hedge != face.he0);
}
protected void resolveUnclaimedPoints(FaceList newFaces) {
Vertex vtxNext = unclaimed.first();
for (Vertex vtx = vtxNext; vtx != null; vtx = vtxNext) {
vtxNext = vtx.next;
double maxDist = tolerance;
Face maxFace = null;
for (Face newFace = newFaces.first(); newFace != null; newFace = newFace.next) {
if (newFace.mark == Face.VISIBLE) {
double dist = newFace.distanceToPlane(vtx.pnt);
if (dist > maxDist) {
maxDist = dist;
maxFace = newFace;
}
if (maxDist > 1000 * tolerance) {
break;
}
}
}
if (maxFace != null) {
addPointToFace(vtx, maxFace);
} else {
}
}
}
protected void deleteFacePoints(Face face, Face absorbingFace) {
Vertex faceVtxs = removeAllPointsFromFace(face);
if (faceVtxs != null) {
if (absorbingFace == null) {
unclaimed.addAll(faceVtxs);
} else {
Vertex vtxNext = faceVtxs;
for (Vertex vtx = vtxNext; vtx != null; vtx = vtxNext) {
vtxNext = vtx.next;
double dist = absorbingFace.distanceToPlane(vtx.pnt);
if (dist > tolerance) {
addPointToFace(vtx, absorbingFace);
} else {
unclaimed.add(vtx);
}
}
}
}
}
private static final int NONCONVEX_WRT_LARGER_FACE = 1;
private static final int NONCONVEX = 2;
protected double oppFaceDistance(HalfEdge he) {
return he.face.distanceToPlane(he.opposite.face.getCentroid());
}
private boolean doAdjacentMerge(Face face, int mergeType) {
HalfEdge hedge = face.he0;
boolean convex = true;
do {
Face oppFace = hedge.oppositeFace();
boolean merge = false;
if (mergeType == NONCONVEX) { // then merge faces if they are
// definitively non-convex
if (oppFaceDistance(hedge) > -tolerance || oppFaceDistance(hedge.opposite) > -tolerance) {
merge = true;
}
} else {
// mergeType == NONCONVEX_WRT_LARGER_FACE
// merge faces if they are parallel or non-convex
// wrt to the larger face; otherwise, just mark
// the face non-convex for the second pass.
if (face.area > oppFace.area) {
if (oppFaceDistance(hedge) > -tolerance) {
merge = true;
} else if (oppFaceDistance(hedge.opposite) > -tolerance) {
convex = false;
}
} else {
if (oppFaceDistance(hedge.opposite) > -tolerance) {
merge = true;
} else if (oppFaceDistance(hedge) > -tolerance) {
convex = false;
}
}
}
if (merge) {
int numd = face.mergeAdjacentFace(hedge, discardedFaces);
for (int i = 0; i < numd; i++) {
deleteFacePoints(discardedFaces[i], face);
}
return true;
}
hedge = hedge.next;
} while (hedge != face.he0);
if (!convex) {
face.mark = Face.NON_CONVEX;
}
return false;
}
protected void calculateHorizon(Point3d eyePnt, HalfEdge edge0, Face face, Vector horizon) {
// oldFaces.add (face);
deleteFacePoints(face, null);
face.mark = Face.DELETED;
HalfEdge edge;
if (edge0 == null) {
edge0 = face.getEdge(0);
edge = edge0;
} else {
edge = edge0.getNext();
}
do {
Face oppFace = edge.oppositeFace();
if (oppFace.mark == Face.VISIBLE) {
if (oppFace.distanceToPlane(eyePnt) > tolerance) {
calculateHorizon(eyePnt, edge.getOpposite(), oppFace, horizon);
} else {
horizon.add(edge);
}
}
edge = edge.getNext();
} while (edge != edge0);
}
private HalfEdge addAdjoiningFace(Vertex eyeVtx, HalfEdge he) {
Face face = Face.createTriangle(eyeVtx, he.tail(), he.head());
faces.add(face);
face.getEdge(-1).setOpposite(he.getOpposite());
return face.getEdge(0);
}
protected void addNewFaces(FaceList newFaces, Vertex eyeVtx, Vector horizon) {
newFaces.clear();
HalfEdge hedgeSidePrev = null;
HalfEdge hedgeSideBegin = null;
for (Iterator it = horizon.iterator(); it.hasNext(); ) {
HalfEdge horizonHe = (HalfEdge) it.next();
HalfEdge hedgeSide = addAdjoiningFace(eyeVtx, horizonHe);
if (hedgeSidePrev != null) {
hedgeSide.next.setOpposite(hedgeSidePrev);
} else {
hedgeSideBegin = hedgeSide;
}
newFaces.add(hedgeSide.getFace());
hedgeSidePrev = hedgeSide;
}
hedgeSideBegin.next.setOpposite(hedgeSidePrev);
}
protected Vertex nextPointToAdd() {
if (!claimed.isEmpty()) {
Face eyeFace = claimed.first().face;
Vertex eyeVtx = null;
double maxDist = 0;
for (Vertex vtx = eyeFace.outside; vtx != null && vtx.face == eyeFace; vtx = vtx.next) {
double dist = eyeFace.distanceToPlane(vtx.pnt);
if (dist > maxDist) {
maxDist = dist;
eyeVtx = vtx;
}
}
return eyeVtx;
} else {
return null;
}
}
protected void addPointToHull(Vertex eyeVtx) {
horizon.clear();
unclaimed.clear();
removePointFromFace(eyeVtx, eyeVtx.face);
calculateHorizon(eyeVtx.pnt, null, eyeVtx.face, horizon);
newFaces.clear();
addNewFaces(newFaces, eyeVtx, horizon);
// first merge pass ... merge faces which are non-convex
// as determined by the larger face
for (Face face = newFaces.first(); face != null; face = face.next) {
if (face.mark == Face.VISIBLE) {
while (doAdjacentMerge(face, NONCONVEX_WRT_LARGER_FACE))
;
}
}
// second merge pass ... merge faces which are non-convex
// wrt either face
for (Face face = newFaces.first(); face != null; face = face.next) {
if (face.mark == Face.NON_CONVEX) {
face.mark = Face.VISIBLE;
while (doAdjacentMerge(face, NONCONVEX))
;
}
}
resolveUnclaimedPoints(newFaces);
}
protected void buildHull() {
int cnt = 0;
Vertex eyeVtx;
computeMaxAndMin();
createInitialSimplex();
while ((eyeVtx = nextPointToAdd()) != null) {
addPointToHull(eyeVtx);
cnt++;
}
reindexFacesAndVertices();
}
private void markFaceVertices(Face face, int mark) {
HalfEdge he0 = face.getFirstEdge();
HalfEdge he = he0;
do {
he.head().index = mark;
he = he.next;
} while (he != he0);
}
protected void reindexFacesAndVertices() {
for (int i = 0; i < numPoints; i++) {
pointBuffer[i].index = -1;
}
// remove inactive faces and mark active vertices
numFaces = 0;
for (Iterator it = faces.iterator(); it.hasNext(); ) {
Face face = (Face) it.next();
if (face.mark != Face.VISIBLE) {
it.remove();
} else {
markFaceVertices(face, 0);
numFaces++;
}
}
// reindex vertices
numVertices = 0;
for (int i = 0; i < numPoints; i++) {
Vertex vtx = pointBuffer[i];
if (vtx.index == 0) {
vertexPointIndices[numVertices] = i;
vtx.index = numVertices++;
}
}
}
protected boolean checkFaceConvexity(Face face, double tol, PrintStream ps) {
double dist;
HalfEdge he = face.he0;
do {
face.checkConsistency();
// make sure edge is convex
dist = oppFaceDistance(he);
if (dist > tol) {
if (ps != null) {
ps.println("Edge " + he.getVertexString() + " non-convex by " + dist);
}
return false;
}
dist = oppFaceDistance(he.opposite);
if (dist > tol) {
if (ps != null) {
ps.println("Opposite edge " + he.opposite.getVertexString() + " non-convex by " + dist);
}
return false;
}
if (he.next.oppositeFace() == he.oppositeFace()) {
if (ps != null) {
ps.println("Redundant vertex " + he.head().index + " in face " + face.getVertexString());
}
return false;
}
he = he.next;
} while (he != face.he0);
return true;
}
protected boolean checkFaces(double tol, PrintStream ps) {
// check edge convexity
boolean convex = true;
for (Iterator it = faces.iterator(); it.hasNext(); ) {
Face face = (Face) it.next();
if (face.mark == Face.VISIBLE && !checkFaceConvexity(face, tol, ps)) {
convex = false;
}
}
return convex;
}
/**
* Checks the correctness of the hull using the distance tolerance returned
* by {@link QuickHull3D#getDistanceTolerance getDistanceTolerance}; see
* {@link QuickHull3D#check(PrintStream, double) check(PrintStream,double)}
* for details.
*
* @param ps print stream for diagnostic messages; may be set to
* <code>null</code> if no messages are desired.
* @return true if the hull is valid
* @see QuickHull3D#check(PrintStream, double)
*/
public boolean check(PrintStream ps) {
return check(ps, getDistanceTolerance());
}
/**
* Checks the correctness of the hull. This is done by making sure that no
* faces are non-convex and that no points are outside any face. These tests
* are performed using the distance tolerance <i>tol</i>. Faces are
* considered non-convex if any edge is non-convex, and an edge is
* non-convex if the centroid of either adjoining face is more than
* <i>tol</i> above the plane of the other face. Similarly, a point is
* considered outside a face if its distance to that face's plane is more
* than 10 times <i>tol</i>.
* <p>
* If the hull has been {@link #triangulate triangulated}, then this routine
* may fail if some of the resulting triangles are very small or thin.
*
* @param ps print stream for diagnostic messages; may be set to
* <code>null</code> if no messages are desired.
* @param tol distance tolerance
* @return true if the hull is valid
* @see QuickHull3D#check(PrintStream)
*/
public boolean check(PrintStream ps, double tol) {
// check to make sure all edges are fully connected
// and that the edges are convex
double dist;
double pointTol = 10 * tol;
if (!checkFaces(tolerance, ps)) {
return false;
}
// check point inclusion
for (int i = 0; i < numPoints; i++) {
Point3d pnt = pointBuffer[i].pnt;
for (Iterator it = faces.iterator(); it.hasNext(); ) {
Face face = (Face) it.next();
if (face.mark == Face.VISIBLE) {
dist = face.distanceToPlane(pnt);
if (dist > pointTol) {
if (ps != null) {
ps.println("Point " + i + " " + dist + " above face " + face.getVertexString());
}
return false;
}
}
}
}
return true;
}
/**
* Return the volume of the convex hull
* @return
*/
public double calVolume() {
//Compute the average of the facial centroids inorder to get an arbitrary point inside the polyhedron
Vector3d avgPoint = new Point3d(0, 0, 0);
for (Iterator it = faces.iterator(); it.hasNext(); ) {
Face face = (Face) it.next();
avgPoint.add(face.getCentroid());
}
avgPoint.scale(1 / faces.size());
//Initialise the volume
double volume = 0;
for (Iterator it = faces.iterator(); it.hasNext(); ) {
Face face = (Face) it.next();
// Find a vector from avgPoint to the centroid of the face
Vector3d avgToCentoid = face.getCentroid().clone();
avgToCentoid.sub(avgPoint);
//distance from avgPoint to plane
double distance = avgToCentoid.scalarProjection(face.getNormal());
// Finds the volume of the pyramid using V = 1/3 * B * h
// where: B = area of the pyramid base.
// h = pyramid height.
double pryramidVolume = face.area * distance / 3;
volume += pryramidVolume;
}
return volume;
}
}
package utils;
import Entity.*;
import java.util.Vector;
public class QuickHull3d {
/**
* Specifies that the distance tolerance should be computed automatically from the input point data
*/
public static final double AUTOMATIC_TOLERANCE = -1;
protected int findIndex = -1;
//estimated size of the point set
protected double charLength;
protected Vertex[] pointBuffer = new Vertex[0];
protected int[] vertexPointIndices = new int[0];
private Face[] discardedFaces = new Face[3];
private Vertex[] maxVtxs = new Vertex[3];
private Vertex[] minVtxs = new Vertex[3];
protected Vector face = new Vector(16);
protected Vector horizon = new Vector(16);
private FaceList newFaces = new FaceList();
private VertexList unclaimed = new VertexList();
private VertexList claimed = new VertexList();
protected int numVertices;
protected int numFaces;
protected int numPoints;
protected double explicitTolerance = AUTOMATIC_TOLERANCE;
protected double tolerance;
private static final double DOUBLE_PREC = 2.2204460492503131e-16;
/**
* Create a convex hull object and initializes it to the convex hull of a set of points
* @param points
* input points
* @throws IllegalArgumentException
* the number of input points is less than four, or the point appear to be coincident, colinear, or coplanar
*/
public QuickHull3d(Point3D[] points) throws IllegalArgumentException{
build(points, points.length);
}
/**
* Constructs the convex hull of a set of points
* @param points
* input points
* @param nump
* number of input poins
* @throws IllegalArgumentException
* the number of input points is less than four or greater than the length if points, or the points appear to be coincident, colinear, or coplanar
*/
public void build(Point3D[] points, int nump) throws IllegalArgumentException{
if (nump < 4){
throw new IllegalArgumentException("Less than four input points specified");
}
if (points.length < nump){
throw new IllegalArgumentException("Point array too small for specified number of points");
}
initBuffers(nump);
setPoints(points, nump);
buildHull();
}
protected void initBuffers(int nump){
if (pointBuffer.length < nump){
Vertex[] newBuffer = new Vertex[nump];
vertexPointIndices = new int[nump];
for (int i = 0; i < pointBuffer.length; i++){
newBuffer[i] = pointBuffer[i];
}
for (int i = pointBuffer.length; i < nump; i++){
newBuffer[i] = new Vertex();
}
pointBuffer = newBuffer;
}
face.clear();
claimed.clear();
numFaces = 0;
numPoints = nump;
}
protected void setPoints(Point3D[] pnts, int nump) {
for (int i = 0; i < nump; i++){
Vertex vtx = pointBuffer[i];
vtx.pnt.set(pnts[i]);
vtx.index = i;
}
}
protected void buildHull(){
int cnt = 0;
Vertex eyeVtx;
computeMaxAndMin();
createInitialSimplex();
while ((eyeVtx = nextPointToAdd()) != null){
addPointToHull(eyeVtx);
cnt++;
}
reindexFacesAndVertices();
}
protected void computeMaxAndMin(){
//initialize max, min
Vector3D max = new Vector3D();
Vector3D min = new Vector3D();
for (int i=0;i<3;i++){
maxVtxs[i] = minVtxs[i] = pointBuffer[0];
}
max.set(pointBuffer[0].pnt);
min.set(pointBuffer[0].pnt);
//check all point to find max, min
for (int i=1;i<numPoints;i++){
Point3D pnt = pointBuffer[i].pnt;
//find maxVtxs[0] and minVtxs[0] , vectors with max x and min x
if (pnt.x >max.x){
max.x = pnt.x;
maxVtxs[0] = pointBuffer[i];
}else if (pnt.x < min.x){
min.x = pnt.x;
minVtxs[0] = pointBuffer[i];
}
//find maxVtxs[1] and minVtxs[1] , vectors with max y and min y
if (pnt.y >max.y){
max.y = pnt.y;
maxVtxs[1] = pointBuffer[i];
}else if (pnt.y < min.y){
min.y = pnt.y;
minVtxs[1] = pointBuffer[i];
}
//find maxVtxs[2] and minVtxs[2] , vectors with max z and min z
if (pnt.z >max.z){
max.z = pnt.z;
maxVtxs[2] = pointBuffer[i];
}else if (pnt.z < min.z){
min.z = pnt.z;
minVtxs[2] = pointBuffer[i];
}
charLength = Math.max(max.x - min.x, max.y - min.y);
charLength = Math.max(max.z - min.z, charLength);
if (explicitTolerance == AUTOMATIC_TOLERANCE){
tolerance = 3* DOUBLE_PREC * (Math.max(Math.abs(max.x), Math.abs(min.x)) + Math.max(Math.abs(max.y), Math.abs(min.y)) + Math.max(Math.abs(max.z), Math.abs(min.z)));
}else{
tolerance = explicitTolerance;
}
}
}
protected void createInitialSimplex() throws IllegalArgumentException{
double max = 0;
int imax = 0;
for (int i = 0; i< 3; i++){
double diff = maxVtxs[i].pnt.get(i)-minVtxs[i].pnt.get(i);
if (diff > max){
max = diff;
imax = i;
}
}
if (max <= tolerance){
throw new IllegalArgumentException("Input points appear to be conicident");
}
Vertex[] vtx = new Vertex[4];
//set first two vertices to be those with the greatest one dimensional separation
vtx[0] = maxVtxs[imax];
vtx[1] = minVtxs[imax];
// set third vertex to be the vertex farthest from the line between vtx0 and vtx1
Vector3D u01 = new Vector3D();
Vector3D diff02 = new Vector3D();
Vector3D nrml = new Vector3D();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment