704 lines
22 KiB
JavaScript
704 lines
22 KiB
JavaScript
import Cartesian3 from "../Core/Cartesian3.js";
|
|
import defined from "../Core/defined.js";
|
|
import Cartographic from "../Core/Cartographic.js";
|
|
import Ellipsoid from "../Core/Ellipsoid.js";
|
|
import Intersect from "../Core/Intersect.js";
|
|
import Matrix3 from "../Core/Matrix3.js";
|
|
import Plane from "../Core/Plane.js";
|
|
import CoplanarPolygonOutlineGeometry from "../Core/CoplanarPolygonOutlineGeometry.js";
|
|
import BoundingSphere from "../Core/BoundingSphere.js";
|
|
import Check from "../Core/Check.js";
|
|
import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";
|
|
import defaultValue from "../Core/defaultValue.js";
|
|
import GeometryInstance from "../Core/GeometryInstance.js";
|
|
import Matrix4 from "../Core/Matrix4.js";
|
|
import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";
|
|
import Primitive from "./Primitive.js";
|
|
import S2Cell from "../Core/S2Cell.js";
|
|
let centerCartographicScratch = new Cartographic();
|
|
/**
|
|
* A tile bounding volume specified as an S2 cell token with minimum and maximum heights.
|
|
* The bounding volume is a k DOP. A k-DOP is the Boolean intersection of extents along k directions.
|
|
*
|
|
* @alias TileBoundingS2Cell
|
|
* @constructor
|
|
*
|
|
* @param {object} options Object with the following properties:
|
|
* @param {string} options.token The token of the S2 cell.
|
|
* @param {number} [options.minimumHeight=0.0] The minimum height of the bounding volume.
|
|
* @param {number} [options.maximumHeight=0.0] The maximum height of the bounding volume.
|
|
* @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid.
|
|
* @param {boolean} [options.computeBoundingVolumes=true] True to compute the {@link TileBoundingS2Cell#boundingVolume} and
|
|
* {@link TileBoundingS2Cell#boundingSphere}. If false, these properties will be undefined.
|
|
*
|
|
* @private
|
|
*/
|
|
function TileBoundingS2Cell(options) {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
Check.typeOf.object("options", options);
|
|
Check.typeOf.string("options.token", options.token);
|
|
//>>includeEnd('debug');
|
|
|
|
const s2Cell = S2Cell.fromToken(options.token);
|
|
const minimumHeight = defaultValue(options.minimumHeight, 0.0);
|
|
const maximumHeight = defaultValue(options.maximumHeight, 0.0);
|
|
const ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
|
|
|
|
this.s2Cell = s2Cell;
|
|
this.minimumHeight = minimumHeight;
|
|
this.maximumHeight = maximumHeight;
|
|
this.ellipsoid = ellipsoid;
|
|
|
|
const boundingPlanes = computeBoundingPlanes(
|
|
s2Cell,
|
|
minimumHeight,
|
|
maximumHeight,
|
|
ellipsoid
|
|
);
|
|
this._boundingPlanes = boundingPlanes;
|
|
|
|
// Pre-compute vertices to speed up the plane intersection test.
|
|
const vertices = computeVertices(boundingPlanes);
|
|
this._vertices = vertices;
|
|
|
|
// Pre-compute edge normals to speed up the point-polygon distance check in distanceToCamera.
|
|
this._edgeNormals = new Array(6);
|
|
|
|
this._edgeNormals[0] = computeEdgeNormals(
|
|
boundingPlanes[0],
|
|
vertices.slice(0, 4)
|
|
);
|
|
let i;
|
|
// Based on the way the edge normals are computed, the edge normals all point away from the "face"
|
|
// of the polyhedron they surround, except the plane for the top plane. Therefore, we negate the normals
|
|
// for the top plane.
|
|
for (i = 0; i < 4; i++) {
|
|
this._edgeNormals[0][i] = Cartesian3.negate(
|
|
this._edgeNormals[0][i],
|
|
this._edgeNormals[0][i]
|
|
);
|
|
}
|
|
|
|
this._edgeNormals[1] = computeEdgeNormals(
|
|
boundingPlanes[1],
|
|
vertices.slice(4, 8)
|
|
);
|
|
for (i = 0; i < 4; i++) {
|
|
// For each plane, iterate through the vertices in CCW order.
|
|
this._edgeNormals[2 + i] = computeEdgeNormals(boundingPlanes[2 + i], [
|
|
vertices[i % 4],
|
|
vertices[(i + 1) % 4],
|
|
vertices[4 + ((i + 1) % 4)],
|
|
vertices[4 + i],
|
|
]);
|
|
}
|
|
|
|
this._planeVertices = [
|
|
this._vertices.slice(0, 4),
|
|
this._vertices.slice(4, 8),
|
|
];
|
|
for (i = 0; i < 4; i++) {
|
|
this._planeVertices.push([
|
|
this._vertices[i % 4],
|
|
this._vertices[(i + 1) % 4],
|
|
this._vertices[4 + ((i + 1) % 4)],
|
|
this._vertices[4 + i],
|
|
]);
|
|
}
|
|
|
|
const center = s2Cell.getCenter();
|
|
centerCartographicScratch = ellipsoid.cartesianToCartographic(
|
|
center,
|
|
centerCartographicScratch
|
|
);
|
|
centerCartographicScratch.height = (maximumHeight + minimumHeight) / 2;
|
|
this.center = ellipsoid.cartographicToCartesian(
|
|
centerCartographicScratch,
|
|
center
|
|
);
|
|
|
|
this._boundingSphere = BoundingSphere.fromPoints(vertices);
|
|
}
|
|
|
|
const centerGeodeticNormalScratch = new Cartesian3();
|
|
const topCartographicScratch = new Cartographic();
|
|
const topScratch = new Cartesian3();
|
|
const vertexCartographicScratch = new Cartographic();
|
|
const vertexScratch = new Cartesian3();
|
|
const vertexGeodeticNormalScratch = new Cartesian3();
|
|
const sideNormalScratch = new Cartesian3();
|
|
const sideScratch = new Cartesian3();
|
|
/**
|
|
* Computes bounding planes of the kDOP.
|
|
* @private
|
|
*/
|
|
function computeBoundingPlanes(
|
|
s2Cell,
|
|
minimumHeight,
|
|
maximumHeight,
|
|
ellipsoid
|
|
) {
|
|
const planes = new Array(6);
|
|
const centerPoint = s2Cell.getCenter();
|
|
|
|
// Compute top plane.
|
|
// - Get geodetic surface normal at the center of the S2 cell.
|
|
// - Get center point at maximum height of bounding volume.
|
|
// - Create top plane from surface normal and top point.
|
|
const centerSurfaceNormal = ellipsoid.geodeticSurfaceNormal(
|
|
centerPoint,
|
|
centerGeodeticNormalScratch
|
|
);
|
|
const topCartographic = ellipsoid.cartesianToCartographic(
|
|
centerPoint,
|
|
topCartographicScratch
|
|
);
|
|
topCartographic.height = maximumHeight;
|
|
const top = ellipsoid.cartographicToCartesian(topCartographic, topScratch);
|
|
const topPlane = Plane.fromPointNormal(top, centerSurfaceNormal);
|
|
planes[0] = topPlane;
|
|
|
|
// Compute bottom plane.
|
|
// - Iterate through bottom vertices
|
|
// - Get distance from vertex to top plane
|
|
// - Find longest distance from vertex to top plane
|
|
// - Translate top plane by the distance
|
|
let maxDistance = 0;
|
|
let i;
|
|
const vertices = [];
|
|
let vertex, vertexCartographic;
|
|
for (i = 0; i < 4; i++) {
|
|
vertex = s2Cell.getVertex(i);
|
|
vertices[i] = vertex;
|
|
vertexCartographic = ellipsoid.cartesianToCartographic(
|
|
vertex,
|
|
vertexCartographicScratch
|
|
);
|
|
vertexCartographic.height = minimumHeight;
|
|
const distance = Plane.getPointDistance(
|
|
topPlane,
|
|
ellipsoid.cartographicToCartesian(vertexCartographic, vertexScratch)
|
|
);
|
|
if (distance < maxDistance) {
|
|
maxDistance = distance;
|
|
}
|
|
}
|
|
const bottomPlane = Plane.clone(topPlane);
|
|
// Negate the normal of the bottom plane since we want all normals to point "outwards".
|
|
bottomPlane.normal = Cartesian3.negate(
|
|
bottomPlane.normal,
|
|
bottomPlane.normal
|
|
);
|
|
bottomPlane.distance = bottomPlane.distance * -1 + maxDistance;
|
|
planes[1] = bottomPlane;
|
|
|
|
// Compute side planes.
|
|
// - Iterate through vertices (in CCW order, by default)
|
|
// - Get a vertex and another vertex adjacent to it.
|
|
// - Compute geodetic surface normal at one vertex.
|
|
// - Compute vector between vertices.
|
|
// - Compute normal of side plane. (cross product of top dir and side dir)
|
|
for (i = 0; i < 4; i++) {
|
|
vertex = vertices[i];
|
|
const adjacentVertex = vertices[(i + 1) % 4];
|
|
const geodeticNormal = ellipsoid.geodeticSurfaceNormal(
|
|
vertex,
|
|
vertexGeodeticNormalScratch
|
|
);
|
|
const side = Cartesian3.subtract(adjacentVertex, vertex, sideScratch);
|
|
let sideNormal = Cartesian3.cross(side, geodeticNormal, sideNormalScratch);
|
|
sideNormal = Cartesian3.normalize(sideNormal, sideNormal);
|
|
planes[2 + i] = Plane.fromPointNormal(vertex, sideNormal);
|
|
}
|
|
|
|
return planes;
|
|
}
|
|
|
|
let n0Scratch = new Cartesian3();
|
|
let n1Scratch = new Cartesian3();
|
|
let n2Scratch = new Cartesian3();
|
|
let x0Scratch = new Cartesian3();
|
|
let x1Scratch = new Cartesian3();
|
|
let x2Scratch = new Cartesian3();
|
|
const t0Scratch = new Cartesian3();
|
|
const t1Scratch = new Cartesian3();
|
|
const t2Scratch = new Cartesian3();
|
|
let f0Scratch = new Cartesian3();
|
|
let f1Scratch = new Cartesian3();
|
|
let f2Scratch = new Cartesian3();
|
|
let sScratch = new Cartesian3();
|
|
const matrixScratch = new Matrix3();
|
|
/**
|
|
* Computes intersection of 3 planes.
|
|
* @private
|
|
*/
|
|
function computeIntersection(p0, p1, p2) {
|
|
n0Scratch = p0.normal;
|
|
n1Scratch = p1.normal;
|
|
n2Scratch = p2.normal;
|
|
|
|
x0Scratch = Cartesian3.multiplyByScalar(p0.normal, -p0.distance, x0Scratch);
|
|
x1Scratch = Cartesian3.multiplyByScalar(p1.normal, -p1.distance, x1Scratch);
|
|
x2Scratch = Cartesian3.multiplyByScalar(p2.normal, -p2.distance, x2Scratch);
|
|
|
|
f0Scratch = Cartesian3.multiplyByScalar(
|
|
Cartesian3.cross(n1Scratch, n2Scratch, t0Scratch),
|
|
Cartesian3.dot(x0Scratch, n0Scratch),
|
|
f0Scratch
|
|
);
|
|
f1Scratch = Cartesian3.multiplyByScalar(
|
|
Cartesian3.cross(n2Scratch, n0Scratch, t1Scratch),
|
|
Cartesian3.dot(x1Scratch, n1Scratch),
|
|
f1Scratch
|
|
);
|
|
f2Scratch = Cartesian3.multiplyByScalar(
|
|
Cartesian3.cross(n0Scratch, n1Scratch, t2Scratch),
|
|
Cartesian3.dot(x2Scratch, n2Scratch),
|
|
f2Scratch
|
|
);
|
|
|
|
matrixScratch[0] = n0Scratch.x;
|
|
matrixScratch[1] = n1Scratch.x;
|
|
matrixScratch[2] = n2Scratch.x;
|
|
matrixScratch[3] = n0Scratch.y;
|
|
matrixScratch[4] = n1Scratch.y;
|
|
matrixScratch[5] = n2Scratch.y;
|
|
matrixScratch[6] = n0Scratch.z;
|
|
matrixScratch[7] = n1Scratch.z;
|
|
matrixScratch[8] = n2Scratch.z;
|
|
const determinant = Matrix3.determinant(matrixScratch);
|
|
sScratch = Cartesian3.add(f0Scratch, f1Scratch, sScratch);
|
|
sScratch = Cartesian3.add(sScratch, f2Scratch, sScratch);
|
|
return new Cartesian3(
|
|
sScratch.x / determinant,
|
|
sScratch.y / determinant,
|
|
sScratch.z / determinant
|
|
);
|
|
}
|
|
/**
|
|
* Compute the vertices of the kDOP.
|
|
* @private
|
|
*/
|
|
function computeVertices(boundingPlanes) {
|
|
const vertices = new Array(8);
|
|
for (let i = 0; i < 4; i++) {
|
|
// Vertices on the top plane.
|
|
vertices[i] = computeIntersection(
|
|
boundingPlanes[0],
|
|
boundingPlanes[2 + ((i + 3) % 4)],
|
|
boundingPlanes[2 + (i % 4)]
|
|
);
|
|
// Vertices on the bottom plane.
|
|
vertices[i + 4] = computeIntersection(
|
|
boundingPlanes[1],
|
|
boundingPlanes[2 + ((i + 3) % 4)],
|
|
boundingPlanes[2 + (i % 4)]
|
|
);
|
|
}
|
|
return vertices;
|
|
}
|
|
|
|
let edgeScratch = new Cartesian3();
|
|
let edgeNormalScratch = new Cartesian3();
|
|
/**
|
|
* Compute edge normals on a plane.
|
|
* @private
|
|
*/
|
|
function computeEdgeNormals(plane, vertices) {
|
|
const edgeNormals = [];
|
|
for (let i = 0; i < 4; i++) {
|
|
edgeScratch = Cartesian3.subtract(
|
|
vertices[(i + 1) % 4],
|
|
vertices[i],
|
|
edgeScratch
|
|
);
|
|
edgeNormalScratch = Cartesian3.cross(
|
|
plane.normal,
|
|
edgeScratch,
|
|
edgeNormalScratch
|
|
);
|
|
edgeNormalScratch = Cartesian3.normalize(
|
|
edgeNormalScratch,
|
|
edgeNormalScratch
|
|
);
|
|
edgeNormals[i] = Cartesian3.clone(edgeNormalScratch);
|
|
}
|
|
return edgeNormals;
|
|
}
|
|
|
|
Object.defineProperties(TileBoundingS2Cell.prototype, {
|
|
/**
|
|
* The underlying bounding volume.
|
|
*
|
|
* @memberof TileBoundingS2Cell.prototype
|
|
*
|
|
* @type {object}
|
|
* @readonly
|
|
*/
|
|
boundingVolume: {
|
|
get: function () {
|
|
return this;
|
|
},
|
|
},
|
|
/**
|
|
* The underlying bounding sphere.
|
|
*
|
|
* @memberof TileBoundingS2Cell.prototype
|
|
*
|
|
* @type {BoundingSphere}
|
|
* @readonly
|
|
*/
|
|
boundingSphere: {
|
|
get: function () {
|
|
return this._boundingSphere;
|
|
},
|
|
},
|
|
});
|
|
|
|
const facePointScratch = new Cartesian3();
|
|
/**
|
|
* The distance to point check for this kDOP involves checking the signed distance of the point to each bounding
|
|
* plane. A plane qualifies for a distance check if the point being tested against is in the half-space in the direction
|
|
* of the normal i.e. if the signed distance of the point from the plane is greater than 0.
|
|
*
|
|
* There are 4 possible cases for a point if it is outside the polyhedron:
|
|
*
|
|
* \ X / X \ / \ / \ /
|
|
* ---\---------/--- ---\---------/--- ---X---------/--- ---\---------/---
|
|
* \ / \ / \ / \ /
|
|
* ---\-----/--- ---\-----/--- ---\-----/--- ---\-----/---
|
|
* \ / \ / \ / \ /
|
|
* \ /
|
|
* \
|
|
* / \
|
|
* / X \
|
|
*
|
|
* I II III IV
|
|
*
|
|
* Case I: There is only one plane selected.
|
|
* In this case, we project the point onto the plane and do a point polygon distance check to find the closest point on the polygon.
|
|
* The point may lie inside the "face" of the polygon or outside. If it is outside, we need to determine which edges to test against.
|
|
*
|
|
* Case II: There are two planes selected.
|
|
* In this case, the point will lie somewhere on the line created at the intersection of the selected planes or one of the planes.
|
|
*
|
|
* Case III: There are three planes selected.
|
|
* In this case, the point will lie on the vertex, at the intersection of the selected planes.
|
|
*
|
|
* Case IV: There are more than three planes selected.
|
|
* Since we are on an ellipsoid, this will only happen in the bottom plane, which is what we will use for the distance test.
|
|
*/
|
|
TileBoundingS2Cell.prototype.distanceToCamera = function (frameState) {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
Check.defined("frameState", frameState);
|
|
//>>includeEnd('debug');
|
|
|
|
const point = frameState.camera.positionWC;
|
|
|
|
const selectedPlaneIndices = [];
|
|
const vertices = [];
|
|
let edgeNormals;
|
|
|
|
if (Plane.getPointDistance(this._boundingPlanes[0], point) > 0) {
|
|
selectedPlaneIndices.push(0);
|
|
vertices.push(this._planeVertices[0]);
|
|
edgeNormals = this._edgeNormals[0];
|
|
} else if (Plane.getPointDistance(this._boundingPlanes[1], point) > 0) {
|
|
selectedPlaneIndices.push(1);
|
|
vertices.push(this._planeVertices[1]);
|
|
edgeNormals = this._edgeNormals[1];
|
|
}
|
|
|
|
let i;
|
|
let sidePlaneIndex;
|
|
for (i = 0; i < 4; i++) {
|
|
sidePlaneIndex = 2 + i;
|
|
if (
|
|
Plane.getPointDistance(this._boundingPlanes[sidePlaneIndex], point) > 0
|
|
) {
|
|
selectedPlaneIndices.push(sidePlaneIndex);
|
|
// Store vertices in CCW order.
|
|
vertices.push(this._planeVertices[sidePlaneIndex]);
|
|
edgeNormals = this._edgeNormals[sidePlaneIndex];
|
|
}
|
|
}
|
|
|
|
// Check if inside all planes.
|
|
if (selectedPlaneIndices.length === 0) {
|
|
return 0.0;
|
|
}
|
|
|
|
// We use the skip variable when the side plane indices are non-consecutive.
|
|
let facePoint;
|
|
let selectedPlane;
|
|
if (selectedPlaneIndices.length === 1) {
|
|
// Handles Case I
|
|
selectedPlane = this._boundingPlanes[selectedPlaneIndices[0]];
|
|
facePoint = closestPointPolygon(
|
|
Plane.projectPointOntoPlane(selectedPlane, point, facePointScratch),
|
|
vertices[0],
|
|
selectedPlane,
|
|
edgeNormals
|
|
);
|
|
|
|
return Cartesian3.distance(facePoint, point);
|
|
} else if (selectedPlaneIndices.length === 2) {
|
|
// Handles Case II
|
|
// Since we are on the ellipsoid, the dihedral angle between a top plane and a side plane
|
|
// will always be acute, so we can do a faster check there.
|
|
if (selectedPlaneIndices[0] === 0) {
|
|
const edge = [
|
|
this._vertices[
|
|
4 * selectedPlaneIndices[0] + (selectedPlaneIndices[1] - 2)
|
|
],
|
|
this._vertices[
|
|
4 * selectedPlaneIndices[0] + ((selectedPlaneIndices[1] - 2 + 1) % 4)
|
|
],
|
|
];
|
|
facePoint = closestPointLineSegment(point, edge[0], edge[1]);
|
|
return Cartesian3.distance(facePoint, point);
|
|
}
|
|
let minimumDistance = Number.MAX_VALUE;
|
|
let distance;
|
|
for (i = 0; i < 2; i++) {
|
|
selectedPlane = this._boundingPlanes[selectedPlaneIndices[i]];
|
|
facePoint = closestPointPolygon(
|
|
Plane.projectPointOntoPlane(selectedPlane, point, facePointScratch),
|
|
vertices[i],
|
|
selectedPlane,
|
|
this._edgeNormals[selectedPlaneIndices[i]]
|
|
);
|
|
|
|
distance = Cartesian3.distanceSquared(facePoint, point);
|
|
if (distance < minimumDistance) {
|
|
minimumDistance = distance;
|
|
}
|
|
}
|
|
return Math.sqrt(minimumDistance);
|
|
} else if (selectedPlaneIndices.length > 3) {
|
|
// Handles Case IV
|
|
facePoint = closestPointPolygon(
|
|
Plane.projectPointOntoPlane(
|
|
this._boundingPlanes[1],
|
|
point,
|
|
facePointScratch
|
|
),
|
|
this._planeVertices[1],
|
|
this._boundingPlanes[1],
|
|
this._edgeNormals[1]
|
|
);
|
|
return Cartesian3.distance(facePoint, point);
|
|
}
|
|
|
|
// Handles Case III
|
|
const skip =
|
|
selectedPlaneIndices[1] === 2 && selectedPlaneIndices[2] === 5 ? 0 : 1;
|
|
|
|
// Vertex is on top plane.
|
|
if (selectedPlaneIndices[0] === 0) {
|
|
return Cartesian3.distance(
|
|
point,
|
|
this._vertices[(selectedPlaneIndices[1] - 2 + skip) % 4]
|
|
);
|
|
}
|
|
|
|
// Vertex is on bottom plane.
|
|
return Cartesian3.distance(
|
|
point,
|
|
this._vertices[4 + ((selectedPlaneIndices[1] - 2 + skip) % 4)]
|
|
);
|
|
};
|
|
|
|
const dScratch = new Cartesian3();
|
|
const pL0Scratch = new Cartesian3();
|
|
/**
|
|
* Finds point on a line segment closest to a given point.
|
|
* @private
|
|
*/
|
|
function closestPointLineSegment(p, l0, l1) {
|
|
const d = Cartesian3.subtract(l1, l0, dScratch);
|
|
const pL0 = Cartesian3.subtract(p, l0, pL0Scratch);
|
|
let t = Cartesian3.dot(d, pL0);
|
|
|
|
if (t <= 0) {
|
|
return l0;
|
|
}
|
|
|
|
const dMag = Cartesian3.dot(d, d);
|
|
if (t >= dMag) {
|
|
return l1;
|
|
}
|
|
|
|
t = t / dMag;
|
|
return new Cartesian3(
|
|
(1 - t) * l0.x + t * l1.x,
|
|
(1 - t) * l0.y + t * l1.y,
|
|
(1 - t) * l0.z + t * l1.z
|
|
);
|
|
}
|
|
|
|
const edgePlaneScratch = new Plane(Cartesian3.UNIT_X, 0.0);
|
|
/**
|
|
* Finds closes point on the polygon, created by the given vertices, from
|
|
* a point. The test point and the polygon are all on the same plane.
|
|
* @private
|
|
*/
|
|
function closestPointPolygon(p, vertices, plane, edgeNormals) {
|
|
let minDistance = Number.MAX_VALUE;
|
|
let distance;
|
|
let closestPoint;
|
|
let closestPointOnEdge;
|
|
|
|
for (let i = 0; i < vertices.length; i++) {
|
|
const edgePlane = Plane.fromPointNormal(
|
|
vertices[i],
|
|
edgeNormals[i],
|
|
edgePlaneScratch
|
|
);
|
|
const edgePlaneDistance = Plane.getPointDistance(edgePlane, p);
|
|
|
|
// Skip checking against the edge if the point is not in the half-space that the
|
|
// edgePlane's normal points towards i.e. if the edgePlane is facing away from the point.
|
|
if (edgePlaneDistance < 0) {
|
|
continue;
|
|
}
|
|
|
|
closestPointOnEdge = closestPointLineSegment(
|
|
p,
|
|
vertices[i],
|
|
vertices[(i + 1) % 4]
|
|
);
|
|
|
|
distance = Cartesian3.distance(p, closestPointOnEdge);
|
|
if (distance < minDistance) {
|
|
minDistance = distance;
|
|
closestPoint = closestPointOnEdge;
|
|
}
|
|
}
|
|
|
|
if (!defined(closestPoint)) {
|
|
return p;
|
|
}
|
|
return closestPoint;
|
|
}
|
|
|
|
/**
|
|
* Determines which side of a plane this volume is located.
|
|
*
|
|
* @param {Plane} plane The plane to test against.
|
|
* @returns {Intersect} {@link Intersect.INSIDE} if the entire volume is on the side of the plane
|
|
* the normal is pointing, {@link Intersect.OUTSIDE} if the entire volume is
|
|
* on the opposite side, and {@link Intersect.INTERSECTING} if the volume
|
|
* intersects the plane.
|
|
*/
|
|
TileBoundingS2Cell.prototype.intersectPlane = function (plane) {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
Check.defined("plane", plane);
|
|
//>>includeEnd('debug');
|
|
|
|
let plusCount = 0;
|
|
let negCount = 0;
|
|
for (let i = 0; i < this._vertices.length; i++) {
|
|
const distanceToPlane =
|
|
Cartesian3.dot(plane.normal, this._vertices[i]) + plane.distance;
|
|
if (distanceToPlane < 0) {
|
|
negCount++;
|
|
} else {
|
|
plusCount++;
|
|
}
|
|
}
|
|
|
|
if (plusCount === this._vertices.length) {
|
|
return Intersect.INSIDE;
|
|
} else if (negCount === this._vertices.length) {
|
|
return Intersect.OUTSIDE;
|
|
}
|
|
return Intersect.INTERSECTING;
|
|
};
|
|
|
|
/**
|
|
* Creates a debug primitive that shows the outline of the tile bounding
|
|
* volume.
|
|
*
|
|
* @param {Color} color The desired color of the primitive's mesh
|
|
* @return {Primitive}
|
|
*/
|
|
TileBoundingS2Cell.prototype.createDebugVolume = function (color) {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
Check.defined("color", color);
|
|
//>>includeEnd('debug');
|
|
|
|
const modelMatrix = new Matrix4.clone(Matrix4.IDENTITY);
|
|
const topPlanePolygon = new CoplanarPolygonOutlineGeometry({
|
|
polygonHierarchy: {
|
|
positions: this._planeVertices[0],
|
|
},
|
|
});
|
|
const topPlaneGeometry = CoplanarPolygonOutlineGeometry.createGeometry(
|
|
topPlanePolygon
|
|
);
|
|
const topPlaneInstance = new GeometryInstance({
|
|
geometry: topPlaneGeometry,
|
|
id: "outline",
|
|
modelMatrix: modelMatrix,
|
|
attributes: {
|
|
color: ColorGeometryInstanceAttribute.fromColor(color),
|
|
},
|
|
});
|
|
|
|
const bottomPlanePolygon = new CoplanarPolygonOutlineGeometry({
|
|
polygonHierarchy: {
|
|
positions: this._planeVertices[1],
|
|
},
|
|
});
|
|
const bottomPlaneGeometry = CoplanarPolygonOutlineGeometry.createGeometry(
|
|
bottomPlanePolygon
|
|
);
|
|
const bottomPlaneInstance = new GeometryInstance({
|
|
geometry: bottomPlaneGeometry,
|
|
id: "outline",
|
|
modelMatrix: modelMatrix,
|
|
attributes: {
|
|
color: ColorGeometryInstanceAttribute.fromColor(color),
|
|
},
|
|
});
|
|
|
|
const sideInstances = [];
|
|
for (let i = 0; i < 4; i++) {
|
|
const sidePlanePolygon = new CoplanarPolygonOutlineGeometry({
|
|
polygonHierarchy: {
|
|
positions: this._planeVertices[2 + i],
|
|
},
|
|
});
|
|
const sidePlaneGeometry = CoplanarPolygonOutlineGeometry.createGeometry(
|
|
sidePlanePolygon
|
|
);
|
|
sideInstances[i] = new GeometryInstance({
|
|
geometry: sidePlaneGeometry,
|
|
id: "outline",
|
|
modelMatrix: modelMatrix,
|
|
attributes: {
|
|
color: ColorGeometryInstanceAttribute.fromColor(color),
|
|
},
|
|
});
|
|
}
|
|
|
|
return new Primitive({
|
|
geometryInstances: [
|
|
sideInstances[0],
|
|
sideInstances[1],
|
|
sideInstances[2],
|
|
sideInstances[3],
|
|
bottomPlaneInstance,
|
|
topPlaneInstance,
|
|
],
|
|
appearance: new PerInstanceColorAppearance({
|
|
translucent: false,
|
|
flat: true,
|
|
}),
|
|
asynchronous: false,
|
|
});
|
|
};
|
|
|
|
export default TileBoundingS2Cell;
|