/**
   * <code>mergeLocal</code> combines this sphere with a second bounding sphere locally. Altering
   * this sphere to contain both the original and the additional sphere volumes;
   *
   * @param volume the sphere to combine with this sphere.
   * @return this
   */
  public BoundingVolume mergeLocal(BoundingVolume volume) {
    if (volume == null) {
      return this;
    }

    switch (volume.getType()) {
      case Sphere:
        {
          BoundingSphere sphere = (BoundingSphere) volume;
          float temp_radius = sphere.getRadius();
          Vector3f temp_center = sphere.center;
          return merge(temp_radius, temp_center, this);
        }

      case AABB:
        {
          BoundingBox box = (BoundingBox) volume;
          Vector3f radVect = Vector3f.newInstance();
          radVect.set(box.xExtent, box.yExtent, box.zExtent);
          Vector3f temp_center = box.center;
          float len = radVect.length();
          Vector3f.recycle(radVect);
          return merge(len, temp_center, this);
        }

        // case OBB: {
        // return mergeOBB((OrientedBoundingBox) volume);
        // }
      default:
        return null;
    }
  }
  /**
   * <code>merge</code> combines this sphere with a second bounding sphere. This new sphere contains
   * both bounding spheres and is returned.
   *
   * @param volume the sphere to combine with this sphere.
   * @return a new sphere
   */
  public BoundingVolume merge(BoundingVolume volume) {
    if (volume == null) {
      return this;
    }

    switch (volume.getType()) {
      case Sphere:
        {
          BoundingSphere sphere = (BoundingSphere) volume;
          float temp_radius = sphere.getRadius();
          Vector3f temp_center = sphere.center;
          BoundingSphere rVal = new BoundingSphere();
          return merge(temp_radius, temp_center, rVal);
        }

      case AABB:
        {
          BoundingBox box = (BoundingBox) volume;
          Vector3f radVect = new Vector3f(box.xExtent, box.yExtent, box.zExtent);
          Vector3f temp_center = box.center;
          BoundingSphere rVal = new BoundingSphere();
          return merge(radVect.length(), temp_center, rVal);
        }

        // case OBB: {
        // OrientedBoundingBox box = (OrientedBoundingBox) volume;
        // BoundingSphere rVal = (BoundingSphere) this.clone(null);
        // return rVal.mergeOBB(box);
        // }
      default:
        return null;
    }
  }
  /**
   * <code>clone</code> creates a new BoundingSphere object containing the same data as this one.
   *
   * @param store where to store the cloned information. if null or wrong class, a new store is
   *     created.
   * @return the new BoundingSphere
   */
  public BoundingVolume clone(BoundingVolume store) {
    if (store != null && store.getType() == Type.Sphere) {
      BoundingSphere rVal = (BoundingSphere) store;
      if (null == rVal.center) {
        rVal.center = new Vector3f();
      }
      rVal.center.set(center);
      rVal.radius = radius;
      rVal.checkPlane = checkPlane;
      return rVal;
    }

    return new BoundingSphere(radius, (center != null ? (Vector3f) center.clone() : null));
  }
  public BoundingVolume transform(Matrix4f trans, BoundingVolume store) {
    BoundingSphere sphere;
    if (store == null || store.getType() != BoundingVolume.Type.Sphere) {
      sphere = new BoundingSphere(1, new Vector3f(0, 0, 0));
    } else {
      sphere = (BoundingSphere) store;
    }

    trans.mult(center, sphere.center);
    Vector3f axes = new Vector3f(1, 1, 1);
    trans.mult(axes, axes);
    float ax = getMaxAxis(axes);
    sphere.radius = FastMath.abs(ax * radius) + RADIUS_EPSILON - 1f;
    return sphere;
  }
  /*
   * (non-Javadoc)
   * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
   */
  public boolean intersectsSphere(BoundingSphere bs) {
    assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);

    Vector3f vect1 = Vector3f.newInstance();
    Vector3f diff = center.subtract(bs.center, vect1);
    float rsum = getRadius() + bs.getRadius();
    boolean eq = (diff.dot(diff) <= rsum * rsum);
    Vector3f.recycle(vect1);
    return eq;
  }
  // /**
  // * Merges this sphere with the given OBB.
  // *
  // * @param volume
  // * The OBB to merge.
  // * @return This sphere, after merging.
  // */
  // private BoundingSphere mergeOBB(OrientedBoundingBox volume) {
  // // compute edge points from the obb
  // if (!volume.correctCorners)
  // volume.computeCorners();
  // _mergeBuf.rewind();
  // for (int i = 0; i < 8; i++) {
  // _mergeBuf.put(volume.vectorStore[i].x);
  // _mergeBuf.put(volume.vectorStore[i].y);
  // _mergeBuf.put(volume.vectorStore[i].z);
  // }
  //
  // // remember old radius and center
  // float oldRadius = radius;
  // Vector3f oldCenter = _compVect2.set( center );
  //
  // // compute new radius and center from obb points
  // computeFromPoints(_mergeBuf);
  // Vector3f newCenter = _compVect3.set( center );
  // float newRadius = radius;
  //
  // // restore old center and radius
  // center.set( oldCenter );
  // radius = oldRadius;
  //
  // //merge obb points result
  // merge( newRadius, newCenter, this );
  //
  // return this;
  // }
  private BoundingVolume merge(float temp_radius, Vector3f temp_center, BoundingSphere rVal) {
    Vector3f vect1 = Vector3f.newInstance();
    Vector3f diff = temp_center.subtract(center, vect1);
    float lengthSquared = diff.lengthSquared();
    float radiusDiff = temp_radius - radius;

    float fRDiffSqr = radiusDiff * radiusDiff;

    if (fRDiffSqr >= lengthSquared) {
      if (radiusDiff <= 0.0f) {
        Vector3f.recycle(vect1);
        return this;
      }

      Vector3f rCenter = rVal.center;
      if (rCenter == null) {
        rVal.setCenter(rCenter = new Vector3f());
      }
      rCenter.set(temp_center);
      rVal.setRadius(temp_radius);
      Vector3f.recycle(vect1);
      return rVal;
    }

    float length = (float) Math.sqrt(lengthSquared);

    Vector3f rCenter = rVal.center;
    if (rCenter == null) {
      rVal.setCenter(rCenter = new Vector3f());
    }
    if (length > RADIUS_EPSILON) {
      float coeff = (length + radiusDiff) / (2.0f * length);
      rCenter.set(center.addLocal(diff.multLocal(coeff)));
    } else {
      rCenter.set(center);
    }

    rVal.setRadius(0.5f * (length + radius + temp_radius));
    Vector3f.recycle(vect1);
    return rVal;
  }