示例#1
0
  /**
   * Creates a <code>Frustum</code> from a horizontal field-of-view, viewport aspect ratio and
   * distance to near and far depth clipping planes. The near plane must be closer than the far
   * plane, and both near and far values must be positive.
   *
   * @param horizontalFieldOfView horizontal field-of-view angle in the range (0, 180)
   * @param viewportWidth the width of the viewport in screen pixels
   * @param viewportHeight the height of the viewport in screen pixels
   * @param near distance to the near depth clipping plane
   * @param far distance to far depth clipping plane
   * @return Frustum configured from the specified perspective parameters.
   * @throws IllegalArgumentException if fov is not in the range (0, 180), if either near or far are
   *     negative, or near is greater than or equal to far
   */
  public Frustum setPerspective(
      Angle horizontalFieldOfView,
      double viewportWidth,
      double viewportHeight,
      double near,
      double far) {
    if (horizontalFieldOfView == null) {
      String msg = Logging.getMessage("nullValue.FieldOfViewIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (horizontalFieldOfView.degrees <= 0 || horizontalFieldOfView.degrees > 180) {
      String msg = Logging.getMessage("generic.FieldOfViewIsInvalid", horizontalFieldOfView);
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (viewportWidth < 0) {
      String msg = Logging.getMessage("generic.WidthIsInvalid", viewportWidth);
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (viewportHeight < 0) {
      String msg = Logging.getMessage("generic.HeightIsInvalid", viewportHeight);
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (near <= 0 || near > far) {
      String msg = Logging.getMessage("generic.ClipDistancesAreInvalid", near, far);
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (viewportWidth == 0) viewportWidth = 1;

    if (viewportHeight == 0) viewportHeight = 1;

    if (near == far) far = near + 1;

    double focalLength = 1d / horizontalFieldOfView.tanHalfAngle();
    double aspect = viewportHeight / viewportWidth;
    double lrLen = Math.sqrt(focalLength * focalLength + 1);
    double btLen = Math.sqrt(focalLength * focalLength + aspect * aspect);

    this.left.set(focalLength / lrLen, 0, -1d / lrLen, 0);
    this.right.set(-focalLength / lrLen, 0, -1d / lrLen, 0);
    this.bottom.set(0, focalLength / btLen, -aspect / btLen, 0);
    this.top.set(0, -focalLength / btLen, -aspect / btLen, 0);
    this.near.set(0, 0, -1, -near);
    this.far.set(0, 0, 1, far);

    return this;
  }
示例#2
0
  /**
   * Computes a <code>Box</code> that bounds a specified buffer of points. Principal axes are
   * computed for the points and used to form a <code>Box</code>. This returns <code>null</code> if
   * the buffer is empty or contains only a partial point.
   *
   * <p>The buffer must contain XYZ coordinate tuples which are either tightly packed or offset by
   * the specified stride. The stride specifies the number of buffer elements between the first
   * coordinate of consecutive tuples. For example, a stride of 3 specifies that each tuple is
   * tightly packed as XYZXYZXYZ, whereas a stride of 5 specifies that there are two elements
   * between each tuple as XYZabXYZab (the elements "a" and "b" are ignored). The stride must be at
   * least 3. If the buffer's length is not evenly divisible into stride-sized tuples, this ignores
   * the remaining elements that follow the last complete tuple.
   *
   * @param buffer the buffer containing the point coordinates for which to compute a bounding
   *     volume.
   * @param stride the number of elements between the first coordinate of consecutive points. If
   *     stride is 3, this interprets the buffer has having tightly packed XYZ coordinate tuples.
   * @return the bounding volume, with axes lengths consistent with the conventions described in the
   *     <code>Box</code> class overview.
   * @throws IllegalArgumentException if the buffer is null or empty, or if the stride is less than
   *     three.
   */
  public static Box computeBoundingBox(FloatBuffer buffer, int stride) {
    if (buffer == null) {
      String msg = Logging.getMessage("nullValue.BufferIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (stride < 3) {
      String msg = Logging.getMessage("generic.StrideIsInvalid", stride);
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    Vec4[] axes = WWMath.computePrincipalAxes(buffer, stride);
    if (axes == null) {
      String msg = Logging.getMessage("generic.BufferIsEmpty");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    Vec4 r = axes[0];
    Vec4 s = axes[1];
    Vec4 t = axes[2];

    // Find the extremes along each axis.
    double minDotR = Double.MAX_VALUE;
    double maxDotR = -minDotR;
    double minDotS = Double.MAX_VALUE;
    double maxDotS = -minDotS;
    double minDotT = Double.MAX_VALUE;
    double maxDotT = -minDotT;

    for (int i = buffer.position(); i <= buffer.limit() - stride; i += stride) {
      double x = buffer.get(i);
      double y = buffer.get(i + 1);
      double z = buffer.get(i + 2);

      double pdr = x * r.x + y * r.y + z * r.z;
      if (pdr < minDotR) minDotR = pdr;
      if (pdr > maxDotR) maxDotR = pdr;

      double pds = x * s.x + y * s.y + z * s.z;
      if (pds < minDotS) minDotS = pds;
      if (pds > maxDotS) maxDotS = pds;

      double pdt = x * t.x + y * t.y + z * t.z;
      if (pdt < minDotT) minDotT = pdt;
      if (pdt > maxDotT) maxDotT = pdt;
    }

    if (maxDotR == minDotR) maxDotR = minDotR + 1;
    if (maxDotS == minDotS) maxDotS = minDotS + 1;
    if (maxDotT == minDotT) maxDotT = minDotT + 1;

    return new Box(axes, minDotR, maxDotR, minDotS, maxDotS, minDotT, maxDotT);
  }
示例#3
0
  /**
   * Compute a <code>Box</code> that bounds a specified list of points. Principal axes are computed
   * for the points and used to form a <code>Box</code>.
   *
   * @param points the points for which to compute a bounding volume.
   * @return the bounding volume, with axes lengths consistent with the conventions described in the
   *     overview.
   * @throws IllegalArgumentException if the point list is null or empty.
   */
  public static Box computeBoundingBox(Iterable<? extends Vec4> points) {
    if (points == null) {
      String msg = Logging.getMessage("nullValue.PointListIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    Vec4[] axes = WWMath.computePrincipalAxes(points);
    if (axes == null) {
      String msg = Logging.getMessage("generic.PointListIsEmpty");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    Vec4 r = axes[0];
    Vec4 s = axes[1];
    Vec4 t = axes[2];

    // Find the extremes along each axis.
    double minDotR = Double.MAX_VALUE;
    double maxDotR = -minDotR;
    double minDotS = Double.MAX_VALUE;
    double maxDotS = -minDotS;
    double minDotT = Double.MAX_VALUE;
    double maxDotT = -minDotT;

    for (Vec4 p : points) {
      if (p == null) continue;

      double pdr = p.dot3(r);
      if (pdr < minDotR) minDotR = pdr;
      if (pdr > maxDotR) maxDotR = pdr;

      double pds = p.dot3(s);
      if (pds < minDotS) minDotS = pds;
      if (pds > maxDotS) maxDotS = pds;

      double pdt = p.dot3(t);
      if (pdt < minDotT) minDotT = pdt;
      if (pdt > maxDotT) maxDotT = pdt;
    }

    if (maxDotR == minDotR) maxDotR = minDotR + 1;
    if (maxDotS == minDotS) maxDotS = minDotS + 1;
    if (maxDotT == minDotT) maxDotT = minDotT + 1;

    return new Box(axes, minDotR, maxDotR, minDotS, maxDotS, minDotT, maxDotT);
  }
示例#4
0
  /**
   * Construct a unit-length cube centered at a specified point.
   *
   * @param point the center of the cube.
   * @throws IllegalArgumentException if the point is null.
   */
  public Box(Vec4 point) {
    if (point == null) {
      String msg = Logging.getMessage("nullValue.PointIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    this.ru = new Vec4(1, 0, 0, 1);
    this.su = new Vec4(0, 1, 0, 1);
    this.tu = new Vec4(0, 0, 1, 1);

    this.r = this.ru;
    this.s = this.su;
    this.t = this.tu;

    this.rLength = 1;
    this.sLength = 1;
    this.tLength = 1;

    // Plane normals point outwards from the box.
    this.planes = new Plane[6];
    double d = 0.5 * point.getLength3();
    this.planes[0] = new Plane(-this.ru.x, -this.ru.y, -this.ru.z, -(d + 0.5));
    this.planes[1] = new Plane(+this.ru.x, +this.ru.y, +this.ru.z, -(d + 0.5));
    this.planes[2] = new Plane(-this.su.x, -this.su.y, -this.su.z, -(d + 0.5));
    this.planes[3] = new Plane(+this.su.x, +this.su.y, +this.su.z, -(d + 0.5));
    this.planes[4] = new Plane(-this.tu.x, -this.tu.y, -this.tu.z, -(d + 0.5));
    this.planes[5] = new Plane(+this.tu.x, +this.tu.y, +this.tu.z, -(d + 0.5));

    this.center = ru.add3(su).add3(tu).multiply3(0.5);

    Vec4 rHalf = r.multiply3(0.5);
    this.topCenter = this.center.add3(rHalf);
    this.bottomCenter = this.center.subtract3(rHalf);
  }
示例#5
0
  /**
   * Creates a frustum by extracting the six frustum planes from a projection matrix.
   *
   * @param matrix the projection matrix to extract the frustum planes from.
   * @return a frustum defined by the extracted planes.
   * @throws IllegalArgumentException if the projection matrix is null.
   */
  public Frustum setProjection(Matrix matrix) {
    //noinspection UnnecessaryLocalVariable
    Matrix m = matrix;
    if (m == null) {
      String msg = Logging.getMessage("nullValue.MatrixIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    // Extract the six clipping planes from the projection-matrix.
    this.left
        .set(m.m[12] + m.m[0], m.m[13] + m.m[1], m.m[14] + m.m[2], m.m[15] + m.m[3])
        .normalizeAndSet();
    this.right
        .set(m.m[12] - m.m[0], m.m[13] - m.m[1], m.m[14] - m.m[2], m.m[15] - m.m[3])
        .normalizeAndSet();
    this.bottom
        .set(m.m[12] + m.m[4], m.m[13] + m.m[5], m.m[14] + m.m[6], m.m[15] + m.m[7])
        .normalizeAndSet();
    this.top
        .set(m.m[12] - m.m[4], m.m[13] - m.m[5], m.m[14] - m.m[6], m.m[15] - m.m[7])
        .normalizeAndSet();
    this.near
        .set(m.m[12] + m.m[8], m.m[13] + m.m[9], m.m[14] + m.m[10], m.m[15] + m.m[11])
        .normalizeAndSet();
    this.far
        .set(m.m[12] - m.m[8], m.m[13] - m.m[9], m.m[14] - m.m[10], m.m[15] - m.m[11])
        .normalizeAndSet();

    return this;
  }
示例#6
0
  /**
   * Indicates whether a specified {@link Extent} intersects this frustum.
   *
   * @param extent the Extent to test.
   * @return true if the extent intersects this frustum, otherwise false.
   * @throws IllegalArgumentException if the extent is null.
   */
  public boolean intersects(Extent extent) {
    if (extent == null) {
      String msg = Logging.getMessage("nullValue.ExtentIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    return extent.intersects(this);
  }
示例#7
0
  /** {@inheritDoc} */
  public double distanceTo(Vec4 point) {
    if (point == null) {
      String msg = Logging.getMessage("nullValue.PointIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    double distance = point.distanceTo3(this.center) - this.getRadius();
    return (distance < 0d) ? 0d : distance;
  }
示例#8
0
  /**
   * Get the path length in meter.
   *
   * <p>If the measurer is set to follow terrain, the computed length will account for terrain
   * deformations as if someone was walking along that path. Otherwise the length is the sum of the
   * cartesian distance between each positions.
   *
   * @param globe the globe to draw terrain information from.
   * @return the current path length or -1 if the position list is too short.
   */
  public double getLength(Globe globe) {
    if (globe == null) {
      String message = Logging.getMessage("nullValue.GlobeIsNull");
      Logging.error(message);
      throw new IllegalArgumentException(message);
    }

    if (this.length < 0) this.length = this.computeLength(globe, this.followTerrain);

    return this.length;
  }
示例#9
0
  /**
   * Set the maximum length a segment can have before being subdivided along a line following the
   * current pathType.
   *
   * @param length the maximum length a segment can have before being subdivided.
   */
  public void setMaxSegmentLength(double length) {
    if (length <= 0) {
      String message = Logging.getMessage("generic.ArgumentOutOfRange", length);
      Logging.error(message);
      throw new IllegalArgumentException(message);
    }

    if (this.maxSegmentLength != length) {
      this.maxSegmentLength = length;
      clearCachedValues();
    }
  }
示例#10
0
  public void setPositions(ArrayList<? extends LatLon> positions, double elevation) {
    if (positions == null) {
      String message = Logging.getMessage("nullValue.PositionsListIsNull");
      Logging.error(message);
      throw new IllegalArgumentException(message);
    }

    ArrayList<Position> newPositions = new ArrayList<Position>();
    for (LatLon pos : positions) newPositions.add(new Position(pos, elevation));

    setPositions(newPositions);
  }
示例#11
0
  public Frustum transformBy(Frustum frustum, Matrix matrix) {
    if (frustum == null) {
      String msg = Logging.getMessage("nullValue.FrustumIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (matrix == null) {
      String msg = Logging.getMessage("nullValue.MatrixIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    this.left.getVector().transformBy4AndSet(frustum.left.getVector(), matrix);
    this.right.getVector().transformBy4AndSet(frustum.right.getVector(), matrix);
    this.bottom.getVector().transformBy4AndSet(frustum.bottom.getVector(), matrix);
    this.top.getVector().transformBy4AndSet(frustum.top.getVector(), matrix);
    this.near.getVector().transformBy4AndSet(frustum.near.getVector(), matrix);
    this.far.getVector().transformBy4AndSet(frustum.far.getVector(), matrix);

    return this;
  }
示例#12
0
  public void setPositions(ArrayList<? extends Position> positions) {
    if (positions == null) {
      String message = Logging.getMessage("nullValue.PositionsListIsNull");
      Logging.error(message);
      throw new IllegalArgumentException(message);
    }

    this.positions = positions;
    if (this.positions.size() > 2) this.sector = Sector.boundingSector(this.positions);
    else this.sector = null;

    clearCachedValues();
  }
示例#13
0
  /**
   * Set the number of terrain elevation samples to be used along the path to approximate it's
   * terrain following length.
   *
   * @param steps the number of terrain elevation samples to be used.
   */
  public void setLengthTerrainSamplingSteps(double steps) {
    if (steps < 1) {
      String message = Logging.getMessage("generic.ArgumentOutOfRange", steps);
      Logging.error(message);
      throw new IllegalArgumentException(message);
    }

    if (this.lengthTerrainSamplingSteps != steps) {
      this.lengthTerrainSamplingSteps = steps;
      this.subdividedPositions = null;
      this.length = -1;
    }
  }
示例#14
0
  public Frustum set(Frustum frustum) {
    if (frustum == null) {
      String msg = Logging.getMessage("nullValue.FrustumIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    this.left.set(frustum.left);
    this.right.set(frustum.right);
    this.bottom.set(frustum.bottom);
    this.top.set(frustum.top);
    this.near.set(frustum.near);
    this.far.set(frustum.far);

    return this;
  }
示例#15
0
  public Box translate(Vec4 point) {
    if (point == null) {
      String msg = Logging.getMessage("nullValue.PointIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    this.bottomCenter.add3AndSet(point);
    this.topCenter.add3AndSet(point);
    this.center.add3AndSet(point);

    for (int i = 0; i < this.planes.length; i++) {
      Vec4 n = this.planes[i].getNormal();
      double d = this.planes[i].getDistance();
      this.planes[i].set(n.x, n.y, n.z, d - n.dot3(point));
    }

    return this;
  }
示例#16
0
  /** {@inheritDoc} */
  public boolean intersects(Frustum frustum) {
    if (frustum == null) {
      String msg = Logging.getMessage("nullValue.FrustumIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    // FYI: this code is identical to that in Cylinder.intersects.

    double intersectionPoint;

    this.tmp1.set(this.bottomCenter);
    this.tmp2.set(this.topCenter);

    double effectiveRadius = this.getEffectiveRadius(frustum.getNear());
    intersectionPoint = this.intersectsAt(frustum.getNear(), effectiveRadius, this.tmp1, this.tmp2);
    if (intersectionPoint < 0) return false;

    // Near and far have the same effective radius.
    effectiveRadius = this.getEffectiveRadius(frustum.getFar());
    intersectionPoint = this.intersectsAt(frustum.getFar(), effectiveRadius, this.tmp1, this.tmp2);
    if (intersectionPoint < 0) return false;

    effectiveRadius = this.getEffectiveRadius(frustum.getLeft());
    intersectionPoint = this.intersectsAt(frustum.getLeft(), effectiveRadius, this.tmp1, this.tmp2);
    if (intersectionPoint < 0) return false;

    effectiveRadius = this.getEffectiveRadius(frustum.getRight());
    intersectionPoint =
        this.intersectsAt(frustum.getRight(), effectiveRadius, this.tmp1, this.tmp2);
    if (intersectionPoint < 0) return false;

    effectiveRadius = this.getEffectiveRadius(frustum.getTop());
    intersectionPoint = this.intersectsAt(frustum.getTop(), effectiveRadius, this.tmp1, this.tmp2);
    if (intersectionPoint < 0) return false;

    effectiveRadius = this.getEffectiveRadius(frustum.getBottom());
    intersectionPoint =
        this.intersectsAt(frustum.getBottom(), effectiveRadius, this.tmp1, this.tmp2);
    return intersectionPoint >= 0;
  }
示例#17
0
  /**
   * Create a frustum from six {@link gov.nasa.worldwind.geom.Plane}s defining the frustum
   * boundaries.
   *
   * <p>None of the arguments may be null.
   *
   * @param near the near plane
   * @param far the far plane
   * @param left the left plane
   * @param right the right plane
   * @param top the top plane
   * @param bottom the bottom plane
   * @throws IllegalArgumentException if any argument is null.
   */
  public Frustum(Plane left, Plane right, Plane bottom, Plane top, Plane near, Plane far) {
    if (left == null) {
      String msg = Logging.getMessage("nullValue.LeftIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (right == null) {
      String msg = Logging.getMessage("nullValue.RightIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (bottom == null) {
      String msg = Logging.getMessage("nullValue.BottomIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (top == null) {
      String msg = Logging.getMessage("nullValue.TopIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (near == null) {
      String msg = Logging.getMessage("nullValue.NearIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (far == null) {
      String msg = Logging.getMessage("nullValue.FarIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    this.left = left;
    this.right = right;
    this.bottom = bottom;
    this.top = top;
    this.near = near;
    this.far = far;

    this.allPlanes =
        new Plane[] {this.left, this.right, this.bottom, this.top, this.near, this.far};
  }
示例#18
0
  /**
   * Indicates whether a specified point is within this frustum.
   *
   * @param point the point to test.
   * @return <code>true</code> if the point is within the frustum, otherwise <code>false</code>.
   * @throws IllegalArgumentException if the point is <code>null</code>.
   */
  public boolean contains(Vec4 point) {
    if (point == null) {
      String msg = Logging.getMessage("nullValue.PointIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    // See if the point is entirely within the frustum. The dot product of the point with each
    // plane's vector
    // provides a distance to each plane. If this distance is less than 0, the point is clipped by
    // that plane and
    // neither intersects nor is contained by the space enclosed by this Frustum.

    if (this.far.dot(point) <= 0) return false;
    if (this.left.dot(point) <= 0) return false;
    if (this.right.dot(point) <= 0) return false;
    if (this.top.dot(point) <= 0) return false;
    if (this.bottom.dot(point) <= 0) return false;
    if (this.near.dot(point) <= 0) return false;

    return true;
  }
示例#19
0
  public Frustum set(Plane left, Plane right, Plane bottom, Plane top, Plane near, Plane far) {
    if (left == null) {
      String msg = Logging.getMessage("nullValue.LeftIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (right == null) {
      String msg = Logging.getMessage("nullValue.RightIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (bottom == null) {
      String msg = Logging.getMessage("nullValue.BottomIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (top == null) {
      String msg = Logging.getMessage("nullValue.TopIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (near == null) {
      String msg = Logging.getMessage("nullValue.NearIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    if (far == null) {
      String msg = Logging.getMessage("nullValue.FarIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    this.left.set(left);
    this.right.set(right);
    this.bottom.set(bottom);
    this.top.set(top);
    this.near.set(near);
    this.far.set(far);

    return this;
  }
示例#20
0
  /**
   * Construct a box from three specified unit axes and the locations of the box faces relative to
   * those axes. The box faces are specified by two scalar locations along each axis, each location
   * indicating a face. The non-unit length of an axis is the distance between its respective two
   * locations. The longest side is specified first, followed by the second longest side and then
   * the shortest side.
   *
   * <p>The axes are normally principal axes computed from a collection of points in order to form
   * an oriented bounding volume. See {@link WWMath#computePrincipalAxes(Iterable)}.
   *
   * <p>Note: No check is made to ensure the order of the face locations.
   *
   * @param axes the unit-length axes.
   * @param rMin the location along the first axis corresponding to the left-most box side relative
   *     to the axis.
   * @param rMax the location along the first axis corresponding to the right-most box side relative
   *     to the axis.
   * @param sMin the location along the second axis corresponding to the left-most box side relative
   *     to the axis.
   * @param sMax the location along the second axis corresponding to the right-most box side
   *     relative to the axis.
   * @param tMin the location along the third axis corresponding to the left-most box side relative
   *     to the axis.
   * @param tMax the location along the third axis corresponding to the right-most box side relative
   *     to the axis.
   * @throws IllegalArgumentException if the axes array or one of its entries is null.
   */
  public Box(
      Vec4 axes[], double rMin, double rMax, double sMin, double sMax, double tMin, double tMax) {
    if (axes == null || axes[0] == null || axes[1] == null || axes[2] == null) {
      String msg = Logging.getMessage("nullValue.AxesIsNull");
      Logging.error(msg);
      throw new IllegalArgumentException(msg);
    }

    this.ru = axes[0];
    this.su = axes[1];
    this.tu = axes[2];

    this.r = this.ru.multiply3(rMax - rMin);
    this.s = this.su.multiply3(sMax - sMin);
    this.t = this.tu.multiply3(tMax - tMin);

    this.rLength = this.r.getLength3();
    this.sLength = this.s.getLength3();
    this.tLength = this.t.getLength3();

    // Plane normals point outward from the box.
    this.planes = new Plane[6];
    this.planes[0] = new Plane(-this.ru.x, -this.ru.y, -this.ru.z, +rMin);
    this.planes[1] = new Plane(+this.ru.x, +this.ru.y, +this.ru.z, -rMax);
    this.planes[2] = new Plane(-this.su.x, -this.su.y, -this.su.z, +sMin);
    this.planes[3] = new Plane(+this.su.x, +this.su.y, +this.su.z, -sMax);
    this.planes[4] = new Plane(-this.tu.x, -this.tu.y, -this.tu.z, +tMin);
    this.planes[5] = new Plane(+this.tu.x, +this.tu.y, +this.tu.z, -tMax);

    double a = 0.5 * (rMin + rMax);
    double b = 0.5 * (sMin + sMax);
    double c = 0.5 * (tMin + tMax);
    this.center = ru.multiply3(a).add3(su.multiply3(b)).add3(tu.multiply3(c));

    Vec4 rHalf = r.multiply3(0.5);
    this.topCenter = this.center.add3(rHalf);
    this.bottomCenter = this.center.subtract3(rHalf);
  }