/** * 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; }
/** * 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); }
/** * 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); }
/** * 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); }
/** * 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; }
/** * 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); }
/** {@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; }
/** * 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; }
/** * 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(); } }
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); }
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; }
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(); }
/** * 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; } }
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; }
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; }
/** {@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; }
/** * 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}; }
/** * 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; }
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; }
/** * 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); }