📄 ellipsoidalglobe.java
字号:
* * @param verticalExaggeration the vertical exaggeration to apply to the globe's elevations when computing the * cylinder. * @param sector the sector to return the bounding cylinder for. * * @return The minimal bounding cylinder in Cartesian coordinates. * @throws IllegalArgumentException if <code>sector</code> is null */ public Cylinder computeBoundingCylinder(double verticalExaggeration, Sector sector) { if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } double[] minAndMaxElevations = this.getMinAndMaxElevations(sector); return this.computeBoundingCylinder(verticalExaggeration, sector, minAndMaxElevations[0], minAndMaxElevations[1]); } /** * Returns a cylinder that minimally surrounds the specified minimum and maximum elevations in the sector at a * specified vertical exaggeration. * * @param verticalExaggeration the vertical exaggeration to apply to the minimum and maximum elevations when * computing the cylinder. * @param sector the sector to return the bounding cylinder for. * @param minElevation the minimum elevation of the bounding cylinder. * @param maxElevation the maximum elevation of the bounding cylinder. * * @return The minimal bounding cylinder in Cartesian coordinates. * @throws IllegalArgumentException if <code>sector</code> is null */ public Cylinder computeBoundingCylinder(double verticalExaggeration, Sector sector, double minElevation, double maxElevation) { if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } // Compute the exaggerated minimum and maximum heights. double minHeight = minElevation * verticalExaggeration; double maxHeight = maxElevation * verticalExaggeration; // If the sector spans both poles in latitude, or spans greater than 180 degrees in longitude, we cannot use the // sector's Cartesian quadrilateral to compute a bounding cylinde. This is because the quadrilateral is either // smaller than the geometry defined by the sector (when deltaLon >= 180), or the quadrilateral degenerates to // two points (when deltaLat >= 180). So we compute a bounging cylinder that spans the equator and covers the // sector's latitude range. In some cases this cylinder may be too large, but we're typically not interested // in culling these cylinders since the sector will span most of the globe. if (sector.getDeltaLatDegrees() >= 180d || sector.getDeltaLonDegrees() >= 180d) { return this.computeBoundsFromSectorLatitudeRange(sector, minHeight, maxHeight); } // Otherwise, create a standard bounding cylinder that minimally surrounds the specified sector and elevations. else { return this.computeBoundsFromSectorQuadrilateral(sector, minHeight, maxHeight); } } public SectorGeometryList tessellate(DrawContext dc) { if (this.tessellator == null) { this.tessellator = (Tessellator) WorldWind.createConfigurationComponent(AVKey.TESSELLATOR_CLASS_NAME); } return this.tessellator.tessellate(dc); } /** * Determines whether a point is above a given elevation * * @param point the <code>Vec4</code> point to test. * @param elevation the elevation to test for. * @return true if the given point is above the given elevation. */ public boolean isPointAboveElevation(Vec4 point, double elevation) { if (point == null) { String msg = Logging.getMessage("nullValue.PointIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } return (point.x() * point.x()) / ((this.equatorialRadius + elevation) * (this.equatorialRadius + elevation)) + (point.y() * point.y()) / ((this.polarRadius + elevation) * (this.polarRadius + elevation)) + (point.z() * point.z()) / ((this.equatorialRadius + elevation) * (this.equatorialRadius + elevation)) - 1 > 0; } /** * Returns a cylinder that minimally surrounds the specified height range in the sector. * * @param sector the sector to return the bounding cylinder for. * @param minHeight the minimum height to include in the bounding cylinder. * @param maxHeight the maximum height to include in the bounding cylinder. * * @return The minimal bounding cylinder in Cartesian coordinates. * @throws IllegalArgumentException if <code>sector</code> is null */ protected Cylinder computeBoundsFromSectorQuadrilateral(Sector sector, double minHeight, double maxHeight) { if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } // Get three non-coincident points on the sector's quadrilateral. We choose the north or south pair that is // closest to the equator, then choose a third point from the opposite pair. We use maxHeight as elevation // because we want to bound the largest potential quadrilateral for the sector. Vec4 p0, p1, p2; if (Math.abs(sector.getMinLatitude().degrees) <= Math.abs(sector.getMaxLatitude().degrees)) { p0 = this.computePointFromPosition(sector.getMinLatitude(), sector.getMaxLongitude(), maxHeight); // SE p1 = this.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), maxHeight); // SW p2 = this.computePointFromPosition(sector.getMaxLatitude(), sector.getMinLongitude(), maxHeight); // NW } else { p0 = this.computePointFromPosition(sector.getMaxLatitude(), sector.getMinLongitude(), maxHeight); // NW p1 = this.computePointFromPosition(sector.getMaxLatitude(), sector.getMaxLongitude(), maxHeight); // NE p2 = this.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), maxHeight); // SW } // Compute the center, axis, and radius of the circle that circumscribes the three points. // This circle is guaranteed to circumscribe all four points of the sector's Cartesian quadrilateral. Vec4[] centerOut = new Vec4[1]; Vec4[] axisOut = new Vec4[1]; double[] radiusOut = new double[1]; if (!this.computeCircleThroughPoints(p0, p1, p2, centerOut, axisOut, radiusOut)) { // If the computation failed, then two of the points are coincident. Fall back to creating a bounding // cylinder based on the vertices of the sector. This bounding cylinder won't be as tight a fit, but // it will be correct. return this.computeBoundsFromSectorVertices(sector, minHeight, maxHeight); } Vec4 centerPoint = centerOut[0]; Vec4 axis = axisOut[0]; double radius = radiusOut[0]; // Compute the sector's lowest projection along the cylinder axis. We test opposite corners of the sector // using minHeight. One of these will be the lowest point in the sector. Vec4 extremePoint = this.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), minHeight); double minProj = extremePoint.subtract3(centerPoint).dot3(axis); extremePoint = this.computePointFromPosition(sector.getMaxLatitude(), sector.getMaxLongitude(), minHeight); minProj = Math.min(minProj, extremePoint.subtract3(centerPoint).dot3(axis)); // Compute the sector's highest projection along the cylinder axis. We only need to use the point at the // sector's centroid with maxHeight. This point is guaranteed to be the highest point in the sector. LatLon centroid = sector.getCentroid(); extremePoint = this.computePointFromPosition(centroid.getLatitude(), centroid.getLongitude(), maxHeight); double maxProj = extremePoint.subtract3(centerPoint).dot3(axis); Vec4 bottomCenterPoint = axis.multiply3(minProj).add3(centerPoint); Vec4 topCenterPoint = axis.multiply3(maxProj).add3(centerPoint); return new Cylinder(bottomCenterPoint, topCenterPoint, radius); } /** * Compute the Cylinder that surrounds the equator, and has height defined by the sector's minumum and maximum * latitudes (including maxHeight). * * @param sector the sector to return the bounding cylinder for. * @param minHeight the minimum height to include in the bounding cylinder. * @param maxHeight the maximum height to include in the bounding cylinder. * * @return the minimal bounding cylinder in Cartesianl coordinates. * @throws IllegalArgumentException if <code>sector</code> is null */ @SuppressWarnings({"UnusedDeclaration"}) protected Cylinder computeBoundsFromSectorLatitudeRange(Sector sector, double minHeight, double maxHeight) { if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } Vec4 centerPoint = Vec4.ZERO; Vec4 axis = Vec4.UNIT_Y; double radius = this.getEquatorialRadius() + maxHeight; // Compute the sector's lowest projection along the cylinder axis. This will be a point of minimum latitude // with maxHeight. Vec4 extremePoint = this.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), maxHeight); double minProj = extremePoint.subtract3(centerPoint).dot3(axis); // Compute the sector's lowest highest along the cylinder axis. This will be a point of maximum latitude // with maxHeight. extremePoint = this.computePointFromPosition(sector.getMaxLatitude(), sector.getMaxLongitude(), maxHeight); double maxProj = extremePoint.subtract3(centerPoint).dot3(axis); Vec4 bottomCenterPoint = axis.multiply3(minProj).add3(centerPoint); Vec4 topCenterPoint = axis.multiply3(maxProj).add3(centerPoint); return new Cylinder(bottomCenterPoint, topCenterPoint, radius); } /** * Returns a cylinder that surrounds the specified height range in the zero-area sector. The returned cylinder * won't be as tight a fit as <code>computeBoundsFromSectorQuadrilateral</code>. * * @param sector the sector to return the bounding cylinder for. * @param minHeight the minimum height to include in the bounding cylinder. * @param maxHeight the maximum height to include in the bounding cylinder. * * @return The minimal bounding cylinder in Cartesian coordinates. * @throws IllegalArgumentException if <code>sector</code> is null */ protected Cylinder computeBoundsFromSectorVertices(Sector sector, double minHeight, double maxHeight) { if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } // Compute the top center point as the surface point with maxHeight at the sector's centroid. LatLon centroid = sector.getCentroid(); Vec4 topCenterPoint = this.computePointFromPosition(centroid.getLatitude(), centroid.getLongitude(), maxHeight); // Compute the axis as the surface normal at the sector's centroid. Vec4 axis = this.computeSurfaceNormalAtPoint(topCenterPoint); // Compute the four corner points of the sector with minHeight. Vec4 southwest = this.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), minHeight); Vec4 southeast = this.computePointFromPosition(sector.getMinLatitude(), sector.getMaxLongitude(), minHeight); Vec4 northeast = this.computePointFromPosition(sector.getMaxLatitude(), sector.getMaxLongitude(), minHeight); Vec4 northwest = this.computePointFromPosition(sector.getMaxLatitude(), sector.getMinLongitude(), minHeight); // Compute the bottom center point as the lowest projection along the axis. double minProj = southwest.subtract3(topCenterPoint).dot3(axis); minProj = Math.min(minProj, southeast.subtract3(topCenterPoint).dot3(axis)); minProj = Math.min(minProj, northeast.subtract3(topCenterPoint).dot3(axis)); minProj = Math.min(minProj, northwest.subtract3(topCenterPoint).dot3(axis)); Vec4 bottomCenterPoint = axis.multiply3(minProj).add3(topCenterPoint); // Compute the radius as the maximum distance from the top center point to any of the corner points. double radius = topCenterPoint.distanceTo3(southwest); radius = Math.max(radius, topCenterPoint.distanceTo3(southeast)); radius = Math.max(radius, topCenterPoint.distanceTo3(northeast)); radius = Math.max(radius, topCenterPoint.distanceTo3(northwest)); return new Cylinder(bottomCenterPoint, topCenterPoint, radius); } /** * Computes the center, axis, and radius of the circle that circumscribes the specified points. If the points are * oriented in a clockwise winding order, the circle's axis will point toward the viewer. Otherwise the axis * will point away from the viewer. Values are returned in the first element of centerOut, axisOut, and radiusOut. * The caller must provide a preallocted arrays of length one or greater for each of these values. * * @param p0 the first point. * @param p1 the second point. * @param p2 the third point. * @param centerOut preallocated array to hold the circle's center. * @param axisOut preallocated array to hold the circle's axis. * @param radiusOut preallocated array to hold the circle's radius. * * @return true if the computation was successful; false otherwise. * @throws IllegalArgumentException if <code>p0</code>, <code>p1</code>, or <code>p2</code> is null */ private boolean computeCircleThroughPoints(Vec4 p0, Vec4 p1, Vec4 p2, Vec4[] centerOut, Vec4[] axisOut, double[] radiusOut) { if (p0 == null || p1 == null || p2 == null) { String msg = Logging.getMessage("nullValue.Vec4IsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } Vec4 v0 = p1.subtract3(p0); Vec4 v1 = p2.subtract3(p1); Vec4 v2 = p2.subtract3(p0); double d0 = v0.dot3(v2); double d1 = -v0.dot3(v1); double d2 = v1.dot3(v2); double t0 = d1 + d2; double t1 = d0 + d2; double t2 = d0 + d1; double e0 = d0 * t0; double e1 = d1 * t1; double e2 = d2 * t2; double max_e = Math.max(Math.max(e0, e1), e2); double min_e = Math.min(Math.min(e0, e1), e2); double E = e0 + e1 + e2; double tolerance = 1e-6; if (Math.abs(E) <= tolerance * (max_e - min_e)) return false; double radiusSquared = 0.5d * t0 * t1 * t2 / E; // the three points are collinear -- no circle with finite radius is possible if (radiusSquared < 0d) return false; double radius = Math.sqrt(radiusSquared); Vec4 center = p0.multiply3(e0 / E); center = center.add3(p1.multiply3(e1 / E)); center = center.add3(p2.multiply3(e2 / E)); Vec4 axis = v2.cross3(v0); axis = axis.normalize3(); if (centerOut != null) centerOut[0] = center; if (axisOut != null) axisOut[0] = axis; if (radiusOut != null) radiusOut[0] = radius; return true; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -