/** * Compare two windings, taking into account that start points may not match * * @param that other winding * @return true if it matches this winding */ public boolean matches(Winding that) { final int size = verts.size(); // if windings have different number of points, trivially fail if (size != that.verts.size()) { return false; } // minimum match distance float min = 1e6f; for (int i = 0; i < size; i++) { float mdist = 0; // get the aggregate distance at offset i for (int j = 0; j < size; j++) { // wrap index if greater than size int k = (j + i) % size; // distance between vertex j of this and k of that mdist += verts.get(j).sub(that.verts.get(k)).length(); } // update minimum match distance min = Math.min(min, mdist); } // check if match was close enough return min < EPS_COMP; }
/** * Returns the total area of this winding. * * @return total area */ public float getArea() { float total = 0; final int size = verts.size(); for (int i = 2; i < size; i++) { Vector3f v1 = verts.get(i - 1).sub(verts.get(0)); Vector3f v2 = verts.get(i).sub(verts.get(0)); total += v1.cross(v2).length(); } return total * 0.5f; }
/** * Returns the center point (barycenter) of this winding. * * <p>Equals WindingCenter() in polylib.cpp * * @return */ public Vector3f getCenter() { Vector3f sum = Vector3f.NULL; // add all verts for (Vector3f vert : verts) { sum = sum.add(vert); } // average vertex position return sum.scalar(1f / verts.size()); }
/** * Checks if this winding contains any duplicate vertices. * * @return true if this winding contains duplicate vertices */ public boolean hasDuplicates() { final int size = verts.size(); for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { if (i == j) { continue; } Vector3f v1 = verts.get(i); Vector3f v2 = verts.get(j); if (v1.equals(v2)) { return true; } } } return false; }
/** * Removes degenerated vertices from this winding. A vertex is degenerated when its distance to * the previous vertex is smaller than {@link EPS_DEGEN}. * * @return number of removed vertices */ public Winding removeDegenerated() { if (verts.isEmpty()) { return this; } ArrayList<Vector3f> vertsNew = new ArrayList<>(); final int size = verts.size(); for (int i = 0; i < size; i++) { int j = (i + 1) % size; Vector3f v1 = verts.get(i); Vector3f v2 = verts.get(j); if (v1.sub(v2).length() > EPS_DEGEN) { vertsNew.add(v1); } } return new Winding(vertsNew); }
/** * Removes collinear vertices from this winding. * * @return number of removed vertices */ public Winding removeCollinear() { if (verts.isEmpty()) { return this; } ArrayList<Vector3f> vertsNew = new ArrayList<>(); final int size = verts.size(); for (int i = 0; i < size; i++) { int j = (i + 1) % size; int k = (i + size - 1) % size; Vector3f v1 = verts.get(j).sub(verts.get(i)).normalize(); Vector3f v2 = verts.get(i).sub(verts.get(k)).normalize(); if (v1.dot(v2) < 0.999) { vertsNew.add(verts.get(i)); } } return new Winding(vertsNew); }
/** * Rotates all vertices in this winding by the given euler angles. * * @param angles rotation angles */ public Winding rotate(Vector3f angles) { if (verts.isEmpty()) { return this; } ArrayList<Vector3f> vertsNew = new ArrayList<>(); for (Vector3f vert : verts) { vertsNew.add(vert.rotate(angles)); } return new Winding(vertsNew); }
public Winding translate(Vector3f offset) { if (verts.isEmpty()) { return this; } ArrayList<Vector3f> vertsNew = new ArrayList<>(); for (Vector3f vert : verts) { vertsNew.add(vert.add(offset)); } return new Winding(vertsNew); }
/** * Returns the plane points of this winding in form of a triangle. * * @return Vector3f array with three points of the triangle */ public Vector3f[] buildPlane() { Vector3f[] vertsNew = new Vector3f[verts.size()]; Vector3f[] plane = new Vector3f[3]; // 1st vert is always base vertex plane[0] = get(0); // build vector list for (int i = 0; i < vertsNew.length; i++) { // the vector from start vertex to i'th vertsNew[i] = get(i).sub(plane[0]); } // the largest modulus of cross product found between ixj float maxmcp = -1; // the i index of largest cp int imax = -1; // the j index of largest cp int jmax = -1; // loop through all i x j combinations for (int i = 1; i < vertsNew.length; i++) { // ensures j>i for (int j = i + 1; j < vertsNew.length; j++) { float mcp = vertsNew[i].cross(vertsNew[j]).length(); if (mcp > maxmcp) { maxmcp = mcp; imax = i; jmax = j; } } } // choose other two such that cross product is maximum plane[1] = get(imax); plane[2] = get(jmax); return plane; }
public Winding addBackface() { if (verts.isEmpty()) { return this; } List<Vector3f> vertsNew = new ArrayList<>(); final int size = verts.size(); for (int i = 0; i < size; i++) { if (i != 0) { vertsNew.add(verts.get(i)); } if (i != size) { vertsNew.add(verts.get(i)); } } return new Winding(vertsNew); }
@Override public int indexOf(Object o) { return verts.indexOf(o); }
@Override public Vector3f get(int index) { return verts.get(index); }
@Override public boolean containsAll(Collection<?> c) { return verts.containsAll(c); }
@Override public <T> T[] toArray(T[] a) { return verts.toArray(a); }
@Override public Object[] toArray() { return verts.toArray(); }
@Override public Iterator<Vector3f> iterator() { return verts.iterator(); }
/** * Clips this winding to a plane defined by a normal and distance, removing all vertices in front * or behind it. * * <p>Equals ClipWindingEpsilon() in polylib.cpp * * @param normal plane normal * @param dist plane distance to origin * @param eps clipping epsilon * @param back keep vertices behind the plane? */ public Winding clipEpsilon(Vector3f normal, float dist, float eps, boolean back) { // counts number of front, back and on vertices int[] counts = new int[] {0, 0, 0}; final int size = verts.size(); float[] dists = new float[size + 1]; int[] sides = new int[size + 1]; // determine sides for each point for (int i = 0; i < size; i++) { // distance along norm-dirn from origin to vertex float dot = verts.get(i).dot(normal); // distance along norm-dirn from clip plane to vertex dot -= dist; // store it dists[i] = dot; if (dot > eps) { // vertex in front of plane sides[i] = SIDE_FRONT; } else if (dot < -eps) { // vertex behind plane sides[i] = SIDE_BACK; } else { // vertex on plane (within epsilon) sides[i] = SIDE_ON; } // count relative vertex positions counts[sides[i]]++; } sides[size] = sides[0]; // loop around to 0'th dists[size] = dists[0]; if (counts[SIDE_FRONT] == 0) { // no vertices in front - all behind clip plane if (!back) { return EMPTY; } else { return this; } } if (counts[SIDE_BACK] == 0) { // no vertices in back - all in front of clip plane if (back) { return EMPTY; } else { return this; } } List<Vector3f> vertsNew = new ArrayList<Vector3f>(); for (int i = 0; i < size; i++) { // get i'th vertex Vector3f p1 = verts.get(i); if (sides[i] == SIDE_ON) { vertsNew.add(p1); continue; } if (sides[i] == SIDE_FRONT && !back) { // add copy the current vertex vertsNew.add(p1); } if (sides[i] == SIDE_BACK && back) { // add copy the current vertex vertsNew.add(p1); } if (sides[i + 1] == SIDE_ON) { // next vertex is on the plane, so go to next vertex stat continue; } if (sides[i + 1] == sides[i]) { // next vertex does not change side, so go to next vertex stat continue; } // otherwise, we are crossing the clip plane between this vertex and the next // so generate a split point // will contain the next vertex position Vector3f p2; if (i == size - 1) { // we're the last vertex in the winding // next vertex is the 0'th one p2 = verts.get(0); } else { // else get the next vertex p2 = verts.get(i + 1); } // dot is fractional position of clip plane between // this vertex and the next float dot = dists[i] / (dists[i] - dists[i + 1]); // vector of the split vertex Vector3f mv = Vector3f.NULL; for (int j = 0; j < normal.size; j++) { // avoid round off error when possible if (normal.get(j) == 1) { mv = mv.set(j, dist); } else if (normal.get(j) == -1) { mv = mv.set(j, -dist); } else { // check it! MSH mv = mv.set(j, p1.get(j) + dot * (p2.get(j) - p1.get(j))); } } // write the output vertex vertsNew.add(mv); } return new Winding(vertsNew); }
@Override public ListIterator<Vector3f> listIterator(int index) { return verts.listIterator(index); }
@Override public int lastIndexOf(Object o) { return verts.lastIndexOf(o); }
@Override public ListIterator<Vector3f> listIterator() { return verts.listIterator(); }
@Override public int size() { return verts.size(); }
@Override public List<Vector3f> subList(int fromIndex, int toIndex) { return verts.subList(fromIndex, toIndex); }
@Override public boolean isEmpty() { return verts.isEmpty(); }
@Override public String toString() { return verts.toString(); }
@Override public boolean contains(Object o) { return verts.contains(o); }