胶州空管前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1689 lines
52 KiB

const EPSILON14 = 0.00000000000001;
const PI_OVER_TWO = Math.PI / 2.0;
const SHIFT_LEFT_12 = Math.pow(2.0, 12.0);
const TerrainQuantization = {
NONE: 0,
BITS12: 1
}
function defined(target) {
return target !== undefined && target !== null;
}
function defaultValue(value, defaultValue) {
if (!defined(value)) return defaultValue;
return value;
}
function lerp(p, q, time) {
return (1.0 - time) * p + time * q;
};
function clamp(value, min, max) {
return value < min ? min : value > max ? max : value;
};
function equalsEpsilon(a, b, epsilon) {
return Math.abs(a - b) <= epsilon;
}
function sign(a) {
if (a == 0) return 0;
return Math.abs(a) === a ? 1.0 : -1.0;
}
class Vector3 {
constructor (x,y,z) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
static minimumByComponent(v1, v2, result) {
if (!defined(result)) result = new Vector3();
result.set(
Math.min(v1.x, v2.x),
Math.min(v1.y, v2.y),
Math.min(v1.z, v2.z)
)
return result;
}
static maximumByComponent(v1, v2, result) {
if (!defined(result)) result = new Vector3();
result.set(
Math.max(v1.x, v2.x),
Math.max(v1.y, v2.y),
Math.max(v1.z, v2.z)
);
return result;
}
static maximumComponent(v) {
return Math.max(v.x, v.y, v.z);
}
static equals(v1, v2) {
return (v1.x == v2.x &&
v1.y == v2.x &&
v1.z == v2.z);
}
static equalsEpsilon(v1, v2, epsilon) {
return (Math.abs(v1.x - v2.x) <= epsilon &&
Math.abs(v1.y - v2.y) <= epsilon &&
Math.abs(v1.z - v2.z) <= epsilon
);
}
static unpack(array, startIndex, v) {
v.x = array[startIndex];
v.y = array[startIndex + 1];
v.z = array[startIndex + 2];
}
static multiplyByScalar(v, scalar, result) {
if (!defined(result)) result = new Vector3();
result.x = v.x * scalar;
result.y = v.y * scalar;
result.z = v.z * scalar;
return result;
}
static multiplyComponents(left, right, result) {
if (!defined(result)) result = new Vector3();
result.x = left.x * right.x;
result.y = left.y * right.y;
result.z = left.z * right.z;
return result;
}
static divideByScalar(v, scalar, result) {
if (!defined(result)) result = new Vector3();
result.x = v.x / scalar;
result.y = v.y / scalar;
result.z = v.z / scalar;
return result;
}
static subtract(left, right, result) {
if (!defined(result)) {
result = new Vector3();
}
result.set(
left.x - right.x,
left.y - right.y,
left.z - right.z
);
return result;
}
static negate(v, result) {
if (!defined(result)) {
result = new Vector3();
}
result.set(
-v.x, -v.y, -v.z
);
return result;
}
static dot(left, right) {
return left.x * right.x + left.y * right.y + left.z * right.z;
}
static cross(left, right, result) {
if (!defined(result)) result = new Vector3();
const leftX = left.x;
const leftY = left.y;
const leftZ = left.z;
const rightX = right.x;
const rightY = right.y;
const rightZ = right.z;
const x = leftY * rightZ - leftZ * rightY;
const y = leftZ * rightX - leftX * rightZ;
const z = leftX * rightY - leftY * rightX;
result.x = x;
result.y = y;
result.z = z;
return result;
}
clone() {
return new Vector3(this.x, this.y, this.z);
}
static clone(source) {
return new Vector3(source.x, source.y, source.z);
}
copy(source) {
this.x = source.x;
this.y = source.y;
this.z = source.z;
return this;
}
length() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
lengthSquare() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
normalize() {
const length = this.length() || 1;
return Vector3.divideByScalar(this, length, this);
}
set(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
static add(left, right, result) {
if (!defined(result)) result = new Vector3();
result.set(
left.x + right.x,
left.y + right.y,
left.z + right.z
);
return result;
}
static normalize(v, result) {
if (!defined(result)) result = new Vector3();
const length = v.length() || 1.0;
return Vector3.divideByScalar(v, length, result);
}
static ZERO = Object.freeze(new Vector3(0, 0, 0))
}
class Vector2 {
constructor(x, y) {
this.x = x || 0;
this.y = y || 0;
}
set(x, y) {
this.x = x;
this.y = y;
}
}
class Matrix4 {
constructor(
column0Row0,
column1Row0,
column2Row0,
column3Row0,
column0Row1,
column1Row1,
column2Row1,
column3Row1,
column0Row2,
column1Row2,
column2Row2,
column3Row2,
column0Row3,
column1Row3,
column2Row3,
column3Row3
) {
this[0] = defaultValue(column0Row0, 0.0);
this[1] = defaultValue(column0Row1, 0.0);
this[2] = defaultValue(column0Row2, 0.0);
this[3] = defaultValue(column0Row3, 0.0);
this[4] = defaultValue(column1Row0, 0.0);
this[5] = defaultValue(column1Row1, 0.0);
this[6] = defaultValue(column1Row2, 0.0);
this[7] = defaultValue(column1Row3, 0.0);
this[8] = defaultValue(column2Row0, 0.0);
this[9] = defaultValue(column2Row1, 0.0);
this[10] = defaultValue(column2Row2, 0.0);
this[11] = defaultValue(column2Row3, 0.0);
this[12] = defaultValue(column3Row0, 0.0);
this[13] = defaultValue(column3Row1, 0.0);
this[14] = defaultValue(column3Row2, 0.0);
this[15] = defaultValue(column3Row3, 0.0);
}
static multiplyByPoint(matrix, cartesian, result) {
if (!defined(result)) result = new Vector3();
const vX = cartesian.x;
const vY = cartesian.y;
const vZ = cartesian.z;
const x = matrix[0] * vX + matrix[4] * vY + matrix[8] * vZ + matrix[12];
const y = matrix[1] * vX + matrix[5] * vY + matrix[9] * vZ + matrix[13];
const z = matrix[2] * vX + matrix[6] * vY + matrix[10] * vZ + matrix[14];
result.x = x;
result.y = y;
result.z = z;
return result;
}
static inverseTransformation(matrix, result) {
if (!defined(result)) result = new Matrix4();
const matrix0 = matrix[0];
const matrix1 = matrix[1];
const matrix2 = matrix[2];
const matrix4 = matrix[4];
const matrix5 = matrix[5];
const matrix6 = matrix[6];
const matrix8 = matrix[8];
const matrix9 = matrix[9];
const matrix10 = matrix[10];
const vX = matrix[12];
const vY = matrix[13];
const vZ = matrix[14];
const x = -matrix0 * vX - matrix1 * vY - matrix2 * vZ;
const y = -matrix4 * vX - matrix5 * vY - matrix6 * vZ;
const z = -matrix8 * vX - matrix9 * vY - matrix10 * vZ;
result[0] = matrix0;
result[1] = matrix4;
result[2] = matrix8;
result[3] = 0.0;
result[4] = matrix1;
result[5] = matrix5;
result[6] = matrix9;
result[7] = 0.0;
result[8] = matrix2;
result[9] = matrix6;
result[10] = matrix10;
result[11] = 0.0;
result[12] = x;
result[13] = y;
result[14] = z;
result[15] = 1.0;
return result;
}
static multiply(left, right, result) {
if (!defined(result)) {
result = new Matrix4();
}
const left0 = left[0];
const left1 = left[1];
const left2 = left[2];
const left3 = left[3];
const left4 = left[4];
const left5 = left[5];
const left6 = left[6];
const left7 = left[7];
const left8 = left[8];
const left9 = left[9];
const left10 = left[10];
const left11 = left[11];
const left12 = left[12];
const left13 = left[13];
const left14 = left[14];
const left15 = left[15];
const right0 = right[0];
const right1 = right[1];
const right2 = right[2];
const right3 = right[3];
const right4 = right[4];
const right5 = right[5];
const right6 = right[6];
const right7 = right[7];
const right8 = right[8];
const right9 = right[9];
const right10 = right[10];
const right11 = right[11];
const right12 = right[12];
const right13 = right[13];
const right14 = right[14];
const right15 = right[15];
const column0Row0 =
left0 * right0 + left4 * right1 + left8 * right2 + left12 * right3;
const column0Row1 =
left1 * right0 + left5 * right1 + left9 * right2 + left13 * right3;
const column0Row2 =
left2 * right0 + left6 * right1 + left10 * right2 + left14 * right3;
const column0Row3 =
left3 * right0 + left7 * right1 + left11 * right2 + left15 * right3;
const column1Row0 =
left0 * right4 + left4 * right5 + left8 * right6 + left12 * right7;
const column1Row1 =
left1 * right4 + left5 * right5 + left9 * right6 + left13 * right7;
const column1Row2 =
left2 * right4 + left6 * right5 + left10 * right6 + left14 * right7;
const column1Row3 =
left3 * right4 + left7 * right5 + left11 * right6 + left15 * right7;
const column2Row0 =
left0 * right8 + left4 * right9 + left8 * right10 + left12 * right11;
const column2Row1 =
left1 * right8 + left5 * right9 + left9 * right10 + left13 * right11;
const column2Row2 =
left2 * right8 + left6 * right9 + left10 * right10 + left14 * right11;
const column2Row3 =
left3 * right8 + left7 * right9 + left11 * right10 + left15 * right11;
const column3Row0 =
left0 * right12 + left4 * right13 + left8 * right14 + left12 * right15;
const column3Row1 =
left1 * right12 + left5 * right13 + left9 * right14 + left13 * right15;
const column3Row2 =
left2 * right12 + left6 * right13 + left10 * right14 + left14 * right15;
const column3Row3 =
left3 * right12 + left7 * right13 + left11 * right14 + left15 * right15;
result[0] = column0Row0;
result[1] = column0Row1;
result[2] = column0Row2;
result[3] = column0Row3;
result[4] = column1Row0;
result[5] = column1Row1;
result[6] = column1Row2;
result[7] = column1Row3;
result[8] = column2Row0;
result[9] = column2Row1;
result[10] = column2Row2;
result[11] = column2Row3;
result[12] = column3Row0;
result[13] = column3Row1;
result[14] = column3Row2;
result[15] = column3Row3;
return result;
}
static fromTranslation(translation, result) {
if (!defined(result)) {
return new Matrix4(
1.0, 0.0, 0.0, translation.x,
0.0, 1.0, 0.0, translation.y,
0.0, 0.0, 1.0, translation.z,
0.0, 0.0, 0.0, 1.0
);
}
result[0] = 1.0;
result[1] = 0.0;
result[2] = 0.0;
result[3] = 0.0;
result[4] = 0.0;
result[5] = 1.0;
result[6] = 0.0;
result[7] = 0.0;
result[8] = 0.0;
result[9] = 0.0;
result[10] = 1.0;
result[11] = 0.0;
result[12] = translation.x;
result[13] = translation.y;
result[14] = translation.z;
result[15] = 1.0;
return result;
}
static fromRotationTranslation(rotation, translation, result) {
translation = defaultValue(translation, Vector3.ZERO);
if (!defined(result)) {
return new Matrix4(
rotation[0],
rotation[3],
rotation[6],
translation.x,
rotation[1],
rotation[4],
rotation[7],
translation.y,
rotation[2],
rotation[5],
rotation[8],
translation.z,
0.0,
0.0,
0.0,
1.0
);
}
result[0] = rotation[0];
result[1] = rotation[1];
result[2] = rotation[2];
result[3] = 0.0;
result[4] = rotation[3];
result[5] = rotation[4];
result[6] = rotation[5];
result[7] = 0.0;
result[8] = rotation[6];
result[9] = rotation[7];
result[10] = rotation[8];
result[11] = 0.0;
result[12] = translation.x;
result[13] = translation.y;
result[14] = translation.z;
result[15] = 1.0;
return result;
}
static fromScale(scale, result) {
if (!defined(result)) {
return new Matrix4(
scale.x,
0.0,
0.0,
0.0,
0.0,
scale.y,
0.0,
0.0,
0.0,
0.0,
scale.z,
0.0,
0.0,
0.0,
0.0,
1.0
);
}
result[0] = scale.x;
result[1] = 0.0;
result[2] = 0.0;
result[3] = 0.0;
result[4] = 0.0;
result[5] = scale.y;
result[6] = 0.0;
result[7] = 0.0;
result[8] = 0.0;
result[9] = 0.0;
result[10] = scale.z;
result[11] = 0.0;
result[12] = 0.0;
result[13] = 0.0;
result[14] = 0.0;
result[15] = 1.0;
return result;
}
static clone(matrix, result) {
if (!defined(result)) {
return new Matrix4(
matrix[0],
matrix[4],
matrix[8],
matrix[12],
matrix[1],
matrix[5],
matrix[9],
matrix[13],
matrix[2],
matrix[6],
matrix[10],
matrix[14],
matrix[3],
matrix[7],
matrix[11],
matrix[15]
);
}
result[0] = matrix[0];
result[1] = matrix[1];
result[2] = matrix[2];
result[3] = matrix[3];
result[4] = matrix[4];
result[5] = matrix[5];
result[6] = matrix[6];
result[7] = matrix[7];
result[8] = matrix[8];
result[9] = matrix[9];
result[10] = matrix[10];
result[11] = matrix[11];
result[12] = matrix[12];
result[13] = matrix[13];
result[14] = matrix[14];
result[15] = matrix[15];
return result;
}
static setTranslation(matrix, translation, result) {
if (!defined(result)) result = new Matrix4();
result[0] = matrix[0];
result[1] = matrix[1];
result[2] = matrix[2];
result[3] = matrix[3];
result[4] = matrix[4];
result[5] = matrix[5];
result[6] = matrix[6];
result[7] = matrix[7];
result[8] = matrix[8];
result[9] = matrix[9];
result[10] = matrix[10];
result[11] = matrix[11];
result[12] = translation.x;
result[13] = translation.y;
result[14] = translation.z;
result[15] = matrix[15];
return result;
}
}
class Rectangle {
constructor(west, south, east, north) {
this.west = west || 0;
this.south = south || 0;
this.east = east || 0;
this.north = north || 0;
}
clone(source) {
return new Rectangle(
source.west,
source.south,
source.east,
source.north
);
}
}
class Cartographic {
constructor(longitude, latitude, height) {
this.longitude = longitude || 0;
this.latitude = latitude || 0;
this.height = height || 0;
}
}
const scratchFirstArray = [0, 1, 0];
const scratchSecondArray = [-1, 0, 0];
const scratchThirdArray = [0, 0, 1];
const scratchFirstCartesian = new Vector3();
const scratchSecondCartesian = new Vector3();
const scratchThirdCartesian = new Vector3();
const scratchCalculateCartesian = {
east: new Vector3(),
north: new Vector3(),
up: new Vector3(),
west: new Vector3(),
south: new Vector3(),
down: new Vector3(),
}
class Transforms {
constructor(){}
static eastNorthUpToFixedFrame(origin, ellipsoid, result) {
if (!defined(result)) result = new Matrix4();
if (Vector3.equalsEpsilon(origin, Vector3.ZERO, EPSILON14)) {
Vector3.unpack(scratchFirstArray, 0, scratchFirstCartesian);
Vector3.unpack(scratchSecondArray, 0, scratchSecondCartesian);
Vector3.unpack(scratchThirdArray, 0, scratchThirdCartesian);
} else if (
equalsEpsilon(origin.x, 0.0, EPSILON14) &&
equalsEpsilon(origin.y, 0.0, EPSILON14)
) {
const sign = sign(origin.z);
Vector3.unpack(scratchFirstArray, 0, scratchFirstCartesian);
Vector3.unpack(scratchSecondArray, 0, scratchSecondCartesian);
Vector3.multiplyByScalar(scratchSecondCartesian, sign, scratchSecondCartesian);
Vector3.unpack(scratchThirdArray, 0, scratchThirdCartesian);
Vector3.multiplyByScalar(scratchThirdCartesian, sign, scratchThirdCartesian);
} else {
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
ellipsoid.geodeticSurfaceNormal(origin, scratchCalculateCartesian.up);
const up = scratchCalculateCartesian.up;
const east = scratchCalculateCartesian.east;
east.x = -origin.y;
east.y = origin.x;
east.z = 0.0;
Vector3.normalize(east, scratchCalculateCartesian.east);
Vector3.cross(up, east, scratchCalculateCartesian.north);
Vector3.multiplyByScalar(scratchCalculateCartesian.up, -1, scratchCalculateCartesian.down);
Vector3.multiplyByScalar(scratchCalculateCartesian.east, -1, scratchCalculateCartesian.west);
Vector3.multiplyByScalar(scratchCalculateCartesian.north, -1, scratchCalculateCartesian.south);
scratchFirstCartesian.copy(scratchCalculateCartesian.east);
scratchSecondCartesian.copy(scratchCalculateCartesian.north);
scratchThirdCartesian.copy(scratchCalculateCartesian.up);
}
result[0] = scratchFirstCartesian.x;
result[1] = scratchFirstCartesian.y;
result[2] = scratchFirstCartesian.z;
result[3] = 0.0;
result[4] = scratchSecondCartesian.x;
result[5] = scratchSecondCartesian.y;
result[6] = scratchSecondCartesian.z;
result[7] = 0.0;
result[8] = scratchThirdCartesian.x;
result[9] = scratchThirdCartesian.y;
result[10] = scratchThirdCartesian.z;
result[11] = 0.0;
result[12] = origin.x;
result[13] = origin.y;
result[14] = origin.z;
result[15] = 1.0;
return result;
}
}
class Ellipsoid {
constructor(x, y, z) {
x = x || 0;
y = y || 0;
z = z || 0;
this.x = x;
this.y = y;
this.z = z;
this.radii = new Vector3(x, y, z);
this.radiiSquared = new Vector3(x * x, y * y, z * z);
this.radiiToTheFourth = new Vector3(
x * x * x * x,
y * y * y * y,
z * z * z * z
);
this.oneOverRadii = new Vector3(
x === 0.0 ? 0.0 : 1.0 / x,
y === 0.0 ? 0.0 : 1.0 / y,
z === 0.0 ? 0.0 : 1.0 / z
);
this.oneOverRadiiSquared = new Vector3(
x === 0.0 ? 0.0 : 1.0 / (x * x),
y === 0.0 ? 0.0 : 1.0 / (y * y),
z === 0.0 ? 0.0 : 1.0 / (z * z)
);
this.minimumRadius = Math.min(x, y, z);
this.maximumRadius = Math.max(x, y, z);
this.centerToleranceSquared = 0.1;
this.squaredFirstEccentricity = 1 - (z * z) / (x * x);
if (this.radiiSquared.z !== 0) {
this.squaredXOverSquaredZ = this.radiiSquared.x / this.radiiSquared.z;
}
}
transformPositionToScaledSpace(position, result) {
if (!defined(result)) {
result = new Vector3();
}
return Vector3.multiplyComponents(position, this.oneOverRadii, result);
}
geodeticSurfaceNormal(cartesian, result) {
if (Vector3.equalsEpsilon(cartesian, Vector3.ZERO, EPSILON14)) {
return undefined;
}
if (!defined(result)) result = new Vector3();
let temp = this.oneOverRadiiSquared.clone();
result = Vector3.multiplyComponents(cartesian, temp, result);
return result.normalize();
}
cartographicToCartesian(cartographic, result) {
if (!defined(result)) result = new Vector3();
const cartographicToCartesianNormal = new Vector3();
const cartographicToCartesianK = new Vector3();
this.geodeticSurfaceNormalCartographic(cartographic, cartographicToCartesianNormal);
Vector3.multiplyComponents(this.radiiSquared, cartographicToCartesianNormal, cartographicToCartesianK);
const gamma = Math.sqrt(Vector3.dot(cartographicToCartesianNormal, cartographicToCartesianK));
Vector3.divideByScalar(cartographicToCartesianK, gamma, cartographicToCartesianK);
Vector3.multiplyByScalar(cartographicToCartesianNormal, cartographic.height, cartographicToCartesianNormal);
return Vector3.add(cartographicToCartesianK, cartographicToCartesianNormal, result);
}
geodeticSurfaceNormalCartographic(cartographic, result) {
if (!defined(result)) result = new Vector3();
const longitude = cartographic.longitude;
const latitude = cartographic.latitude;
const cosLatitude = Math.cos(latitude);
const x = cosLatitude * Math.cos(longitude);
const y = cosLatitude * Math.sin(longitude);
const z = Math.sin(latitude);
result.set(x, y, z);
return result.normalize();
}
}
Ellipsoid.WGS84 = Object.freeze(new Ellipsoid(6378137.0, 6378137.0, 6356752.3142451793))
class WebMercatorProjection {
constructor(){}
static mercatorAngleToGeodeticLatitude(mercatorAngle) {
return PI_OVER_TWO - 2.0 * Math.atan(Math.exp(-mercatorAngle));
}
static geodeticLatitudeToMercatorAngle(latitude) {
if (latitude > WebMercatorProjection.MaximumLatitude) {
latitude = WebMercatorProjection.MaximumLatitude;
} else if (latitude < -WebMercatorProjection.MaximumLatitude) {
latitude = -WebMercatorProjection.MaximumLatitude;
}
const sinLatitude = Math.sin(latitude);
return 0.5 * Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude));
}
}
WebMercatorProjection.MaximumLatitude = WebMercatorProjection.mercatorAngleToGeodeticLatitude(Math.PI);
class EllipsoidalOccluder {
constructor(ellipsoid) {
this.ellipsoid = ellipsoid;
this.cameraPosition = new Vector3();
this.cameraPositionInScaledSpace = new Vector3();
this.distanceToLimbInScaledSquared = 0.0;
}
computeHorizonCullingPointPossiblyUnderEllipsoid(directionToPoint, positions, minimumHeight, result) {
const possiblyShrunkEllipsoid = EllipsoidalOccluder.getPossiblyShrunkEllipsoid(
this.ellipsoid,
minimumHeight
);
return EllipsoidalOccluder.computeHorizonCullingPointFromPositions(
possiblyShrunkEllipsoid,
directionToPoint,
positions,
result
);
}
static getPossiblyShrunkEllipsoid(ellipsoid, minimumHeight) {
if (defined(minimumHeight) &&
minimumHeight < 0.0 &&
ellipsoid.minimumRadius > - minimumHeight
) {
const ellipsoidShrunkRadii = new Vector3(
ellipsoid.radii.x + minimumHeight,
ellipsoid.radii.y + minimumHeight,
ellipsoid.radii.z + minimumHeight,
);
ellipsoid = new Ellipsoid(
ellipsoidShrunkRadii.x,
ellipsoidShrunkRadii.y,
ellipsoidShrunkRadii.z
);
}
return ellipsoid;
}
static computeHorizonCullingPointFromPositions(ellipsoid, directionToPoint, positions, result) {
if (!defined(result)) result = new Vector3();
const scaledSpaceDirectionToPoint = EllipsoidalOccluder.computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint);
let resultMagnitude = 0.0;
for (let i = 0, len = positions.length; i < len; ++i) {
const position = positions[i];
const candidateMagnitude = EllipsoidalOccluder.computeMagnitude(
ellipsoid,
position,
scaledSpaceDirectionToPoint
);
if (candidateMagnitude < 0.0) {
// all points should face the same direction, but this one doesn't, so return undefined
return undefined;
}
resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);
}
return EllipsoidalOccluder.magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
}
static computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint){
if (Vector3.equals(directionToPoint, Vector3.ZERO)) {
return directionToPoint;
}
const directionToPointScratch = ellipsoid.transformPositionToScaledSpace(
directionToPoint
);
return directionToPointScratch.normalize();
}
static computeMagnitude(ellipsoid, position, scaledSpaceDirectionToPoint) {
const scaledSpacePosition = ellipsoid.transformPositionToScaledSpace(
position
);
let magnitudeSquared = scaledSpacePosition.lengthSquare();
let magnitude = scaledSpacePosition.length();
const direction = Vector3.divideByScalar(scaledSpacePosition, magnitude);
magnitudeSquared = Math.max(1.0, magnitudeSquared);
magnitude = Math.max(1.0, magnitude);
const cosAlpha = Vector3.dot(direction, scaledSpaceDirectionToPoint);
const sinAlpha = Vector3.cross(direction, scaledSpaceDirectionToPoint, direction).length()
const cosBeta = 1.0 / magnitude;
const sinBeta = Math.sqrt(magnitudeSquared - 1.0) * cosBeta;
return 1.0 / (cosAlpha * cosBeta - sinAlpha * sinBeta);
}
static magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result) {
if (resultMagnitude <= 0.0 ||
resultMagnitude === 1.0 / 0.0 ||
resultMagnitude !== resultMagnitude) {
return undefined;
}
return Vector3.multiplyByScalar(scaledSpaceDirectionToPoint, resultMagnitude, result);
}
}
class AxisAlignedBoundingBox {
constructor(minimum, maximum, center) {
this.minimum = defaultValue(minimum, Vector3.ZERO).clone();
this.maximum = defaultValue(maximum, Vector3.ZERO).clone();
if (!defined(center)) {
center = new Vector3(
(this.minimum.x + this.maximum.x) * 0.5,
(this.minimum.y + this.maximum.y) * 0.5,
(this.minimum.z + this.maximum.z) * 0.5
);
} else {
center = new Vector3(center.x, center.y, center.z);
}
this.center = center;
}
}
class TerrainEncoding {
constructor(
center,
axisAlignedBoundingBox,
minimumHeight,
maximumHeight,
fromENU,
hasVertexNormals,
hasWebMercatorT,
hasGeodeticSurfaceNormals,
exaggeration,
exaggerationRelativeHeight,
name
) {
let quantization = TerrainQuantization.NONE;
let toENU;
let matrix;
const cartesian3DimScratch = new Vector3();
if (
defined(axisAlignedBoundingBox) &&
defined(minimumHeight) &&
defined(maximumHeight) &&
defined(fromENU)
) {
const minimum = axisAlignedBoundingBox.minimum;
const maximum = axisAlignedBoundingBox.maximum;
const dimensions = Vector3.subtract(
maximum,
minimum,
cartesian3DimScratch
);
const hDim = maximumHeight - minimumHeight;
const maxDim = Math.max(Vector3.maximumComponent(dimensions), hDim);
if (maxDim < SHIFT_LEFT_12 - 1.0) {
quantization = TerrainQuantization.BITS12;
} else {
quantization = TerrainQuantization.NONE;
}
toENU = Matrix4.inverseTransformation(fromENU, new Matrix4());
const matrix4Scratch13T = new Matrix4();
const cartesian3Scratch13T = new Vector3();
const matrix4Scratch2 = new Matrix4();
const translation = Vector3.negate(minimum, cartesian3Scratch13T);
Matrix4.multiply(
Matrix4.fromTranslation(translation, matrix4Scratch13T),
toENU,
toENU
);
const scale = cartesian3Scratch13T;
scale.x = 1.0 / dimensions.x;
scale.y = 1.0 / dimensions.y;
scale.z = 1.0 / dimensions.z;
Matrix4.multiply(Matrix4.fromScale(scale, matrix4Scratch13T), toENU, toENU);
matrix = Matrix4.clone(fromENU);
Matrix4.setTranslation(matrix, Vector3.ZERO, matrix);
fromENU = Matrix4.clone(fromENU, new Matrix4());
const translationMatrix = Matrix4.fromTranslation(minimum, matrix4Scratch13T);
const scaleMatrix = Matrix4.fromScale(dimensions, matrix4Scratch2);
const st = Matrix4.multiply(translationMatrix, scaleMatrix, matrix4Scratch13T);
Matrix4.multiply(fromENU, st, fromENU);
Matrix4.multiply(matrix, st, matrix);
}
this.quantization = quantization;
this.minimumHeight = minimumHeight;
this.maximumHeight = maximumHeight;
this.center = Vector3.clone(center);
this.toScaledENU = toENU;
this.fromScaledENU = fromENU;
this.matrix = matrix;
this.hasVertexNormals = hasVertexNormals;
this.hasWebMercatorT = defaultValue(hasWebMercatorT, false);
this.hasGeodeticSurfaceNormals = defaultValue(
hasGeodeticSurfaceNormals,
false
);
this.exaggeration = defaultValue(exaggeration, 1.0);
this.exaggerationRelativeHeight = defaultValue(
exaggerationRelativeHeight,
0.0
);
this.stride = 0;
this._offsetGeodeticSurfaceNormal = 0;
this._offsetVertexNormal = 0;
this._calculateStrideAndOffsets();
}
_calculateStrideAndOffsets() {
let vertexStride = 0;
switch (this.quantization) {
case TerrainQuantization.BITS12:
vertexStride += 3;
break;
default:
vertexStride += 6;
}
if (this.hasWebMercatorT) {
vertexStride += 1;
}
if (this.hasVertexNormals) {
this._offsetVertexNormal = vertexStride;
vertexStride += 1;
}
if (this.hasGeodeticSurfaceNormals) {
this._offsetGeodeticSurfaceNormal = vertexStride;
vertexStride += 3;
}
this.stride = vertexStride;
}
encode(vertexBuffer, bufferIndex, position, uv, height, normalToPack, webMercatorT, geodeticSurfaceNormal) {
const cartesian3Scratch13T = new Vector3();
const cartesian2Scratch = new Vector2();
const u = uv.x;
const v = uv.y;
if (this.quantization === TerrainQuantization.BITS12) {
position = Matrix4.multiplyByPoint(
this.toScaledENU,
position,
cartesian3Scratch13T
);
position.x = clamp(position.x, 0.0, 1.0);
position.y = clamp(position.y, 0.0, 1.0);
position.z = clamp(position.z, 0.0, 1.0);
const hDim = this.maximumHeight - this.minimumHeight;
const h = clamp((height - this.minimumHeight) / hDim, 0.0, 1.0);
cartesian2Scratch.set(position.x, position.y);
const compressed0 = AttributeCompression.compressTextureCoordinates(
cartesian2Scratch
);
cartesian2Scratch.set(position.z, h);
const compressed1 = AttributeCompression.compressTextureCoordinates(
cartesian2Scratch
);
cartesian2Scratch.set(u, v);
const compressed2 = AttributeCompression.compressTextureCoordinates(
cartesian2Scratch
);
vertexBuffer[bufferIndex++] = compressed0;
vertexBuffer[bufferIndex++] = compressed1;
vertexBuffer[bufferIndex++] = compressed2;
if (this.hasWebMercatorT) {
cartesian2Scratch.set(webMercatorT, 0.0);
const compressed3 = AttributeCompression.compressTextureCoordinates(
cartesian2Scratch
);
vertexBuffer[bufferIndex++] = compressed3;
}
} else {
Vector3.subtract(position, this.center, cartesian3Scratch13T);
vertexBuffer[bufferIndex++] = cartesian3Scratch13T.x;
vertexBuffer[bufferIndex++] = cartesian3Scratch13T.y;
vertexBuffer[bufferIndex++] = cartesian3Scratch13T.z;
vertexBuffer[bufferIndex++] = height;
vertexBuffer[bufferIndex++] = u;
vertexBuffer[bufferIndex++] = v;
if (this.hasWebMercatorT) {
vertexBuffer[bufferIndex++] = webMercatorT;
}
}
if (this.hasVertexNormals) {
vertexBuffer[bufferIndex++] = AttributeCompression.octPackFloat(
normalToPack
);
}
if (this.hasGeodeticSurfaceNormals) {
vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.x;
vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.y;
vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.z;
}
return bufferIndex;
}
}
class IndexDatatype {
constructor() {}
static createTypedArray(numberOfVertices, indicesLengthOrArray) {
if (!defined(numberOfVertices)) {
throw new Error("numberOfVertices is required");
}
if (numberOfVertices >= 6 * 1024 * 1024) {
return new Uint32Array(indicesLengthOrArray);
}
return new Uint16Array(indicesLengthOrArray);
}
}
class TerrainProvider {
constructor(){}
static addSkirtIndice(edgeIndices, vertexIndex, indices, offset) {
let previousIndex = edgeIndices[0];
const length = edgeIndices.length;
for (let i = 1; i < length; ++i) {
const index = edgeIndices[i];
indices[offset++] = previousIndex;
indices[offset++] = index;
indices[offset++] = vertexIndex;
indices[offset++] = vertexIndex;
indices[offset++] = index;
indices[offset++] = vertexIndex + 1;
previousIndex = index;
++vertexIndex;
}
return offset;
}
static addSkirtIndices(
westIndicesSouthToNorth,
southIndicesEastToWest,
eastIndicesNorthToSouth,
northIndicesWestToEast,
vertexCount,
indices,
offset
) {
let vertexIndex = vertexCount;
offset = TerrainProvider.addSkirtIndice(
westIndicesSouthToNorth,
vertexIndex,
indices,
offset
);
vertexIndex += westIndicesSouthToNorth.length;
offset = TerrainProvider.addSkirtIndice(
southIndicesEastToWest,
vertexIndex,
indices,
offset
);
vertexIndex += southIndicesEastToWest.length;
offset = TerrainProvider.addSkirtIndice(
eastIndicesNorthToSouth,
vertexIndex,
indices,
offset
);
vertexIndex += eastIndicesNorthToSouth.length;
TerrainProvider.addSkirtIndice(northIndicesWestToEast, vertexIndex, indices, offset);
}
}
// class Vertex {}
class AttributeCompression {
constructor(){}
static compressTextureCoordinates(textureCoordinates) {
const x = (textureCoordinates.x * 4095.0) | 0;
const y = (textureCoordinates.y * 4095.0) | 0;
return 4096 * x + y;
}
static octPackFloat(encoded) {
return 256.0 * encoded.x + encoded.y;
}
}
const cartographicScratch = new Cartographic();
const cartesian3Scratch = new Vector3();
const toPack = new Vector2();
const maxShort = 32767;
const halfMaxShort = (maxShort / 2) || 0;
function createVerticesFromQuantizedTerrainMesh(parameters) {
const quantizedVertices = parameters.quantizedVertices;
const quantizedVerticeCount = quantizedVertices.length / 3;
const octEncodedNormals = parameters.octEncodedNormals;
const edgeVertexCount =
parameters.westIndices.length +
parameters.eastIndices.length +
parameters.southIndices.length +
parameters.northIndices.length;
const includeWebMercatorT = parameters.includeWebMercatorT;
const exaggeration = parameters.exaggeration;
const exaggerationRelativeHeight = parameters.exaggerationRelativeHeight;
const hasExaggeration = exaggeration !== 1.0;
const includeGeodeticSurfaceNormals = hasExaggeration;
const rectangle = new Rectangle().clone(parameters.rectangle);
const [west, east, north, south] = [rectangle.west, rectangle.east, rectangle.north, rectangle.south];
const ellipsoid = new Ellipsoid(parameters.ellipsoid.x, parameters.ellipsoid.y, parameters.ellipsoid.z); // parameters.ellipsoid
const minimumHeight = parameters.minimumHeight;
const maximumHeight = parameters.maximumHeight;
const center = parameters.relativeToCenter;
const fromENU = Transforms.eastNorthUpToFixedFrame(center, ellipsoid);
const name = parameters.name;
const toENU = Matrix4.inverseTransformation(fromENU);
let southMercatorY, oneOverMercatorHeight;
if (includeWebMercatorT) {
southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(south);
oneOverMercatorHeight = 1.0 / (WebMercatorProjection.geodeticLatitudeToMercatorAngle(north) - southMercatorY);
}
const uBuffer = quantizedVertices.subarray(0, quantizedVerticeCount);
const vBuffer = quantizedVertices.subarray(quantizedVerticeCount, quantizedVerticeCount * 2);
const heightBuffer = quantizedVertices.subarray(quantizedVerticeCount * 2, quantizedVerticeCount * 3);
const hasVertexNormals = defined(octEncodedNormals);
// 准备UV、高度、经纬度数组
const uvs = new Array(quantizedVerticeCount),
heights = new Array(quantizedVerticeCount),
positions = new Array(quantizedVerticeCount);
const webMercatorTs = includeWebMercatorT ? new Array(quantizedVerticeCount) : [];
const geodeticSurfaceNormals = includeGeodeticSurfaceNormals ? new Array(quantizedVerticeCount) : [];
let minimum = new Vector3(
Number.POSITIVE_INFINITY,
Number.POSITIVE_INFINITY,
Number.POSITIVE_INFINITY
);
let maximum = new Vector3(
Number.NEGATIVE_INFINITY,
Number.NEGATIVE_INFINITY,
Number.NEGATIVE_INFINITY
);
let minLongitude = Number.POSITIVE_INFINITY;
let maxLongitude = Number.NEGATIVE_INFINITY;
let minLatitude = Number.POSITIVE_INFINITY;
let maxLatitude = Number.NEGATIVE_INFINITY;
for (let i = 0; i < quantizedVerticeCount; ++i) {
const rawU = uBuffer[i];
const rawV = vBuffer[i];
const u = rawU / maxShort;
const v = rawV / maxShort;
const height = lerp(minimumHeight, maximumHeight, heightBuffer[i] / maxShort);
cartographicScratch.longitude = lerp(west, east, u);
cartographicScratch.latitude = lerp(south, north, v);
cartographicScratch.height = height;
minLongitude = Math.min(
cartographicScratch.longitude,
minLongitude
);
maxLongitude = Math.max(
cartographicScratch.longitude,
maxLongitude
);
minLatitude = Math.min(
cartographicScratch.latitude,
minLatitude
);
maxLatitude = Math.max(
cartographicScratch.latitude,
maxLatitude
);
const position = ellipsoid.cartographicToCartesian(cartographicScratch);
uvs[i] = new Vector3(u, v, 0);
heights[i] = height;
positions[i] = position;
if (includeWebMercatorT) {
webMercatorTs[i] = (
WebMercatorProjection.geodeticLatitudeToMercatorAngle(cartographicScratch.latitude)
-
southMercatorY
) * oneOverMercatorHeight;
}
if (includeGeodeticSurfaceNormals) {
geodeticSurfaceNormals[i] = ellipsoid.geodeticSurfaceNormal(position);
}
Matrix4.multiplyByPoint(toENU, position, cartesian3Scratch);
Vector3.minimumByComponent(cartesian3Scratch, minimum, minimum);
Vector3.maximumByComponent(cartesian3Scratch, maximum, maximum);
}
// 处理四条边的索引
const westIndicesSouthToNorth = copyAndSort(
parameters.westIndices,
function(a, b) { return uvs[a].y - uvs[b].y; }
);
const eastIndicesNorthToSouth = copyAndSort(
parameters.eastIndices,
function(a, b) { return uvs[b].y - uvs[a].y; }
);
const southIndicesEastToWest = copyAndSort(
parameters.southIndices,
function(a, b) { return uvs[b].x - uvs[a].x; }
);
const northIndicesWestToEast = copyAndSort(
parameters.northIndices,
function(a, b) { return uvs[a].x - uvs[b].x; }
);
// 处理地平线
let occludeePointInScaledSpace;
if (minimumHeight < 0.0) {
const occluder = new EllipsoidalOccluder(ellipsoid);
occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(center, positions, minimumHeight);
}
// 处理高程
let hMin = minimumHeight;
hMin = Math.min(
hMin,
findMinMaxSkirts(
parameters.westIndices,
parameters.westSkirtHeight,
heights,
uvs,
rectangle,
ellipsoid,
toENU,
minimum,
maximum
)
);
hMin = Math.min(
hMin,
findMinMaxSkirts(
parameters.southIndices,
parameters.southSkirtHeight,
heights,
uvs,
rectangle,
ellipsoid,
toENU,
minimum,
maximum
)
);
hMin = Math.min(
hMin,
findMinMaxSkirts(
parameters.eastIndices,
parameters.eastSkirtHeight,
heights,
uvs,
rectangle,
ellipsoid,
toENU,
minimum,
maximum
)
);
hMin = Math.min(
hMin,
findMinMaxSkirts(
parameters.northIndices,
parameters.northSkirtHeight,
heights,
uvs,
rectangle,
ellipsoid,
toENU,
minimum,
maximum
)
);
const aaBox = new AxisAlignedBoundingBox(minimum, maximum, center);
// console.log(name, center, aaBox, hMin, maximumHeight, fromENU, hasVertexNormals, includeWebMercatorT, includeGeodeticSurfaceNormals, exaggeration, exaggerationRelativeHeight)
const encoding = new TerrainEncoding(center, aaBox, hMin, maximumHeight, fromENU, hasVertexNormals, includeWebMercatorT, includeGeodeticSurfaceNormals, exaggeration, exaggerationRelativeHeight, name);
const vertexStride = encoding.stride;
const size = (quantizedVerticeCount + edgeVertexCount) * vertexStride;
const vertexBuffer = new Float32Array(size);
let bufferIndex = 0;
for (let j = 0; j < quantizedVerticeCount; ++j) {
if (hasVertexNormals) {
const n = j * 2.0;
toPack.x = octEncodedNormals[n];
toPack.y = octEncodedNormals[n + 1];
}
bufferIndex = encoding.encode(
vertexBuffer,
bufferIndex,
positions[j],
uvs[j],
heights[j],
toPack,
webMercatorTs[j],
geodeticSurfaceNormals[j]
);
}
const edgeTraiangleCount = Math.max(0, (edgeVertexCount - 4) * 2);
const indexBufferLength = parameters.indices.length + edgeTraiangleCount * 3;
const indexBuffer = IndexDatatype.createTypedArray(quantizedVerticeCount + edgeVertexCount, indexBufferLength);
indexBuffer.set(parameters.indices, 0);
const percentage = 0.0001;
const lonOffset = (maxLongitude - minLongitude) * percentage;
const latOffset = (maxLatitude - minLatitude) * percentage;
const westLongitudeOffset = -lonOffset;
const westLatitudeOffset = 0.0;
const eastLongitudeOffset = lonOffset;
const eastLatitudeOffset = 0.0;
const northLongitudeOffset = 0.0;
const northLatitudeOffset = latOffset;
const southLongitudeOffset = 0.0;
const southLatitudeOffset = -latOffset;
// 添加裙边
let vertexBufferIndex = quantizedVerticeCount * vertexStride;
addSkirt(vertexBuffer, vertexBufferIndex, westIndicesSouthToNorth, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.westSkirtHeight, southMercatorY, oneOverMercatorHeight, westLongitudeOffset, westLatitudeOffset);
vertexBufferIndex += parameters.westIndices.length * vertexStride;
addSkirt(vertexBuffer, vertexBufferIndex, southIndicesEastToWest, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.southSkirtHeight, southMercatorY, oneOverMercatorHeight, southLongitudeOffset, southLatitudeOffset);
vertexBufferIndex += parameters.southIndices.length * vertexStride;
addSkirt(vertexBuffer, vertexBufferIndex, eastIndicesNorthToSouth, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.eastSkirtHeight, southMercatorY, oneOverMercatorHeight, eastLongitudeOffset, eastLatitudeOffset);
vertexBufferIndex += parameters.eastIndices.length * vertexStride;
addSkirt(vertexBuffer, vertexBufferIndex, northIndicesWestToEast, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.northSkirtHeight, southMercatorY, oneOverMercatorHeight, northLongitudeOffset, northLatitudeOffset);
TerrainProvider.addSkirtIndices(
westIndicesSouthToNorth,
southIndicesEastToWest,
eastIndicesNorthToSouth,
northIndicesWestToEast,
quantizedVerticeCount,
indexBuffer,
parameters.indices.length
);
return {
vertices: vertexBuffer.buffer,
indices: indexBuffer.buffer,
westIndicesSouthToNorth: westIndicesSouthToNorth,
southIndicesEastToWest: southIndicesEastToWest,
eastIndicesNorthToSouth: eastIndicesNorthToSouth,
northIndicesWestToEast: northIndicesWestToEast,
vertexStride: vertexStride,
center: center,
minimumHeight: minimumHeight,
maximumHeight: maximumHeight,
occludeePointInScaledSpace: occludeePointInScaledSpace,
encoding: encoding,
indexCountWithoutSkirts: parameters.indices.length
};
}
function copyAndSort(typedArray, comparator) {
let copy;
if (typeof typedArray.slice === "function") {
copy = typedArray.slice();
if (typeof copy.sort !== "function") {
copy = undefined;
}
}
if (!defined(copy)) {
copy = Array.prototype.slice.call(typedArray);
}
copy.sort(comparator);
return copy;
}
function findMinMaxSkirts(
edgeIndices,
edgeHeight,
heights,
uvs,
rectangle,
ellipsoid,
toENU,
minimum,
maximum
) {
let hMin = Number.POSITIVE_INFINITY;
const north = rectangle.north;
const south = rectangle.south;
let east = rectangle.east;
const west = rectangle.west;
if (east < west) {
east += Math.PI * 2;
}
const length = edgeIndices.length;
for (let i = 0; i < length; ++i) {
const index = edgeIndices[i];
const h = heights[index];
const uv = uvs[index];
cartographicScratch.longitude = lerp(west, east, uv.x);
cartographicScratch.latitude = lerp(south, north, uv.y);
cartographicScratch.height = h - edgeHeight;
const position = ellipsoid.cartographicToCartesian(
cartographicScratch,
cartesian3Scratch,
);
Matrix4.multiplyByPoint(toENU, position, position);
Vector3.minimumByComponent(position, minimum, minimum);
Vector3.maximumByComponent(position, maximum, maximum);
hMin = Math.min(hMin, cartographicScratch.height);
}
return hMin;
}
function addSkirt(
vertexBuffer,
vertexBufferIndex,
edgeVertices,
encoding,
heights,
uvs,
octEncodedNormals,
ellipsoid,
rectangle,
skirtLength,
southMercatorY,
oneOverMercatorHeight,
longitudeOffset,
latitudeOffset
) {
const hasVertexNormals = defined(octEncodedNormals);
const north = rectangle.north;
const south = rectangle.south;
let east = rectangle.east;
const west = rectangle.west;
if (east < west) {
east += Math.PI * 2;
}
const length = edgeVertices.length;
for(let i = 0; i < length; ++i) {
const index = edgeVertices[i];
const h = heights[index];
const uv = uvs[index];
cartographicScratch.longitude = lerp(west, east, uv.x) + longitudeOffset;
cartographicScratch.latitude = lerp(south, north, uv.y) + latitudeOffset;
cartographicScratch.height = h - skirtLength;
const position = ellipsoid.cartographicToCartesian(
cartographicScratch,
cartesian3Scratch
);
if (hasVertexNormals) {
const n = index * 2.0;
toPack.x = octEncodedNormals[n];
toPack.y = octEncodedNormals[n + 1];
}
let webMercatorT;
if (encoding.hasWebMercatorT) {
webMercatorT = (
WebMercatorProjection.geodeticLatitudeToMercatorAngle(cartographicScratch.latitude)
-
southMercatorY
) * oneOverMercatorHeight;
}
let geodeticSurfaceNormal;
if (encoding.hasGeodeticSurfaceNormals) {
geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(position, geodeticSurfaceNormalScratch);
}
vertexBufferIndex = encoding.encode(
vertexBuffer,
vertexBufferIndex,
position,
uv,
cartographicScratch.height,
toPack,
webMercatorT,
geodeticSurfaceNormal
);
}
}
onmessage = function(e) {
const data = e.data;
const id = data.id;
const parameters = data.parameters;
const responseData = {
id: id,
result: undefined,
error: undefined
}
return Promise.resolve(
createVerticesFromQuantizedTerrainMesh(parameters)
).then(result => {
responseData.result = result
}).catch(e => {
if (e instanceof Error) {
responseData.error = {
name: e.name,
message: e.message,
stack: e.stack
}
} else {
responseData.error = e;
}
}).finally(() => {
postMessage(responseData);
})
}