public static final double pointSegmentDistance( final Point3D p, final Point3D q1, final Point3D q2) { if (p.equals(q1) || p.equals(q2)) return 0; if (q1.equals(q2)) return p.distance(q1); // line segment: g = q1 + t * (q2 - q1), 0 <= t <= 1 final Point3D q = minus(q2, q1); // projection on line: P(p,g) = q1 + t * q final double t = scalarProduct(minus(p, q1), q) / scalarProduct(q, q); if (t < 0) return p.distance(q1); else if (t > 1) return p.distance(q2); else return p.distance(plus(q1, multiply(t, q))); }
private void optimizeFaces() { int total = 0, sameIndexes = 0, samePoints = 0, smallArea = 0; ObservableIntegerArray newFaces = FXCollections.observableIntegerArray(); ObservableIntegerArray newFaceSmoothingGroups = FXCollections.observableIntegerArray(); for (MeshView meshView : meshViews) { TriangleMesh mesh = (TriangleMesh) meshView.getMesh(); ObservableIntegerArray faces = mesh.getFaces(); ObservableIntegerArray faceSmoothingGroups = mesh.getFaceSmoothingGroups(); ObservableFloatArray points = mesh.getPoints(); newFaces.clear(); newFaces.ensureCapacity(faces.size()); newFaceSmoothingGroups.clear(); newFaceSmoothingGroups.ensureCapacity(faceSmoothingGroups.size()); int pointElementSize = mesh.getPointElementSize(); int faceElementSize = mesh.getFaceElementSize(); for (int i = 0; i < faces.size(); i += faceElementSize) { total++; int i1 = faces.get(i) * pointElementSize; int i2 = faces.get(i + 2) * pointElementSize; int i3 = faces.get(i + 4) * pointElementSize; if (i1 == i2 || i1 == i3 || i2 == i3) { sameIndexes++; continue; } Point3D p1 = new Point3D(points.get(i1), points.get(i1 + 1), points.get(i1 + 2)); Point3D p2 = new Point3D(points.get(i2), points.get(i2 + 1), points.get(i2 + 2)); Point3D p3 = new Point3D(points.get(i3), points.get(i3 + 1), points.get(i3 + 2)); if (p1.equals(p2) || p1.equals(p3) || p2.equals(p3)) { samePoints++; continue; } double a = p1.distance(p2); double b = p2.distance(p3); double c = p3.distance(p1); double p = (a + b + c) / 2; double sqarea = p * (p - a) * (p - b) * (p - c); final float DEAD_FACE = 1.f / 1024 / 1024 / 1024 / 1024; // taken from MeshNormal code if (sqarea < DEAD_FACE) { smallArea++; // System.out.printf("a = %e, b = %e, c = %e, sqarea = %e\n" // + "p1 = %s\np2 = %s\np3 = %s\n", a, b, c, sqarea, // p1.toString(), p2.toString(), p3.toString()); continue; } newFaces.addAll(faces, i, faceElementSize); int fIndex = i / faceElementSize; if (fIndex < faceSmoothingGroups.size()) { newFaceSmoothingGroups.addAll(faceSmoothingGroups.get(fIndex)); } } faces.setAll(newFaces); faceSmoothingGroups.setAll(newFaceSmoothingGroups); faces.trimToSize(); faceSmoothingGroups.trimToSize(); } int badTotal = sameIndexes + samePoints + smallArea; System.out.printf( "Removed %d (%.2f%%) faces with same point indexes, " + "%d (%.2f%%) faces with same points, " + "%d (%.2f%%) faces with small area. " + "Total %d (%.2f%%) bad faces out of %d total.\n", sameIndexes, 100d * sameIndexes / total, samePoints, 100d * samePoints / total, smallArea, 100d * smallArea / total, badTotal, 100d * badTotal / total, total); }