15 package eu.mihosoft.vrl.v3d.ext.quickhull3d;
36 private Vector3d normal;
57 static final int VISIBLE = 1;
60 static final int NON_CONVEX = 2;
63 static final int DELETED = 3;
76 public void computeCentroid (
Point3d centroid)
81 { centroid.add (he.head().pnt);
85 centroid.scale (1/(
double)numVerts);
94 public void computeNormal (Vector3d normal,
double minArea)
96 computeNormal(normal);
103 HalfEdge hedgeMax =
null;
104 double lenSqrMax = 0;
105 HalfEdge hedge = he0;
107 {
double lenSqr = hedge.lengthSquared();
108 if (lenSqr > lenSqrMax)
114 while (hedge != he0);
116 Point3d p2 = hedgeMax.head().pnt;
117 Point3d p1 = hedgeMax.tail().pnt;
118 double lenMax = Math.sqrt(lenSqrMax);
119 double ux = (p2.x - p1.x)/lenMax;
120 double uy = (p2.y - p1.y)/lenMax;
121 double uz = (p2.z - p1.z)/lenMax;
122 double dot = normal.x*ux + normal.y*uy + normal.z*uz;
136 public void computeNormal (Vector3d normal)
138 HalfEdge he1 = he0.next;
139 HalfEdge he2 = he1.next;
144 double d2x = p2.x - p0.x;
145 double d2y = p2.y - p0.y;
146 double d2z = p2.z - p0.z;
163 normal.x += d1y*d2z - d1z*d2y;
164 normal.y += d1z*d2x - d1x*d2z;
165 normal.z += d1x*d2y - d1y*d2x;
171 area = normal.norm();
172 normal.scale (1/area);
178 private void computeNormalAndCentroid()
180 computeNormal (normal);
181 computeCentroid (centroid);
182 planeOffset = normal.dot(centroid);
190 if (numv != numVerts)
191 {
throw new InternalErrorException (
192 "face " + getVertexString() +
" numVerts=" + numVerts +
" should be " + numv);
201 private void computeNormalAndCentroid(
double minArea)
203 computeNormal (normal, minArea);
204 computeCentroid (centroid);
205 planeOffset = normal.dot(centroid);
216 public static Face createTriangle (Vertex v0, Vertex v1, Vertex v2)
218 return createTriangle (v0, v1, v2, 0);
230 public static Face createTriangle (Vertex v0, Vertex v1, Vertex v2,
233 Face face =
new Face();
234 HalfEdge he0 =
new HalfEdge (v0, face);
235 HalfEdge he1 =
new HalfEdge (v1, face);
236 HalfEdge he2 =
new HalfEdge (v2, face);
248 face.computeNormalAndCentroid(minArea);
259 public static Face create (Vertex[] vtxArray,
int[] indices)
261 Face face =
new Face();
262 HalfEdge hePrev =
null;
263 for (
int i=0; i<indices.length; i++)
264 { HalfEdge he =
new HalfEdge (vtxArray[indices[i]], face);
266 { he.setPrev (hePrev);
274 face.he0.setPrev (hePrev);
275 hePrev.setNext (face.he0);
278 face.computeNormalAndCentroid();
287 normal =
new Vector3d();
298 public HalfEdge getEdge(
int i)
317 public HalfEdge getFirstEdge()
329 public HalfEdge findEdge (Vertex vt, Vertex vh)
333 {
if (he.head() == vh && he.tail() == vt)
349 public double distanceToPlane (
Point3d p)
351 return normal.x*p.x + normal.y*p.y + normal.z*p.z - planeOffset;
359 public Vector3d getNormal ()
379 public int numVertices()
389 public String getVertexString ()
395 { s =
"" + he.head().index;
398 { s +=
" " + he.head().index;
411 public void getVertexIndices (
int[] idxs)
416 { idxs[i++] = he.head().index;
429 private Face connectHalfEdges (
430 HalfEdge hedgePrev, HalfEdge hedge)
432 Face discardedFace =
null;
434 if (hedgePrev.oppositeFace() == hedge.oppositeFace())
437 Face oppFace = hedge.oppositeFace();
440 if (hedgePrev == he0)
443 if (oppFace.numVertices() == 3)
445 hedgeOpp = hedge.getOpposite().prev.getOpposite();
447 oppFace.mark = DELETED;
448 discardedFace = oppFace;
451 { hedgeOpp = hedge.getOpposite().next;
453 if (oppFace.he0 == hedgeOpp.prev)
454 { oppFace.he0 = hedgeOpp;
456 hedgeOpp.prev = hedgeOpp.prev.prev;
457 hedgeOpp.prev.next = hedgeOpp;
459 hedge.prev = hedgePrev.prev;
460 hedge.prev.next = hedge;
462 hedge.opposite = hedgeOpp;
463 hedgeOpp.opposite = hedge;
466 oppFace.computeNormalAndCentroid();
469 { hedgePrev.next = hedge;
470 hedge.prev = hedgePrev;
472 return discardedFace;
478 void checkConsistency()
481 HalfEdge hedge = he0;
486 {
throw new InternalErrorException (
487 "degenerate face: " + getVertexString());
490 { HalfEdge hedgeOpp = hedge.getOpposite();
491 if (hedgeOpp ==
null)
492 {
throw new InternalErrorException (
493 "face " + getVertexString() +
": " +
494 "unreflected half edge " + hedge.getVertexString());
496 else if (hedgeOpp.getOpposite() != hedge)
497 {
throw new InternalErrorException (
498 "face " + getVertexString() +
": " +
499 "opposite half edge " + hedgeOpp.getVertexString() +
501 hedgeOpp.getOpposite().getVertexString());
503 if (hedgeOpp.head() != hedge.tail() ||
504 hedge.head() != hedgeOpp.tail())
505 {
throw new InternalErrorException (
506 "face " + getVertexString() +
": " +
507 "half edge " + hedge.getVertexString() +
508 " reflected by " + hedgeOpp.getVertexString());
510 Face oppFace = hedgeOpp.face;
512 {
throw new InternalErrorException (
513 "face " + getVertexString() +
": " +
514 "no face on half edge " + hedgeOpp.getVertexString());
516 else if (oppFace.mark == DELETED)
517 {
throw new InternalErrorException (
518 "face " + getVertexString() +
": " +
519 "opposite face " + oppFace.getVertexString() +
522 double d = Math.abs(distanceToPlane(hedge.head().pnt));
529 while (hedge != he0);
531 if (numv != numVerts)
532 {
throw new InternalErrorException (
533 "face " + getVertexString() +
" numVerts=" + numVerts +
" should be " + numv);
545 public int mergeAdjacentFace (HalfEdge hedgeAdj,
548 Face oppFace = hedgeAdj.oppositeFace();
549 int numDiscarded = 0;
551 discarded[numDiscarded++] = oppFace;
552 oppFace.mark = DELETED;
554 HalfEdge hedgeOpp = hedgeAdj.getOpposite();
556 HalfEdge hedgeAdjPrev = hedgeAdj.prev;
557 HalfEdge hedgeAdjNext = hedgeAdj.next;
558 HalfEdge hedgeOppPrev = hedgeOpp.prev;
559 HalfEdge hedgeOppNext = hedgeOpp.next;
561 while (hedgeAdjPrev.oppositeFace() == oppFace)
562 { hedgeAdjPrev = hedgeAdjPrev.prev;
563 hedgeOppNext = hedgeOppNext.next;
566 while (hedgeAdjNext.oppositeFace() == oppFace)
567 { hedgeOppPrev = hedgeOppPrev.prev;
568 hedgeAdjNext = hedgeAdjNext.next;
573 for (hedge=hedgeOppNext; hedge!=hedgeOppPrev.next; hedge=hedge.next)
578 { he0 = hedgeAdjNext;
584 discardedFace = connectHalfEdges (hedgeOppPrev, hedgeAdjNext);
585 if (discardedFace !=
null)
586 { discarded[numDiscarded++] = discardedFace;
590 discardedFace = connectHalfEdges (hedgeAdjPrev, hedgeOppNext);
591 if (discardedFace !=
null)
592 { discarded[numDiscarded++] = discardedFace;
595 computeNormalAndCentroid ();
608 private double areaSquared (HalfEdge hedge0, HalfEdge hedge1)
614 Point3d p0 = hedge0.tail().pnt;
615 Point3d p1 = hedge0.head().pnt;
616 Point3d p2 = hedge1.head().pnt;
618 double dx1 = p1.x - p0.x;
619 double dy1 = p1.y - p0.y;
620 double dz1 = p1.z - p0.z;
622 double dx2 = p2.x - p0.x;
623 double dy2 = p2.y - p0.y;
624 double dz2 = p2.z - p0.z;
626 double x = dy1*dz2 - dz1*dy2;
627 double y = dz1*dx2 - dx1*dz2;
628 double z = dx1*dy2 - dy1*dx2;
630 return x*x + y*y + z*z;
639 public void triangulate (FaceList newFaces,
double minArea)
643 if (numVertices() < 4)
647 Vertex v0 = he0.head();
648 Face prevFace =
null;
651 HalfEdge oppPrev = hedge.opposite;
654 for (hedge=hedge.next; hedge!=he0.prev; hedge=hedge.next)
656 createTriangle (v0, hedge.prev.head(), hedge.head(), minArea);
657 face.he0.next.setOpposite (oppPrev);
658 face.he0.prev.setOpposite (hedge.opposite);
665 hedge =
new HalfEdge (he0.prev.prev.head(),
this);
666 hedge.setOpposite (oppPrev);
669 hedge.prev.next = hedge;
671 hedge.next = he0.prev;
672 hedge.next.prev = hedge;
674 computeNormalAndCentroid (minArea);
677 for (Face face=face0; face!=
null; face=face.next)
678 { face.checkConsistency();