forked from CesiumGS/cesium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PolylinePipeline.js
353 lines (314 loc) · 13.4 KB
/
PolylinePipeline.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*global define*/
define([
'./defaultValue',
'./defined',
'./DeveloperError',
'./Cartographic',
'./Cartesian3',
'./Cartesian4',
'./Ellipsoid',
'./EllipsoidGeodesic',
'./IntersectionTests',
'./Math',
'./Matrix4',
'./Plane'
], function(
defaultValue,
defined,
DeveloperError,
Cartographic,
Cartesian3,
Cartesian4,
Ellipsoid,
EllipsoidGeodesic,
IntersectionTests,
CesiumMath,
Matrix4,
Plane) {
"use strict";
/**
* DOC_TBA
*
* @exports PolylinePipeline
*/
var PolylinePipeline = {};
var wrapLongitudeInversMatrix = new Matrix4();
var wrapLongitudeOrigin = new Cartesian3();
var wrapLongitudeXZNormal = new Cartesian3();
var wrapLongitudeXZPlane = new Plane(Cartesian3.ZERO, 0.0);
var wrapLongitudeYZNormal = new Cartesian3();
var wrapLongitudeYZPlane = new Plane(Cartesian3.ZERO, 0.0);
var wrapLongitudeIntersection = new Cartesian3();
var wrapLongitudeOffset = new Cartesian3();
var carto1 = new Cartographic();
var carto2 = new Cartographic();
var cartesian = new Cartesian3();
var scaleFirst = new Cartesian3();
var scaleLast = new Cartesian3();
var ellipsoidGeodesic = new EllipsoidGeodesic();
//Returns subdivided line scaled to ellipsoid surface starting at p1 and ending at p2.
//Result includes p1, but not include p2. This function is called for a sequence of line segments,
//and this prevents duplication of end point.
function generateCartesianArc(p1, p2, granularity, ellipsoid) {
var first = ellipsoid.scaleToGeodeticSurface(p1, scaleFirst);
var last = ellipsoid.scaleToGeodeticSurface(p2, scaleLast);
var separationAngle = Cartesian3.angleBetween(first, last);
var numPoints = Math.ceil(separationAngle/granularity);
var result = new Array(numPoints*3);
var start = ellipsoid.cartesianToCartographic(first, carto1);
var end = ellipsoid.cartesianToCartographic(last, carto2);
ellipsoidGeodesic.setEndPoints(start, end);
var surfaceDistanceBetweenPoints = ellipsoidGeodesic.getSurfaceDistance() / (numPoints);
var index = 0;
start.height = 0;
var cart = ellipsoid.cartographicToCartesian(start, cartesian);
result[index++] = cart.x;
result[index++] = cart.y;
result[index++] = cart.z;
for (var i = 1; i < numPoints; i++) {
var carto = ellipsoidGeodesic.interpolateUsingSurfaceDistance(i * surfaceDistanceBetweenPoints, carto2);
cart = ellipsoid.cartographicToCartesian(carto, cartesian);
result[index++] = cart.x;
result[index++] = cart.y;
result[index++] = cart.z;
}
return result;
}
var scaleN = new Cartesian3();
var scaleP = new Cartesian3();
function computeHeight(p, h, ellipsoid) {
var n = scaleN;
ellipsoid.geodeticSurfaceNormal(p, n);
Cartesian3.multiplyByScalar(n, h, n);
Cartesian3.add(p, n, p);
return p;
}
/**
* Breaks a {@link Polyline} into segments such that it does not cross the ±180 degree meridian of an ellipsoid.
* @memberof PolylinePipeline
*
* @param {Array} positions The polyline's Cartesian positions.
* @param {Matrix4} [modelMatrix=Matrix4.IDENTITY] The polyline's model matrix. Assumed to be an affine
* transformation matrix, where the upper left 3x3 elements are a rotation matrix, and
* the upper three elements in the fourth column are the translation. The bottom row is assumed to be [0, 0, 0, 1].
* The matrix is not verified to be in the proper form.
*
* @returns {Object} An object with a <code>positions</code> property that is an array of positions and a
* <code>segments</code> property.
*
* @see PolygonPipeline.wrapLongitude
* @see Polyline
* @see PolylineCollection
*
* @example
* var polylines = new PolylineCollection();
* var polyline = polylines.add(...);
* var positions = polyline.getPositions();
* var modelMatrix = polylines.modelMatrix;
* var segments = PolylinePipeline.wrapLongitude(positions, modelMatrix);
*/
PolylinePipeline.wrapLongitude = function(positions, modelMatrix) {
var cartesians = [];
var segments = [];
if (defined(positions) && positions.length > 0) {
modelMatrix = defaultValue(modelMatrix, Matrix4.IDENTITY);
var inverseModelMatrix = Matrix4.inverseTransformation(modelMatrix, wrapLongitudeInversMatrix);
var origin = Matrix4.multiplyByPoint(inverseModelMatrix, Cartesian3.ZERO, wrapLongitudeOrigin);
var xzNormal = Matrix4.multiplyByPointAsVector(inverseModelMatrix, Cartesian3.UNIT_Y, wrapLongitudeXZNormal);
var xzPlane = Plane.fromPointNormal(origin, xzNormal, wrapLongitudeXZPlane);
var yzNormal = Matrix4.multiplyByPointAsVector(inverseModelMatrix, Cartesian3.UNIT_X, wrapLongitudeYZNormal);
var yzPlane = Plane.fromPointNormal(origin, yzNormal, wrapLongitudeYZPlane);
var count = 1;
cartesians.push(Cartesian3.clone(positions[0]));
var prev = cartesians[0];
var length = positions.length;
for ( var i = 1; i < length; ++i) {
var cur = positions[i];
// intersects the IDL if either endpoint is on the negative side of the yz-plane
if (Plane.getPointDistance(yzPlane, prev) < 0.0 || Plane.getPointDistance(yzPlane, cur) < 0.0) {
// and intersects the xz-plane
var intersection = IntersectionTests.lineSegmentPlane(prev, cur, xzPlane, wrapLongitudeIntersection);
if (defined(intersection)) {
// move point on the xz-plane slightly away from the plane
var offset = Cartesian3.multiplyByScalar(xzNormal, 5.0e-9, wrapLongitudeOffset);
if (Plane.getPointDistance(xzPlane, prev) < 0.0) {
Cartesian3.negate(offset, offset);
}
cartesians.push(Cartesian3.add(intersection, offset));
segments.push(count + 1);
Cartesian3.negate(offset, offset);
cartesians.push(Cartesian3.add(intersection, offset));
count = 1;
}
}
cartesians.push(Cartesian3.clone(positions[i]));
count++;
prev = cur;
}
segments.push(count);
}
return {
positions : cartesians,
lengths : segments
};
};
/**
* Removes adjacent duplicate positions in an array of positions.
*
* @memberof PolylinePipeline
*
* @param {Array} positions The array of {Cartesian3} positions.
*
* @returns {Array} A new array of positions with no adjacent duplicate positions. Positions are shallow copied.
*
* @exception {DeveloperError} positions is required.
*
* @example
* // Returns [(1.0, 1.0, 1.0), (2.0, 2.0, 2.0)]
* var positions = [
* new Cartesian3(1.0, 1.0, 1.0),
* new Cartesian3(1.0, 1.0, 1.0),
* new Cartesian3(2.0, 2.0, 2.0)];
* var nonDuplicatePositions = PolylinePipeline.removeDuplicates(positions);
*/
PolylinePipeline.removeDuplicates = function(positions) {
if (!defined(positions )) {
throw new DeveloperError('positions is required.');
}
var length = positions.length;
if (length < 2) {
return positions.slice(0);
}
var cleanedPositions = [];
cleanedPositions.push(positions[0]);
for (var i = 1; i < length; ++i) {
var v0 = positions[i - 1];
var v1 = positions[i];
if (!Cartesian3.equals(v0, v1)) {
cleanedPositions.push(v1); // Shallow copy!
}
}
return cleanedPositions;
};
/**
* Subdivides polyline and raises all points to the ellipsoid surface
*
* @memberof PolylinePipeline
*
* @param {Array} positions The array of positions of type {Cartesian3}.
* @param {Number} [granularity = CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
* @param {Ellipsoid} [ellipsoid = Ellipsoid.WGS84] The ellipsoid on which the positions lie.
*
* @returns {Array} A new array of positions of type {Number} that have been subdivided and raised to the surface of the ellipsoid.
*
* @exception {DeveloperError} positions is required
*
* @example
* var positions = ellipsoid.cartographicArrayToCartesianArray([
* Cartographic.fromDegrees(-105.0, 40.0),
* Cartographic.fromDegrees(-100.0, 38.0),
* Cartographic.fromDegrees(-105.0, 35.0),
* Cartographic.fromDegrees(-100.0, 32.0)
* ]));
* var surfacePositions = PolylinePipeline.scaleToSurface(positions);
*/
PolylinePipeline.scaleToSurface = function(positions, granularity, ellipsoid) {
if (!defined(positions)) {
throw new DeveloperError('positions is required');
}
granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE);
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
var length = positions.length;
var newPositions = [];
for (var i = 0; i < length - 1; i++) {
var p0 = positions[i];
var p1 = positions[i+1];
newPositions = newPositions.concat(generateCartesianArc(p0, p1, granularity, ellipsoid));
}
var lastPoint = positions[length-1];
var carto = ellipsoid.cartesianToCartographic(lastPoint, carto1);
carto.height = 0;
var cart = ellipsoid.cartographicToCartesian(carto, cartesian);
newPositions.push(cart.x, cart.y, cart.z);
return newPositions;
};
/**
* Raises the positions to the given height.
*
* @memberof PolylinePipeline
*
* @param {Array} positions The array of type {Number} representing positions.
* @param {Number|Array} height A number or array of numbers representing the heights of each position.
* @param {Ellipsoid} [ellipsoid = Ellipsoid.WGS84] The ellipsoid on which the positions lie.
* @param {Array} [result] An array to place the resultant positions in.
*
* @returns {Array} The array of positions scaled to height.
* @exception {DeveloperError} positions must be defined.
* @exception {DeveloperError} height must be defined.
* @exception {DeveloperError} result.length must be equal to positions.length
* @exception {DeveloperError} height.length must be equal to positions.length
*
* @example
* var p1 = ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-105.0, 40.0));
* var p2 = ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-100.0, 38.0));
* var positions = [p1.x, p1.y, p1.z, p2.x, p2.y, p2.z];
* var heights = [1000, 1000, 2000, 2000];
*
* var raisedPositions = PolylinePipeline.scaleToGeodeticHeight(positions, heights);
*/
PolylinePipeline.scaleToGeodeticHeight = function(positions, height, ellipsoid, result) {
if (!defined(positions)) {
throw new DeveloperError('positions must be defined.');
}
if (!defined(height)) {
throw new DeveloperError('height must be defined');
}
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
var length = positions.length;
var i;
var p = scaleP;
var newPositions;
if (defined(result)) {
if (result.length !== positions.length) {
throw new DeveloperError('result.length must be equal to positions.length');
}
newPositions = result;
} else {
newPositions = new Array(positions.length);
}
if (height === 0) {
for(i = 0; i < length; i+=3) {
p = ellipsoid.scaleToGeodeticSurface(Cartesian3.fromArray(positions, i, p), p);
newPositions[i] = p.x;
newPositions[i + 1] = p.y;
newPositions[i + 2] = p.z;
}
return newPositions;
}
var h;
if (Array.isArray(height)) {
if (height.length !== length/3) {
throw new DeveloperError('height.length must be equal to positions.length');
}
for (i = 0; i < length; i += 3) {
h = height[i/3];
p = Cartesian3.fromArray(positions, i, p);
p = computeHeight(p, h, ellipsoid);
newPositions[i] = p.x;
newPositions[i + 1] = p.y;
newPositions[i + 2] = p.z;
}
} else {
h = height;
for (i = 0; i < length; i += 3) {
p = Cartesian3.fromArray(positions, i, p);
p = computeHeight(p, h, ellipsoid);
newPositions[i] = p.x;
newPositions[i + 1] = p.y;
newPositions[i + 2] = p.z;
}
}
return newPositions;
};
return PolylinePipeline;
});