private double[] springPenalty(Vector3d xi, Vector3d xj, double dist) {
    double[] F = new double[6];
    // collision normal
    Vector3d n = xi.minus(xj);
    n = n.dot(1 / n.norm());

    F[0] = -(k * (n.x()) * (-2 * proximity + dist));
    F[1] = -(k * (n.y()) * (-2 * proximity + dist));
    F[2] = -(k * (n.z()) * (-2 * proximity + dist));
    F[3] = -F[0];
    F[4] = -F[1];
    F[5] = -F[2];

    return F;
  }
  private double[] calculatePenaltyForces(double[] X, double[] V, HashSet<Collision> cSet) {
    double[] penalty = new double[X.length];

    // for each collision, apply the penalties
    for (Collision col : cSet) {
      // Collision col = col_rec[i];
      // vertex-face collision
      if (col.getType() == Collision.VERTEX_FACE) {
        //	System.out.println("VERTEX-FACE");
        // get indicies
        int[] parts = col.getParticles();
        int p = parts[0];
        int a = parts[1];
        int b = parts[2];
        int c = parts[3];

        // get barycentric coords
        double[] bc = col.getBaryCoords();
        double u = bc[0];
        double v = bc[1];
        double w = bc[2];

        // get collision point and collision velocities
        Vector3d xa = new Vector3d(); // was Vector3d xa, xb, va, vb;
        Vector3d xb = new Vector3d();
        Vector3d va = new Vector3d();
        Vector3d vb = new Vector3d();

        xa.set(X[3 * p], X[3 * p + 1], X[3 * p + 2]);
        xb.set(
            u * X[3 * a] + v * X[3 * b] + w * X[3 * c],
            u * X[3 * a + 1] + v * X[3 * b + 1] + w * X[3 * c + 1],
            u * X[3 * a + 2] + v * X[3 * b + 2] + w * X[3 * c + 2]);
        va.set(V[3 * p], V[3 * p + 1], V[3 * p + 2]);
        vb.set(
            u * V[3 * a] + v * V[3 * b] + w * V[3 * c],
            u * V[3 * a + 1] + v * V[3 * b + 1] + w * V[3 * c + 1],
            u * V[3 * a + 2] + v * V[3 * b + 2] + w * V[3 * c + 2]);

        // System.out.println("REL VEL "+xa.minus(xb).dot(va.minus(vb))+" "+va+" "+vb+" "+xa+"
        // "+xb);
        // System.out.println("REL VEL2 "+xb.minus(xa).dot(vb.minus(va)));
        /*if the objects have do not have
        a seperating velocity, apply the
        local spring penalty */
        if (xa.minus(xb).dot(va.minus(vb)) < 0) {
          double[] local_penalty = springPenalty(xa, xb, col.getDistance());
          // apply to vertex
          penalty[3 * p] += local_penalty[0];
          penalty[3 * p + 1] += local_penalty[1];
          penalty[3 * p + 2] += local_penalty[2];

          // apply to triangle coordinates
          penalty[3 * a] += bc[0] * local_penalty[3];
          penalty[3 * a + 1] += bc[0] * local_penalty[4];
          penalty[3 * a + 2] += bc[0] * local_penalty[5];
          penalty[3 * b] += bc[1] * local_penalty[3];
          penalty[3 * b + 1] += bc[1] * local_penalty[4];
          penalty[3 * b + 2] += bc[1] * local_penalty[5];
          penalty[3 * c] += bc[2] * local_penalty[3];
          penalty[3 * c + 1] += bc[2] * local_penalty[4];
          penalty[3 * c + 2] += bc[2] * local_penalty[5];
        }
      }

      // edge-edge collision
      if (col.getType() == Collision.EDGE_EDGE) {
        // get indicies
        int[] parts = col.getParticles();
        int p1 = parts[0];
        int q1 = parts[1];
        int p2 = parts[2];
        int q2 = parts[3];

        // get barycentric coords
        double[] coords = col.getBaryCoords();
        double s = coords[0];
        double t = coords[1];

        Vector3d xa = new Vector3d(); // was Vector3d xa, xb, va, vb;
        Vector3d xb = new Vector3d();
        Vector3d va = new Vector3d();
        Vector3d vb = new Vector3d();

        // edge points
        xa.set(
            s * X[3 * p1] + (1 - s) * X[3 * q1],
            s * X[3 * p1 + 1] + (1 - s) * X[3 * q1 + 1],
            s * X[3 * p1 + 2] + (1 - s) * X[3 * q1 + 2]);
        xb.set(
            t * X[3 * p2] + (1 - t) * X[3 * q2],
            t * X[3 * p2 + 1] + (1 - t) * X[3 * q2 + 1],
            t * X[3 * p2 + 2] + (1 - t) * X[3 * q2 + 2]);

        // velocities
        va.set(
            s * V[3 * p1] + (1 - s) * V[3 * q1],
            s * V[3 * p1 + 1] + (1 - s) * V[3 * q1 + 1],
            s * V[3 * p1 + 2] + (1 - s) * V[3 * q1 + 2]);
        vb.set(
            t * V[3 * p2] + (1 - t) * V[3 * q2],
            t * V[3 * p2 + 1] + (1 - t) * V[3 * q2 + 1],
            t * V[3 * p2 + 2] + (1 - t) * V[3 * q2 + 2]);

        /*if the objects have do not have
        a seperating velocity, apply the
        local spring penalty */
        if (xa.minus(xb).dot(va.minus(vb)) < 0) {
          double[] local_penalty = springPenalty(xa, xb, col.getDistance());
          // apply penalty to first edge
          penalty[3 * p1] += s * local_penalty[0];
          penalty[3 * p1 + 1] += s * local_penalty[1];
          penalty[3 * p1 + 2] += s * local_penalty[2];
          penalty[3 * q1] += (1 - s) * local_penalty[0];
          penalty[3 * q1 + 1] += (1 - s) * local_penalty[1];
          penalty[3 * q1 + 2] += (1 - s) * local_penalty[2];
          // apply penalty to second edge
          penalty[3 * p2] += t * local_penalty[3];
          penalty[3 * p2 + 1] += t * local_penalty[4];
          penalty[3 * p2 + 2] += t * local_penalty[5];
          penalty[3 * q2] += (1 - t) * local_penalty[3];
          penalty[3 * q2 + 1] += (1 - t) * local_penalty[4];
          penalty[3 * q2 + 2] += (1 - t) * local_penalty[5];
        }
      }
    }

    return penalty;
  }