@Override
  public double computeDistance(PointVectorNN pv) {
    n.set(pv.p.x - model.center.x, pv.p.y - model.center.y, pv.p.z - model.center.z);
    n.normalize();

    if (Math.abs(n.dot(pv.normal)) < tolAngleCosine) return Double.MAX_VALUE;

    return Math.abs(Distance3D_F64.distance(model, pv.p));
  }
  @Override
  public boolean generate(List<PointVectorNN> dataSet, Cylinder3D_F64 output) {

    PointVectorNN pa = dataSet.get(0);
    PointVectorNN pb = dataSet.get(1);
    PointVectorNN pc = dataSet.get(2);

    lineA.p = pa.p;
    lineA.slope = pa.normal;

    lineB.p = pb.p;
    lineB.slope = pb.normal;

    // With perfect data, the closest point between the two lines defined by a point and its normal
    // will lie on the axis of the cylinder
    ClosestPoint3D_F64.closestPoint(lineA, lineB, output.line.p);
    // The cylinder's axis will be along the cross product of the two normal vectors
    GeometryMath_F64.cross(pa.normal, pb.normal, output.line.slope);

    // take the normal of the slope so that it can detect if it has a value of zero, which happens
    // if the
    // two input vectors are the same
    double n = output.line.slope.norm();
    // n should actually always be one since the point normals are normalized to one.
    if (n < 1e-8) return false;
    output.line.slope.x /= n;
    output.line.slope.y /= n;
    output.line.slope.z /= n;

    // set the radius to be the average distance point is from the cylinder's axis
    double ra = Distance3D_F64.distance(output.line, pa.p);
    double rb = Distance3D_F64.distance(output.line, pb.p);
    double rc = Distance3D_F64.distance(output.line, pc.p);

    output.radius = (ra + rb) / 2.0;

    // sanity check using the model parameters
    if (!check.valid(output)) return false;

    // sanity check the model using points
    return checkModel(output, pc, ra, rb, rc);
  }