public static void checkNakedEdge( IVec2[] pts, ArrayList<FaceIndex> faceIdx, ArrayList<IVec2[]> triangles, EdgeCounter edgeCount) { for (int i = 0; i < pts.length - 1; i++) { // IOut.p(i+"/"+pts.length); // for (int j = i + 1; j < pts.length; j++) { if (edgeCount.getEdgeNum(i, j) == 1) { boolean added = false; // for(int k=j+1; k<pts.length && !added; k++){ for (int k = 0; k < pts.length && !added; k++) { // fill triangle at the edge or, fill triangle in an obvious triangular hole // not filling when 3 edges already form a triangle FaceIndex fidx = null; boolean addFace = false; if (k != i && k != j) { int idx1 = i; int idx2 = j; int idx3 = k; // re-order if (k < i) { idx1 = k; idx2 = i; idx3 = j; } else if (k < j) { idx1 = i; idx2 = k; idx3 = j; } /* IOut.p("checking <"+i+","+j+","+k+">"); // IOut.p("isEdgeOnOutline("+idx1+","+idx2+") = "+ edgeCount.isEdgeOnOutline(idx1,idx2)); IOut.p("isEdgeOnOutline("+idx1+","+idx3+") = "+ edgeCount.isEdgeOnOutline(idx1,idx3)); IOut.p("isEdgeOnOutline("+idx2+","+idx3+") = "+ edgeCount.isEdgeOnOutline(idx2,idx3)); IOut.p("isFaceDirectionOnEdgeCorrect("+idx1+","+idx2+","+idx3+") = "+ isFaceDirectionOnEdgeCorrect(pts[idx1],pts[idx2],pts[idx3])); IOut.p("isFaceDirectionOnEdgeCorrect("+idx1+","+idx3+","+idx2+") = "+ isFaceDirectionOnEdgeCorrect(pts[idx1],pts[idx3],pts[idx2])); IOut.p("isFaceDirectionOnEdgeCorrect("+idx2+","+idx3+","+idx1+") = "+ isFaceDirectionOnEdgeCorrect(pts[idx2],pts[idx3],pts[idx1])); IOut.p("isEdgeOpen("+idx1+","+idx2+") = "+ edgeCount.isEdgeOpen(idx1,idx2)); IOut.p("isEdgeOpen("+idx1+","+idx3+") = "+ edgeCount.isEdgeOpen(idx1,idx3)); IOut.p("isEdgeOpen("+idx2+","+idx3+") = "+ edgeCount.isEdgeOpen(idx2,idx3)); */ if (edgeCount.isEdgeOnOutline(idx1, idx2) && edgeCount.isEdgeOnOutline(idx2, idx3) && edgeCount.hasOneFace(idx1, idx3) && // isFaceDirectionOnEdgeCorrect(pts[idx1],pts[idx2],pts[idx3])){ // if idx1==0, idx2 is last point on outline and order should be idx2->idx1 isFaceDirectionOnEdgeCorrect( pts[idx1 == 0 ? idx2 : idx1], pts[idx1 == 0 ? idx1 : idx2], pts[idx3])) { // IOut.p("addFace 4"); // addFace = true; } else if (edgeCount.isEdgeOnOutline(idx1, idx3) && edgeCount.isEdgeOnOutline(idx2, idx3) && edgeCount.hasOneFace(idx1, idx2) && // isFaceDirectionOnEdgeCorrect(pts[idx1],pts[idx3],pts[idx2])){ isFaceDirectionOnEdgeCorrect( pts[idx1 == 0 ? idx3 : idx1], pts[idx1 == 0 ? idx1 : idx3], pts[idx2])) { // IOut.p("addFace 5"); // addFace = true; } else if (edgeCount.isEdgeOnOutline(idx1, idx3) && edgeCount.isEdgeOnOutline(idx1, idx2) && edgeCount.hasOneFace(idx2, idx3) && // isFaceDirectionOnEdgeCorrect(pts[idx2],pts[idx3],pts[idx1])){ isFaceDirectionOnEdgeCorrect( pts[idx2 == 0 ? idx3 : idx2], pts[idx2 == 0 ? idx2 : idx3], pts[idx1])) { // IOut.p("addFace 6"); // addFace = true; } else if (edgeCount.isEdgeOnOutline(idx1, idx2) && // (edgeCount.isEdgeOpen(idx1,idx3) || edgeCount.isEdgeOpen(idx2,idx3))){ edgeCount.isEdgeOpen(idx1, idx3) && edgeCount.isEdgeOpen(idx2, idx3)) { if (isFaceDirectionOnEdgeCorrect( pts[idx1 == 0 ? idx2 : idx1], pts[idx1 == 0 ? idx1 : idx2], pts[idx3]) && !isFaceCrossing(pts, edgeCount, idx1, idx2, idx3)) { // IOut.p("addFace 1"); // addFace = true; } } else if (edgeCount.isEdgeOnOutline(idx1, idx3) && // (edgeCount.isEdgeOpen(idx1,idx2)||edgeCount.isEdgeOpen(idx2,idx3))){ edgeCount.isEdgeOpen(idx1, idx2) && edgeCount.isEdgeOpen(idx2, idx3)) { if (isFaceDirectionOnEdgeCorrect( pts[idx1 == 0 ? idx3 : idx1], pts[idx1 == 0 ? idx1 : idx3], pts[idx2]) && !isFaceCrossing(pts, edgeCount, idx1, idx2, idx3)) { // IOut.p("addFace 2"); // addFace = true; } } else if (edgeCount.isEdgeOnOutline(idx2, idx3) && // (edgeCount.isEdgeOpen(idx1,idx2)||edgeCount.isEdgeOpen(idx1,idx3))){ edgeCount.isEdgeOpen(idx1, idx2) && edgeCount.isEdgeOpen(idx1, idx3)) { // IOut.p("isFaceCrossing("+idx1+","+idx2+","+idx3+") = "+isFaceCrossing(pts, // edgeCount, idx1, idx2, idx3)); // if (isFaceDirectionOnEdgeCorrect( pts[idx2 == 0 ? idx3 : idx2], pts[idx2 == 0 ? idx2 : idx3], pts[idx1]) && !isFaceCrossing(pts, edgeCount, idx1, idx2, idx3)) { // IOut.p("addFace 3 <"+idx2+","+idx3+","+idx1+">"); // addFace = true; } } else if ((edgeCount.getEdgeNum(idx1, idx2) == 1 && edgeCount.getEdgeNum(idx1, idx3) == 1 && edgeCount.getEdgeNum(idx2, idx3) == 1 && edgeCount.getFaceVertexIndex(idx1, idx2) != -1 && edgeCount.getFaceVertexIndex(idx1, idx3) != -1 && edgeCount.getFaceVertexIndex(idx2, idx3) != -1 && ((fidx = findTriangleIndexWithEdge(faceIdx, idx1, idx2)) == null || fidx.getOtherIndex(idx1, idx2) != idx3))) { // IOut.p("addFace 7"); // addFace = true; } if (addFace) { // IOut.p("face added at <"+i+","+j+","+k+">"); // // edgeCount.addFace(i,j,k); // triangles.add(new IVec2[]{ pts[i],pts[j],pts[k]}); // faceIdx.add(new FaceIndex(i,j,k)); // IOut.p("face added at <"+idx1+","+idx2+","+idx3+">"); // edgeCount.addFace(idx1, idx2, idx3); triangles.add(new IVec2[] {pts[idx1], pts[idx2], pts[idx3]}); faceIdx.add(new FaceIndex(idx1, idx2, idx3)); added = true; } } /* if((edgeCount.getEdgeNum(i,k)==1&&edgeCount.getEdgeNum(j,k)==1 || edgeCount.getEdgeNum(i,k)==1&&edgeCount.getEdgeNum(j,k)==0 || edgeCount.getEdgeNum(i,k)==0&&edgeCount.getEdgeNum(j,k)==1 ) && (edgeCount.getFaceVertexIndex(i,j)==-1 || edgeCount.getFaceVertexIndex(i,k)==-1 || edgeCount.getFaceVertexIndex(j,k)==-1 ) || ( edgeCount.getEdgeNum(i,k)==1&&edgeCount.getEdgeNum(j,k)==1 && edgeCount.getFaceVertexIndex(i,j)!=-1 && edgeCount.getFaceVertexIndex(i,k)!=-1 && edgeCount.getFaceVertexIndex(j,k)!=-1 && ((fidx = findTriangleIndexWithEdge(faceIdx, i, j)) ==null || fidx.getOtherIndex(i,j) != k)) ){ IOut.p("face added at <"+i+","+j+","+k+">"); // edgeCount.addFace(i,j,k); triangles.add(new IVec2[]{ pts[i],pts[j],pts[k]}); faceIdx.add(new FaceIndex(i,j,k)); added=true; } */ } } } } // remove triangles which have naked edge but not on the outline // (likely to be inside of inner holes or outside of outer trim for (int i = 0; i < pts.length - 1; i++) { for (int j = i + 1; j < pts.length; j++) { if (edgeCount.getEdgeNum(i, j) == 1 && edgeCount.getFaceVertexIndex(i, j) != -1) { // IOut.p("naked edge found at <"+i+","+j+">"); // FaceIndex fidx = findTriangleIndexWithEdge(faceIdx, i, j); if (fidx != null) { int k = fidx.getOtherIndex(i, j); IVec2[] tr = findTriangleWithIndex(pts, triangles, i, j, k); if (tr != null) { triangles.remove(tr); // IOut.p("face removed at <"+i+","+j+","+k+">"); // } } } } } }
/** * Calculates Delaunay triangles out of array of 2D points * * @param innerPts points inside edges defined by edgePts * @param edgePts points on the edge (the edge should be naked having only one triangle touching); * order of edge points needs to be counter clockwise for outer trim loop, clockwise for inner * hole trim loop. * @return array of triangles, which consist of array of 3 points of IVec2 */ public static IVec2[][] getTriangles(IVec2[] innerPts, IVec2[][] edgePts) { int innerPtsNum = 0; int edgePtsNum = 0; if (innerPts != null) innerPtsNum = innerPts.length; if (edgePts != null) { for (int i = 0; i < edgePts.length; i++) edgePtsNum += edgePts[i].length; } IVec2[] pts = new IVec2[innerPtsNum + edgePtsNum]; if (innerPts != null) { for (int i = 0; i < innerPts.length; i++) { pts[i] = innerPts[i]; // IOut.p("pts["+i+"] is innerPts "+innerPts[i]); // } } int[][] edgePtIdx = null; if (edgePts != null) { edgePtIdx = new int[edgePts.length][]; int ptIdx = innerPtsNum; for (int i = 0; i < edgePts.length; i++) { edgePtIdx[i] = new int[edgePts[i].length]; for (int j = 0; j < edgePts[i].length; j++) { // IOut.p("ptIdx="+ptIdx+" / pts.length="+pts.length); // // IOut.p("pts["+ptIdx+"] is edgePts "+edgePts[i][j]); // pts[ptIdx] = edgePts[i][j]; edgePtIdx[i][j] = ptIdx; ptIdx++; } } } if (pts.length == 3) { if (isClockwise(pts[0], pts[1], pts[2])) return new IVec2[][] {new IVec2[] {pts[0], pts[2], pts[1]}}; return new IVec2[][] {new IVec2[] {pts[0], pts[1], pts[2]}}; } ArrayList<IVec2[]> triangles = new ArrayList<IVec2[]>(); ArrayList<FaceIndex> triangleIndex = new ArrayList<FaceIndex>(); // IOut.p("pts.length = "+pts.length); // // for(int i=0; i<pts.length; i++) IOut.p("pts["+i+"] = "+ pts[i]); // EdgeCounter edgeCount = new EdgeCounter(pts.length); for (int i = 0; i < edgePtIdx.length; i++) { for (int j = 0; j < edgePtIdx[i].length; j++) { edgeCount.addEdge(edgePtIdx[i][j], edgePtIdx[i][(j + 1) % edgePtIdx[i].length]); } } // in case a point on edges are on other loop's edge for (int i = 0; i < edgePtIdx.length; i++) { for (int j = 0; j < edgePtIdx[i].length; j++) { int ept1 = edgePtIdx[i][j]; int ept2 = edgePtIdx[i][(j + 1) % edgePtIdx[i].length]; for (int k = 0; k < edgePtIdx.length; k++) { if (k != i) { for (int l = 0; l < edgePtIdx[k].length; l++) { if (pts[edgePtIdx[k][l]].isStraight(pts[ept1], pts[ept2]) && pts[edgePtIdx[k][l]].isBetween(pts[ept1], pts[ept2])) { edgeCount.addEdge(ept1, edgePtIdx[k][l]); edgeCount.addEdge(ept2, edgePtIdx[k][l]); } } } } } } for (int i = 0; i < pts.length - 2; i++) { // IOut.p((i+1)+"/"+(pts.length-2)); // for (int j = i + 1; j < pts.length - 1; j++) { for (int k = j + 1; k < pts.length && edgeCount.isEdgeOpen(i, j); k++) { boolean anyInside = false; // IOut.p("<"+i+","+j+","+k+">"); // // IOut.p("isFaceCrossing = "+ isFaceCrossing(pts, edgeCount, i, j, k)); // IOut.p("checkDirectionOnEdgePoints = // "+checkDirectionOnEdgePoints(pts,edgePts,i,j,k,innerPtsNum) ); // IOut.p("isStraight = "+pts[i].isStraight(pts[j],pts[k])); // if (edgeCount.isEdgeOpen(i, k) && edgeCount.isEdgeOpen(j, k) && !isFaceCrossing(pts, edgeCount, i, j, k) // && checkFaceDirection(pts, edgeCount, i, j, k) && checkDirectionOnEdgePoints(pts, edgePts, i, j, k, innerPtsNum) // added && !pts[i].isStraight(pts[j], pts[k]) // added ) { for (int l = 0; l < pts.length && !anyInside; l++) if (l != i && l != j && l != k) { // IOut.p("<"+i+","+j+","+k+","+l+">"); // if (isInsideCircumcircle(pts[l], pts[i], pts[j], pts[k])) { // check if case pts[l] is crossing edge of trim lines if (checkDirectionOfInnerCircumcirclePoint( pts, edgePts, i, j, k, l, innerPtsNum)) { anyInside = true; // IOut.p(pts[l] + " is inside <"+pts[i]+", "+pts[j]+", "+pts[k]+">"); // // IOut.p("pt["+l+"] is INside pt["+i+"], pt["+j+"], pt["+k+"]"); // } // else; // when pts[l] is crossing the edge of trim } else { // IOut.p(pts[l] + " is outside <"+pts[i]+", "+pts[j]+", "+pts[k]+">"); // // IOut.p("pt["+l+"] is OUTside pt["+i+"], pt["+j+"], pt["+k+"]"); // } } if (!anyInside) { // nothing is inside // IOut.p("<"+i+","+j+","+k+">: triangle is added at "+triangles.size()); // if (isClockwise(pts[i], pts[j], pts[k])) { triangles.add(new IVec2[] {pts[i], pts[k], pts[j]}); triangleIndex.add(new FaceIndex(i, k, j)); } else { triangles.add(new IVec2[] {pts[i], pts[j], pts[k]}); triangleIndex.add(new FaceIndex(i, j, k)); } /* if(i==0&&j==1&&k==4){ new IGCurve(new IVec[]{ new IVec(pts[i]), new IVec(pts[j]), new IVec(pts[k])}); } */ edgeCount.addFace(i, j, k); // IOut.p("edgeCount.isEdgeOnOutline("+i+","+j+") = "+edgeCount.isEdgeOnOutline(i,j)); // IOut.p("edgeCount.getEdgeNum("+i+","+j+") = "+edgeCount.getEdgeNum(i,j)); // IOut.p("edgeCount.getFaceVertexIndex("+i+","+j+") = // "+edgeCount.getFaceVertexIndex(i,j)); // edgeCount.addEdge(i,j); // edgeCount.addEdge(i,k); // edgeCount.addEdge(j,k); } // else IOut.p("pt out"); // } } } } // filling holes checkNakedEdge(pts, triangleIndex, triangles, edgeCount); // IOut.p("trinangles.size()="+triangles.size()); // return triangles.toArray(new IVec2[triangles.size()][]); }