diff --git a/Apps/Sandcastle/gallery/Partial Ellipsoids.html b/Apps/Sandcastle/gallery/Partial Ellipsoids.html
new file mode 100644
index 000000000000..a837d7d264d8
--- /dev/null
+++ b/Apps/Sandcastle/gallery/Partial Ellipsoids.html
@@ -0,0 +1,208 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+Loading...
+
+
+
+
diff --git a/Apps/Sandcastle/gallery/Partial Ellipsoids.jpg b/Apps/Sandcastle/gallery/Partial Ellipsoids.jpg
new file mode 100644
index 000000000000..1a97edf61d33
Binary files /dev/null and b/Apps/Sandcastle/gallery/Partial Ellipsoids.jpg differ
diff --git a/Apps/Sandcastle/gallery/Spheres and Ellipsoids.html b/Apps/Sandcastle/gallery/Spheres and Ellipsoids.html
index 8d8e30bbd3b4..fe2f0983fd83 100644
--- a/Apps/Sandcastle/gallery/Spheres and Ellipsoids.html
+++ b/Apps/Sandcastle/gallery/Spheres and Ellipsoids.html
@@ -10,10 +10,12 @@
diff --git a/CHANGES.md b/CHANGES.md
index 9752a2113cd4..19c0bca7ddfd 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,6 +3,9 @@ Change Log
### 1.62 - 2019-10-01
+##### Additions :tada:
+* Added ability to create partial ellipsoids using both the Entity API and CZML. New ellipsoid geometry properties: `innerRadii`, `minimumClock`, `maximumClock`, `minimumCone`, and `maximumCone`. This affects both `EllipsoidGeometry` and `EllipsoidOutlineGeometry`. See the updated [Sandcastle example](https://cesiumjs.org/Cesium/Apps/Sandcastle/?src=Partial%20Ellipsoids.html&label=Geometries). [#5995](https://github.com/AnalyticalGraphicsInc/cesium/pull/5995)
+
##### Fixes :wrench:
* `Camera.flyTo` flies to the correct location in 2D when the destination crosses the international date line [#7909](https://github.com/AnalyticalGraphicsInc/cesium/pull/7909)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 7c0a7eb5ce32..c8453a6257f3 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -136,6 +136,8 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
* [Brandon Barker](https://github.com/ProjectBarks)
* [Peter Gagliardi](https://github.com/ptrgags)
* [Ian Lilley](https://github.com/IanLilleyT)
+* [Northrop Grumman](http://www.northropgrumman.com)
+ * [Joseph Stein](https://github.com/nahgrin)
## [Individual CLA](Documentation/Contributors/CLAs/individual-contributor-license-agreement-v1.0.pdf)
* [Victor Berchet](https://github.com/vicb)
@@ -205,6 +207,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
* [Cody Guldner](https://github.com/burn123)
* [Nacho Carnicero](https://github.com/nacho-carnicero)
* [Y.Selim Abidin](https://github.com/SelimAbidin)
+* [Steven Trotter](https://github.com/srtrotter)
* [Tamar Cohen](https://github.com/tamarmot)
* [Stephen Wiseman](https://github.com/srwiseman)
* [Gabriel Macario](https://github.com/gabriel-macario)
diff --git a/Source/Core/EllipsoidGeometry.js b/Source/Core/EllipsoidGeometry.js
index 5209f15fa507..b496942864d0 100644
--- a/Source/Core/EllipsoidGeometry.js
+++ b/Source/Core/EllipsoidGeometry.js
@@ -54,6 +54,11 @@ define([
*
* @param {Object} [options] Object with the following properties:
* @param {Cartesian3} [options.radii=Cartesian3(1.0, 1.0, 1.0)] The radii of the ellipsoid in the x, y, and z directions.
+ * @param {Cartesian3} [options.innerRadii=options.radii] The inner radii of the ellipsoid in the x, y, and z directions.
+ * @param {Number} [options.minimumClock=0.0] The minimum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis.
+ * @param {Number} [options.maximumClock=2*PI] The maximum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis.
+ * @param {Number} [options.minimumCone=0.0] The minimum angle measured from the positive z-axis and toward the negative z-axis.
+ * @param {Number} [options.maximumCone=PI] The maximum angle measured from the positive z-axis and toward the negative z-axis.
* @param {Number} [options.stackPartitions=64] The number of times to partition the ellipsoid into stacks.
* @param {Number} [options.slicePartitions=64] The number of times to partition the ellipsoid into radial slices.
* @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
@@ -74,23 +79,30 @@ define([
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
var radii = defaultValue(options.radii, defaultRadii);
+ var innerRadii = defaultValue(options.innerRadii, radii);
+ var minimumClock = defaultValue(options.minimumClock, 0.0);
+ var maximumClock = defaultValue(options.maximumClock, CesiumMath.TWO_PI);
+ var minimumCone = defaultValue(options.minimumCone, 0.0);
+ var maximumCone = defaultValue(options.maximumCone, CesiumMath.PI);
var stackPartitions = Math.round(defaultValue(options.stackPartitions, 64));
var slicePartitions = Math.round(defaultValue(options.slicePartitions, 64));
var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
//>>includeStart('debug', pragmas.debug);
if (slicePartitions < 3) {
- throw new DeveloperError ('options.slicePartitions cannot be less than three.');
+ throw new DeveloperError('options.slicePartitions cannot be less than three.');
}
if (stackPartitions < 3) {
throw new DeveloperError('options.stackPartitions cannot be less than three.');
}
- if (defined(options.offsetAttribute) && options.offsetAttribute === GeometryOffsetAttribute.TOP) {
- throw new DeveloperError('GeometryOffsetAttribute.TOP is not a supported options.offsetAttribute for this geometry.');
- }
//>>includeEnd('debug');
this._radii = Cartesian3.clone(radii);
+ this._innerRadii = Cartesian3.clone(innerRadii);
+ this._minimumClock = minimumClock;
+ this._maximumClock = maximumClock;
+ this._minimumCone = minimumCone;
+ this._maximumCone = maximumCone;
this._stackPartitions = stackPartitions;
this._slicePartitions = slicePartitions;
this._vertexFormat = VertexFormat.clone(vertexFormat);
@@ -102,7 +114,7 @@ define([
* The number of elements used to pack the object into an array.
* @type {Number}
*/
- EllipsoidGeometry.packedLength = Cartesian3.packedLength + VertexFormat.packedLength + 3;
+ EllipsoidGeometry.packedLength = 2 * (Cartesian3.packedLength) + VertexFormat.packedLength + 7;
/**
* Stores the provided instance into the provided array.
@@ -128,9 +140,16 @@ define([
Cartesian3.pack(value._radii, array, startingIndex);
startingIndex += Cartesian3.packedLength;
+ Cartesian3.pack(value._innerRadii, array, startingIndex);
+ startingIndex += Cartesian3.packedLength;
+
VertexFormat.pack(value._vertexFormat, array, startingIndex);
startingIndex += VertexFormat.packedLength;
+ array[startingIndex++] = value._minimumClock;
+ array[startingIndex++] = value._maximumClock;
+ array[startingIndex++] = value._minimumCone;
+ array[startingIndex++] = value._maximumCone;
array[startingIndex++] = value._stackPartitions;
array[startingIndex++] = value._slicePartitions;
array[startingIndex] = defaultValue(value._offsetAttribute, -1);
@@ -139,10 +158,16 @@ define([
};
var scratchRadii = new Cartesian3();
+ var scratchInnerRadii = new Cartesian3();
var scratchVertexFormat = new VertexFormat();
var scratchOptions = {
radii : scratchRadii,
+ innerRadii : scratchInnerRadii,
vertexFormat : scratchVertexFormat,
+ minimumClock : undefined,
+ maximumClock : undefined,
+ minimumCone : undefined,
+ maximumCone : undefined,
stackPartitions : undefined,
slicePartitions : undefined,
offsetAttribute : undefined
@@ -168,14 +193,25 @@ define([
var radii = Cartesian3.unpack(array, startingIndex, scratchRadii);
startingIndex += Cartesian3.packedLength;
+ var innerRadii = Cartesian3.unpack(array, startingIndex, scratchInnerRadii);
+ startingIndex += Cartesian3.packedLength;
+
var vertexFormat = VertexFormat.unpack(array, startingIndex, scratchVertexFormat);
startingIndex += VertexFormat.packedLength;
+ var minimumClock = array[startingIndex++];
+ var maximumClock = array[startingIndex++];
+ var minimumCone = array[startingIndex++];
+ var maximumCone = array[startingIndex++];
var stackPartitions = array[startingIndex++];
var slicePartitions = array[startingIndex++];
var offsetAttribute = array[startingIndex];
if (!defined(result)) {
+ scratchOptions.minimumClock = minimumClock;
+ scratchOptions.maximumClock = maximumClock;
+ scratchOptions.minimumCone = minimumCone;
+ scratchOptions.maximumCone = maximumCone;
scratchOptions.stackPartitions = stackPartitions;
scratchOptions.slicePartitions = slicePartitions;
scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute;
@@ -183,7 +219,12 @@ define([
}
result._radii = Cartesian3.clone(radii, result._radii);
+ result._innerRadii = Cartesian3.clone(innerRadii, result._innerRadii);
result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat);
+ result._minimumClock = minimumClock;
+ result._maximumClock = maximumClock;
+ result._minimumCone = minimumCone;
+ result._maximumCone = maximumCone;
result._stackPartitions = stackPartitions;
result._slicePartitions = slicePartitions;
result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute;
@@ -199,71 +240,237 @@ define([
*/
EllipsoidGeometry.createGeometry = function(ellipsoidGeometry) {
var radii = ellipsoidGeometry._radii;
-
if ((radii.x <= 0) || (radii.y <= 0) || (radii.z <= 0)) {
return;
}
- var ellipsoid = Ellipsoid.fromCartesian3(radii);
+ var innerRadii = ellipsoidGeometry._innerRadii;
+ if ((innerRadii.x <= 0) || (innerRadii.y <= 0) || innerRadii.z <= 0) {
+ return;
+ }
+
+ var minimumClock = ellipsoidGeometry._minimumClock;
+ var maximumClock = ellipsoidGeometry._maximumClock;
+ var minimumCone = ellipsoidGeometry._minimumCone;
+ var maximumCone = ellipsoidGeometry._maximumCone;
var vertexFormat = ellipsoidGeometry._vertexFormat;
- // The extra slice and stack are for duplicating points at the x axis and poles.
- // We need the texture coordinates to interpolate from (2 * pi - delta) to 2 * pi instead of
- // (2 * pi - delta) to 0.
+ // Add an extra slice and stack so that the number of partitions is the
+ // number of surfaces rather than the number of joints
var slicePartitions = ellipsoidGeometry._slicePartitions + 1;
var stackPartitions = ellipsoidGeometry._stackPartitions + 1;
- var vertexCount = stackPartitions * slicePartitions;
+ slicePartitions = Math.round(slicePartitions * Math.abs(maximumClock - minimumClock) / CesiumMath.TWO_PI);
+ stackPartitions = Math.round(stackPartitions * Math.abs(maximumCone - minimumCone) / CesiumMath.PI);
+
+ if (slicePartitions < 2) {
+ slicePartitions = 2;
+ }
+ if (stackPartitions < 2) {
+ stackPartitions = 2;
+ }
+
+ var i;
+ var j;
+ var index = 0;
+
+ // Create arrays for theta and phi. Duplicate first and last angle to
+ // allow different normals at the intersections.
+ var phis = [minimumCone];
+ var thetas = [minimumClock];
+ for (i = 0; i < stackPartitions; i++) {
+ phis.push(minimumCone + i * (maximumCone - minimumCone) / (stackPartitions - 1));
+ }
+ phis.push(maximumCone);
+ for (j = 0; j < slicePartitions; j++) {
+ thetas.push(minimumClock + j * (maximumClock - minimumClock) / (slicePartitions - 1));
+ }
+ thetas.push(maximumClock);
+ var numPhis = phis.length;
+ var numThetas = thetas.length;
+
+ // Allow for extra indices if there is an inner surface and if we need
+ // to close the sides if the clock range is not a full circle
+ var extraIndices = 0;
+ var vertexMultiplier = 1.0;
+ var hasInnerSurface = ((innerRadii.x !== radii.x) || (innerRadii.y !== radii.y) || innerRadii.z !== radii.z);
+ var isTopOpen = false;
+ var isBotOpen = false;
+ var isClockOpen = false;
+ if (hasInnerSurface) {
+ vertexMultiplier = 2.0;
+ if (minimumCone > 0.0) {
+ isTopOpen = true;
+ extraIndices += (slicePartitions - 1);
+ }
+ if (maximumCone < Math.PI) {
+ isBotOpen = true;
+ extraIndices += (slicePartitions - 1);
+ }
+ if ((maximumClock - minimumClock) % CesiumMath.TWO_PI) {
+ isClockOpen = true;
+ extraIndices += ((stackPartitions - 1) * 2) + 1;
+ } else {
+ extraIndices += 1;
+ }
+ }
+
+ var vertexCount = numThetas * numPhis * vertexMultiplier;
var positions = new Float64Array(vertexCount * 3);
+ var isInner = new Array(vertexCount).fill(false);
+ var negateNormal = new Array(vertexCount).fill(false);
- var numIndices = 6 * (slicePartitions - 1) * (stackPartitions - 2);
- var indices = IndexDatatype.createTypedArray(vertexCount, numIndices);
+ // Multiply by 6 because there are two triangles per sector
+ var indexCount = slicePartitions * stackPartitions * vertexMultiplier;
+ var numIndices = 6 * (indexCount + extraIndices + 1 - (slicePartitions + stackPartitions) * vertexMultiplier);
+ var indices = IndexDatatype.createTypedArray(indexCount, numIndices);
var normals = (vertexFormat.normal) ? new Float32Array(vertexCount * 3) : undefined;
var tangents = (vertexFormat.tangent) ? new Float32Array(vertexCount * 3) : undefined;
var bitangents = (vertexFormat.bitangent) ? new Float32Array(vertexCount * 3) : undefined;
var st = (vertexFormat.st) ? new Float32Array(vertexCount * 2) : undefined;
- var cosTheta = new Array(slicePartitions);
- var sinTheta = new Array(slicePartitions);
+ // Calculate sin/cos phi
+ var sinPhi = new Array(numPhis);
+ var cosPhi = new Array(numPhis);
+ for (i = 0; i < numPhis; i++) {
+ sinPhi[i] = sin(phis[i]);
+ cosPhi[i] = cos(phis[i]);
+ }
- var i;
- var j;
- var index = 0;
+ // Calculate sin/cos theta
+ var sinTheta = new Array(numThetas);
+ var cosTheta = new Array(numThetas);
+ for (j = 0; j < numThetas; j++) {
+ cosTheta[j] = cos(thetas[j]);
+ sinTheta[j] = sin(thetas[j]);
+ }
+
+ // Create outer surface
+ for (i = 0; i < numPhis; i++) {
+ for (j = 0; j < numThetas; j++) {
+ positions[index++] = radii.x * sinPhi[i] * cosTheta[j];
+ positions[index++] = radii.y * sinPhi[i] * sinTheta[j];
+ positions[index++] = radii.z * cosPhi[i];
+ }
+ }
- for (i = 0; i < slicePartitions; i++) {
- var theta = CesiumMath.TWO_PI * i / (slicePartitions - 1);
- cosTheta[i] = cos(theta);
- sinTheta[i] = sin(theta);
+ // Create inner surface
+ var vertexIndex = vertexCount / 2.0;
+ if (hasInnerSurface) {
+ for (i = 0; i < numPhis; i++) {
+ for (j = 0; j < numThetas; j++) {
+ positions[index++] = innerRadii.x * sinPhi[i] * cosTheta[j];
+ positions[index++] = innerRadii.y * sinPhi[i] * sinTheta[j];
+ positions[index++] = innerRadii.z * cosPhi[i];
+
+ // Keep track of which vertices are the inner and which ones
+ // need the normal to be negated
+ isInner[vertexIndex] = true;
+ if (i > 0 && i !== (numPhis - 1) && j !== 0 && j !== (numThetas - 1)) {
+ negateNormal[vertexIndex] = true;
+ }
+ vertexIndex++;
+ }
+ }
+ }
+
+ // Create indices for outer surface
+ index = 0;
+ var topOffset;
+ var bottomOffset;
+ for (i = 1; i < (numPhis - 2); i++) {
+ topOffset = i * numThetas;
+ bottomOffset = (i + 1) * numThetas;
+
+ for (j = 1; j < numThetas - 2; j++) {
+ indices[index++] = bottomOffset + j;
+ indices[index++] = bottomOffset + j + 1;
+ indices[index++] = topOffset + j + 1;
- // duplicate first point for correct
- // texture coordinates at the north pole.
- positions[index++] = 0.0;
- positions[index++] = 0.0;
- positions[index++] = radii.z;
+ indices[index++] = bottomOffset + j;
+ indices[index++] = topOffset + j + 1;
+ indices[index++] = topOffset + j;
+ }
}
- for (i = 1; i < stackPartitions - 1; i++) {
- var phi = Math.PI * i / (stackPartitions - 1);
- var sinPhi = sin(phi);
+ // Create indices for inner surface
+ if (hasInnerSurface) {
+ var offset = numPhis * numThetas;
+ for (i = 1; i < (numPhis - 2); i++) {
+ topOffset = offset + i * numThetas;
+ bottomOffset = offset + (i + 1) * numThetas;
+
+ for (j = 1; j < numThetas - 2; j++) {
+ indices[index++] = bottomOffset + j;
+ indices[index++] = topOffset + j;
+ indices[index++] = topOffset + j + 1;
+
+ indices[index++] = bottomOffset + j;
+ indices[index++] = topOffset + j + 1;
+ indices[index++] = bottomOffset + j + 1;
+ }
+ }
+ }
- var xSinPhi = radii.x * sinPhi;
- var ySinPhi = radii.y * sinPhi;
- var zCosPhi = radii.z * cos(phi);
+ var outerOffset;
+ var innerOffset;
+ if (hasInnerSurface) {
+ if (isTopOpen) {
+ // Connect the top of the inner surface to the top of the outer surface
+ innerOffset = numPhis * numThetas;
+ for (i = 1; i < numThetas - 2; i++) {
+ indices[index++] = i;
+ indices[index++] = i + 1;
+ indices[index++] = innerOffset + i + 1;
+
+ indices[index++] = i;
+ indices[index++] = innerOffset + i + 1;
+ indices[index++] = innerOffset + i;
+ }
+ }
- for (j = 0; j < slicePartitions; j++) {
- positions[index++] = cosTheta[j] * xSinPhi;
- positions[index++] = sinTheta[j] * ySinPhi;
- positions[index++] = zCosPhi;
+ if (isBotOpen) {
+ // Connect the bottom of the inner surface to the bottom of the outer surface
+ outerOffset = numPhis * numThetas - numThetas;
+ innerOffset = numPhis * numThetas * vertexMultiplier - numThetas;
+ for (i = 1; i < numThetas - 2; i++) {
+ indices[index++] = outerOffset + i + 1;
+ indices[index++] = outerOffset + i;
+ indices[index++] = innerOffset + i;
+
+ indices[index++] = outerOffset + i + 1;
+ indices[index++] = innerOffset + i;
+ indices[index++] = innerOffset + i + 1;
+ }
}
}
- for (i = 0; i < slicePartitions; i++) {
- // duplicate first point for correct
- // texture coordinates at the south pole.
- positions[index++] = 0.0;
- positions[index++] = 0.0;
- positions[index++] = -radii.z;
+ // Connect the edges if clock is not closed
+ if (isClockOpen) {
+ for (i = 1; i < numPhis - 2; i++) {
+ innerOffset = numThetas * numPhis + (numThetas * i);
+ outerOffset = numThetas * i;
+ indices[index++] = innerOffset;
+ indices[index++] = outerOffset + numThetas;
+ indices[index++] = outerOffset;
+
+ indices[index++] = innerOffset;
+ indices[index++] = innerOffset + numThetas;
+ indices[index++] = outerOffset + numThetas;
+ }
+
+ for (i = 1; i < numPhis - 2; i++) {
+ innerOffset = numThetas * numPhis + (numThetas * (i + 1)) - 1;
+ outerOffset = numThetas * (i + 1) - 1;
+ indices[index++] = outerOffset + numThetas;
+ indices[index++] = innerOffset;
+ indices[index++] = outerOffset;
+
+ indices[index++] = outerOffset + numThetas;
+ indices[index++] = innerOffset + numThetas;
+ indices[index++] = innerOffset;
+ }
}
var attributes = new GeometryAttributes();
@@ -280,27 +487,23 @@ define([
var normalIndex = 0;
var tangentIndex = 0;
var bitangentIndex = 0;
+ var vertexCountHalf = vertexCount / 2.0;
+
+ var ellipsoid;
+ var ellipsoidOuter = Ellipsoid.fromCartesian3(radii);
+ var ellipsoidInner = Ellipsoid.fromCartesian3(innerRadii);
if (vertexFormat.st || vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent) {
- for( i = 0; i < vertexCount; i++) {
+ for (i = 0; i < vertexCount; i++) {
+ ellipsoid = (isInner[i]) ? ellipsoidInner : ellipsoidOuter;
var position = Cartesian3.fromArray(positions, i * 3, scratchPosition);
var normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal);
+ if (negateNormal[i]) {
+ Cartesian3.negate(normal, normal);
+ }
if (vertexFormat.st) {
var normalST = Cartesian2.negate(normal, scratchNormalST);
-
- // if the point is at or close to the pole, find a point along the same longitude
- // close to the xy-plane for the s coordinate.
- if (Cartesian2.magnitude(normalST) < CesiumMath.EPSILON6) {
- index = (i + slicePartitions * Math.floor(stackPartitions * 0.5)) * 3;
- if (index > positions.length) {
- index = (i - slicePartitions * Math.floor(stackPartitions * 0.5)) * 3;
- }
- Cartesian3.fromArray(positions, index, normalST);
- ellipsoid.geodeticSurfaceNormal(normalST, normalST);
- Cartesian2.negate(normalST, normalST);
- }
-
st[stIndex++] = (Math.atan2(normalST.y, normalST.x) / CesiumMath.TWO_PI) + 0.5;
st[stIndex++] = (Math.asin(normal.z) / Math.PI) + 0.5;
}
@@ -313,13 +516,20 @@ define([
if (vertexFormat.tangent || vertexFormat.bitangent) {
var tangent = scratchTangent;
- if (i < slicePartitions || i > vertexCount - slicePartitions - 1) {
- Cartesian3.cross(Cartesian3.UNIT_X, normal, tangent);
- Cartesian3.normalize(tangent, tangent);
+
+ // Use UNIT_X for the poles
+ var tangetOffset = 0;
+ var unit;
+ if (isInner[i]) {
+ tangetOffset = vertexCountHalf;
+ }
+ if ((!isTopOpen && (i >= tangetOffset && i < (tangetOffset + numThetas * 2)))) {
+ unit = Cartesian3.UNIT_X;
} else {
- Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent);
- Cartesian3.normalize(tangent, tangent);
+ unit = Cartesian3.UNIT_Z;
}
+ Cartesian3.cross(unit, normal, tangent);
+ Cartesian3.normalize(tangent, tangent);
if (vertexFormat.tangent) {
tangents[tangentIndex++] = tangent.x;
@@ -379,49 +589,15 @@ define([
attributes.applyOffset = new GeometryAttribute({
componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute : 1,
- values: applyOffset
+ values : applyOffset
});
}
- index = 0;
- for (j = 0; j < slicePartitions - 1; j++) {
- indices[index++] = slicePartitions + j;
- indices[index++] = slicePartitions + j + 1;
- indices[index++] = j + 1;
- }
-
- var topOffset;
- var bottomOffset;
- for (i = 1; i < stackPartitions - 2; i++) {
- topOffset = i * slicePartitions;
- bottomOffset = (i + 1) * slicePartitions;
-
- for (j = 0; j < slicePartitions - 1; j++) {
- indices[index++] = bottomOffset + j;
- indices[index++] = bottomOffset + j + 1;
- indices[index++] = topOffset + j + 1;
-
- indices[index++] = bottomOffset + j;
- indices[index++] = topOffset + j + 1;
- indices[index++] = topOffset + j;
- }
- }
-
- i = stackPartitions - 2;
- topOffset = i * slicePartitions;
- bottomOffset = (i + 1) * slicePartitions;
-
- for (j = 0; j < slicePartitions - 1; j++) {
- indices[index++] = bottomOffset + j;
- indices[index++] = topOffset + j + 1;
- indices[index++] = topOffset + j;
- }
-
return new Geometry({
attributes : attributes,
indices : indices,
primitiveType : PrimitiveType.TRIANGLES,
- boundingSphere : BoundingSphere.fromEllipsoid(ellipsoid),
+ boundingSphere : BoundingSphere.fromEllipsoid(ellipsoidOuter),
offsetAttribute : ellipsoidGeometry._offsetAttribute
});
};
diff --git a/Source/Core/EllipsoidOutlineGeometry.js b/Source/Core/EllipsoidOutlineGeometry.js
index bd064ba684af..af9df76e26e7 100644
--- a/Source/Core/EllipsoidOutlineGeometry.js
+++ b/Source/Core/EllipsoidOutlineGeometry.js
@@ -44,6 +44,11 @@ define([
*
* @param {Object} [options] Object with the following properties:
* @param {Cartesian3} [options.radii=Cartesian3(1.0, 1.0, 1.0)] The radii of the ellipsoid in the x, y, and z directions.
+ * @param {Cartesian3} [options.innerRadii=options.radii] The inner radii of the ellipsoid in the x, y, and z directions.
+ * @param {Number} [options.minimumClock=0.0] The minimum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis.
+ * @param {Number} [options.maximumClock=2*PI] The maximum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis.
+ * @param {Number} [options.minimumCone=0.0] The minimum angle measured from the positive z-axis and toward the negative z-axis.
+ * @param {Number} [options.maximumCone=PI] The maximum angle measured from the positive z-axis and toward the negative z-axis.
* @param {Number} [options.stackPartitions=10] The count of stacks for the ellipsoid (1 greater than the number of parallel lines).
* @param {Number} [options.slicePartitions=8] The count of slices for the ellipsoid (Equal to the number of radial lines).
* @param {Number} [options.subdivisions=128] The number of points per line, determining the granularity of the curvature.
@@ -64,6 +69,11 @@ define([
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
var radii = defaultValue(options.radii, defaultRadii);
+ var innerRadii = defaultValue(options.innerRadii, radii);
+ var minimumClock = defaultValue(options.minimumClock, 0.0);
+ var maximumClock = defaultValue(options.maximumClock, CesiumMath.TWO_PI);
+ var minimumCone = defaultValue(options.minimumCone, 0.0);
+ var maximumCone = defaultValue(options.maximumCone, CesiumMath.PI);
var stackPartitions = Math.round(defaultValue(options.stackPartitions, 10));
var slicePartitions = Math.round(defaultValue(options.slicePartitions, 8));
var subdivisions = Math.round(defaultValue(options.subdivisions, 128));
@@ -84,6 +94,11 @@ define([
//>>includeEnd('debug');
this._radii = Cartesian3.clone(radii);
+ this._innerRadii = Cartesian3.clone(innerRadii);
+ this._minimumClock = minimumClock;
+ this._maximumClock = maximumClock;
+ this._minimumCone = minimumCone;
+ this._maximumCone = maximumCone;
this._stackPartitions = stackPartitions;
this._slicePartitions = slicePartitions;
this._subdivisions = subdivisions;
@@ -95,7 +110,7 @@ define([
* The number of elements used to pack the object into an array.
* @type {Number}
*/
- EllipsoidOutlineGeometry.packedLength = Cartesian3.packedLength + 4;
+ EllipsoidOutlineGeometry.packedLength = 2 * (Cartesian3.packedLength) + 8;
/**
* Stores the provided instance into the provided array.
@@ -121,6 +136,13 @@ define([
Cartesian3.pack(value._radii, array, startingIndex);
startingIndex += Cartesian3.packedLength;
+ Cartesian3.pack(value._innerRadii, array, startingIndex);
+ startingIndex += Cartesian3.packedLength;
+
+ array[startingIndex++] = value._minimumClock;
+ array[startingIndex++] = value._maximumClock;
+ array[startingIndex++] = value._minimumCone;
+ array[startingIndex++] = value._maximumCone;
array[startingIndex++] = value._stackPartitions;
array[startingIndex++] = value._slicePartitions;
array[startingIndex++] = value._subdivisions;
@@ -130,8 +152,14 @@ define([
};
var scratchRadii = new Cartesian3();
+ var scratchInnerRadii = new Cartesian3();
var scratchOptions = {
radii : scratchRadii,
+ innerRadii : scratchInnerRadii,
+ minimumClock : undefined,
+ maximumClock : undefined,
+ minimumCone : undefined,
+ maximumCone : undefined,
stackPartitions : undefined,
slicePartitions : undefined,
subdivisions : undefined,
@@ -158,12 +186,23 @@ define([
var radii = Cartesian3.unpack(array, startingIndex, scratchRadii);
startingIndex += Cartesian3.packedLength;
+ var innerRadii = Cartesian3.unpack(array, startingIndex, scratchInnerRadii);
+ startingIndex += Cartesian3.packedLength;
+
+ var minimumClock = array[startingIndex++];
+ var maximumClock = array[startingIndex++];
+ var minimumCone = array[startingIndex++];
+ var maximumCone = array[startingIndex++];
var stackPartitions = array[startingIndex++];
var slicePartitions = array[startingIndex++];
var subdivisions = array[startingIndex++];
var offsetAttribute = array[startingIndex];
if (!defined(result)) {
+ scratchOptions.minimumClock = minimumClock;
+ scratchOptions.maximumClock = maximumClock;
+ scratchOptions.minimumCone = minimumCone;
+ scratchOptions.maximumCone = maximumCone;
scratchOptions.stackPartitions = stackPartitions;
scratchOptions.slicePartitions = slicePartitions;
scratchOptions.subdivisions = subdivisions;
@@ -172,6 +211,11 @@ define([
}
result._radii = Cartesian3.clone(radii, result._radii);
+ result._innerRadii = Cartesian3.clone(innerRadii, result._innerRadii);
+ result._minimumClock = minimumClock;
+ result._maximumClock = maximumClock;
+ result._minimumCone = minimumCone;
+ result._maximumCone = maximumCone;
result._stackPartitions = stackPartitions;
result._slicePartitions = slicePartitions;
result._subdivisions = subdivisions;
@@ -188,116 +232,198 @@ define([
*/
EllipsoidOutlineGeometry.createGeometry = function(ellipsoidGeometry) {
var radii = ellipsoidGeometry._radii;
-
if ((radii.x <= 0) || (radii.y <= 0) || (radii.z <= 0)) {
return;
}
- var ellipsoid = Ellipsoid.fromCartesian3(radii);
- var stackPartitions = ellipsoidGeometry._stackPartitions;
- var slicePartitions = ellipsoidGeometry._slicePartitions;
+ var innerRadii = ellipsoidGeometry._innerRadii;
+ if ((innerRadii.x <= 0) || (innerRadii.y <= 0) || (innerRadii.z <= 0)) {
+ return;
+ }
+
+ var minimumClock = ellipsoidGeometry._minimumClock;
+ var maximumClock = ellipsoidGeometry._maximumClock;
+ var minimumCone = ellipsoidGeometry._minimumCone;
+ var maximumCone = ellipsoidGeometry._maximumCone;
var subdivisions = ellipsoidGeometry._subdivisions;
+ var ellipsoid = Ellipsoid.fromCartesian3(radii);
+
+ // Add an extra slice and stack to remain consistent with EllipsoidGeometry
+ var slicePartitions = ellipsoidGeometry._slicePartitions + 1;
+ var stackPartitions = ellipsoidGeometry._stackPartitions + 1;
+
+ slicePartitions = Math.round(slicePartitions * Math.abs(maximumClock - minimumClock) / CesiumMath.TWO_PI);
+ stackPartitions = Math.round(stackPartitions * Math.abs(maximumCone - minimumCone) / CesiumMath.PI);
+
+ if (slicePartitions < 2) {
+ slicePartitions = 2;
+ }
+ if (stackPartitions < 2) {
+ stackPartitions = 2;
+ }
+
+ var extraIndices = 0;
+ var vertexMultiplier = 1.0;
+ var hasInnerSurface = ((innerRadii.x !== radii.x) || (innerRadii.y !== radii.y) || innerRadii.z !== radii.z);
+ var isTopOpen = false;
+ var isBotOpen = false;
+ if (hasInnerSurface) {
+ vertexMultiplier = 2.0;
+ // Add 2x slicePartitions to connect the top/bottom of the outer to
+ // the top/bottom of the inner
+ if (minimumCone > 0.0) {
+ isTopOpen = true;
+ extraIndices += slicePartitions;
+ }
+ if (maximumCone < Math.PI) {
+ isBotOpen = true;
+ extraIndices += slicePartitions;
+ }
+ }
+
+ var vertexCount = subdivisions * vertexMultiplier * (stackPartitions + slicePartitions);
+ var positions = new Float64Array(vertexCount * 3);
- var indicesSize = subdivisions * (stackPartitions + slicePartitions - 1);
- var positionSize = indicesSize - slicePartitions + 2;
- var positions = new Float64Array(positionSize * 3);
- var indices = IndexDatatype.createTypedArray(positionSize, indicesSize * 2);
+ // Multiply by two because two points define each line segment
+ var numIndices = 2 * (vertexCount + extraIndices - (slicePartitions + stackPartitions) * vertexMultiplier);
+ var indices = IndexDatatype.createTypedArray(vertexCount, numIndices);
var i;
var j;
var theta;
var phi;
- var cosPhi;
- var sinPhi;
var index = 0;
- var cosTheta = new Array(subdivisions);
+ // Calculate sin/cos phi
+ var sinPhi = new Array(stackPartitions);
+ var cosPhi = new Array(stackPartitions);
+ for (i = 0; i < stackPartitions; i++) {
+ phi = minimumCone + i * (maximumCone - minimumCone) / (stackPartitions - 1);
+ sinPhi[i] = sin(phi);
+ cosPhi[i] = cos(phi);
+ }
+
+ // Calculate sin/cos theta
var sinTheta = new Array(subdivisions);
+ var cosTheta = new Array(subdivisions);
for (i = 0; i < subdivisions; i++) {
- theta = CesiumMath.TWO_PI * i / subdivisions;
- cosTheta[i] = cos(theta);
+ theta = minimumClock + i * (maximumClock - minimumClock) / (subdivisions - 1);
sinTheta[i] = sin(theta);
+ cosTheta[i] = cos(theta);
}
- for (i = 1; i < stackPartitions; i++) {
- phi = Math.PI * i / stackPartitions;
- cosPhi = cos(phi);
- sinPhi = sin(phi);
-
+ // Calculate the latitude lines on the outer surface
+ for (i = 0; i < stackPartitions; i++) {
for (j = 0; j < subdivisions; j++) {
- positions[index++] = radii.x * cosTheta[j] * sinPhi;
- positions[index++] = radii.y * sinTheta[j] * sinPhi;
- positions[index++] = radii.z * cosPhi;
+ positions[index++] = radii.x * sinPhi[i] * cosTheta[j];
+ positions[index++] = radii.y * sinPhi[i] * sinTheta[j];
+ positions[index++] = radii.z * cosPhi[i];
}
}
- cosTheta.length = slicePartitions;
+ // Calculate the latitude lines on the inner surface
+ if (hasInnerSurface) {
+ for (i = 0; i < stackPartitions; i++) {
+ for (j = 0; j < subdivisions; j++) {
+ positions[index++] = innerRadii.x * sinPhi[i] * cosTheta[j];
+ positions[index++] = innerRadii.y * sinPhi[i] * sinTheta[j];
+ positions[index++] = innerRadii.z * cosPhi[i];
+ }
+ }
+ }
+
+ // Calculate sin/cos phi
+ sinPhi.length = subdivisions;
+ cosPhi.length = subdivisions;
+ for (i = 0; i < subdivisions; i++) {
+ phi = minimumCone + i * (maximumCone - minimumCone) / (subdivisions - 1);
+ sinPhi[i] = sin(phi);
+ cosPhi[i] = cos(phi);
+ }
+
+ // Calculate sin/cos theta for each slice partition
sinTheta.length = slicePartitions;
+ cosTheta.length = slicePartitions;
for (i = 0; i < slicePartitions; i++) {
- theta = CesiumMath.TWO_PI * i / slicePartitions;
- cosTheta[i] = cos(theta);
+ theta = minimumClock + i * (maximumClock - minimumClock) / (slicePartitions - 1);
sinTheta[i] = sin(theta);
+ cosTheta[i] = cos(theta);
}
- positions[index++] = 0;
- positions[index++] = 0;
- positions[index++] = radii.z;
-
- for (i = 1; i < subdivisions; i++) {
- phi = Math.PI * i / subdivisions;
- cosPhi = cos(phi);
- sinPhi = sin(phi);
-
+ // Calculate the longitude lines on the outer surface
+ for (i = 0; i < subdivisions; i++) {
for (j = 0; j < slicePartitions; j++) {
- positions[index++] = radii.x * cosTheta[j] * sinPhi;
- positions[index++] = radii.y * sinTheta[j] * sinPhi;
- positions[index++] = radii.z * cosPhi;
+ positions[index++] = radii.x * sinPhi[i] * cosTheta[j];
+ positions[index++] = radii.y * sinPhi[i] * sinTheta[j];
+ positions[index++] = radii.z * cosPhi[i];
}
}
- positions[index++] = 0;
- positions[index++] = 0;
- positions[index++] = -radii.z;
+ // Calculate the longitude lines on the inner surface
+ if (hasInnerSurface) {
+ for (i = 0; i < subdivisions; i++) {
+ for (j = 0; j < slicePartitions; j++) {
+ positions[index++] = innerRadii.x * sinPhi[i] * cosTheta[j];
+ positions[index++] = innerRadii.y * sinPhi[i] * sinTheta[j];
+ positions[index++] = innerRadii.z * cosPhi[i];
+ }
+ }
+ }
+ // Create indices for the latitude lines
index = 0;
- for (i = 0; i < stackPartitions - 1; ++i) {
- var topRowOffset = (i * subdivisions);
- for (j = 0; j < subdivisions - 1; ++j) {
- indices[index++] = topRowOffset + j;
- indices[index++] = topRowOffset + j + 1;
+ for (i = 0; i < stackPartitions * vertexMultiplier; i++) {
+ var topOffset = i * subdivisions;
+ for (j = 0; j < subdivisions - 1; j++) {
+ indices[index++] = topOffset + j;
+ indices[index++] = topOffset + j + 1;
}
-
- indices[index++] = topRowOffset + subdivisions - 1;
- indices[index++] = topRowOffset;
}
- var sliceOffset = subdivisions * (stackPartitions - 1);
- for (j = 1; j < slicePartitions + 1; ++j) {
- indices[index++] = sliceOffset;
- indices[index++] = sliceOffset + j;
+ // Create indices for the outer longitude lines
+ var offset = stackPartitions * subdivisions * vertexMultiplier;
+ for (i = 0; i < slicePartitions; i++) {
+ for (j = 0; j < subdivisions - 1; j++) {
+ indices[index++] = offset + i + (j * slicePartitions);
+ indices[index++] = offset + i + (j + 1) * slicePartitions;
+ }
}
- for (i = 0; i < subdivisions - 2; ++i) {
- var topOffset = (i * slicePartitions) + 1 + sliceOffset;
- var bottomOffset = ((i + 1) * slicePartitions) + 1 + sliceOffset;
-
- for (j = 0; j < slicePartitions - 1; ++j) {
- indices[index++] = bottomOffset + j;
- indices[index++] = topOffset + j;
+ // Create indices for the inner longitude lines
+ if (hasInnerSurface) {
+ offset = stackPartitions * subdivisions * vertexMultiplier + slicePartitions * subdivisions;
+ for (i = 0; i < slicePartitions; i++) {
+ for (j = 0; j < subdivisions - 1; j++) {
+ indices[index++] = offset + i + (j * slicePartitions);
+ indices[index++] = offset + i + (j + 1) * slicePartitions;
+ }
}
-
- indices[index++] = bottomOffset + slicePartitions - 1;
- indices[index++] = topOffset + slicePartitions - 1;
}
- var lastPosition = positions.length / 3 - 1;
- for (j = lastPosition - 1; j > lastPosition - slicePartitions - 1; --j) {
- indices[index++] = lastPosition;
- indices[index++] = j;
+ if (hasInnerSurface) {
+ var outerOffset = stackPartitions * subdivisions * vertexMultiplier;
+ var innerOffset = outerOffset + (subdivisions * slicePartitions);
+ if (isTopOpen) {
+ // Draw lines from the top of the inner surface to the top of the outer surface
+ for (i = 0; i < slicePartitions; i++) {
+ indices[index++] = outerOffset + i;
+ indices[index++] = innerOffset + i;
+ }
+ }
+
+ if (isBotOpen) {
+ // Draw lines from the top of the inner surface to the top of the outer surface
+ outerOffset += (subdivisions * slicePartitions) - slicePartitions;
+ innerOffset += (subdivisions * slicePartitions) - slicePartitions;
+ for (i = 0; i < slicePartitions; i++) {
+ indices[index++] = outerOffset + i;
+ indices[index++] = innerOffset + i;
+ }
+ }
}
var attributes = new GeometryAttributes({
- position: new GeometryAttribute({
+ position : new GeometryAttribute({
componentDatatype : ComponentDatatype.DOUBLE,
componentsPerAttribute : 3,
values : positions
@@ -312,7 +438,7 @@ define([
attributes.applyOffset = new GeometryAttribute({
componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute : 1,
- values: applyOffset
+ values : applyOffset
});
}
diff --git a/Source/Core/barycentricCoordinates.js b/Source/Core/barycentricCoordinates.js
index 085223fd2399..06b5f2d59af5 100644
--- a/Source/Core/barycentricCoordinates.js
+++ b/Source/Core/barycentricCoordinates.js
@@ -100,10 +100,18 @@ define([
dot12 = Cartesian3.dot(v1, v2);
}
+ result.y = (dot11 * dot02 - dot01 * dot12);
+ result.z = (dot00 * dot12 - dot01 * dot02);
var q = dot00 * dot11 - dot01 * dot01;
- var invQ = 1.0 / q;
- result.y = (dot11 * dot02 - dot01 * dot12) * invQ;
- result.z = (dot00 * dot12 - dot01 * dot02) * invQ;
+
+ // This is done to avoid dividing by infinity causing a NaN
+ if (result.y !== 0) {
+ result.y /= q;
+ }
+ if (result.z !== 0) {
+ result.z /= q;
+ }
+
result.x = 1.0 - result.y - result.z;
return result;
}
diff --git a/Source/DataSources/CzmlDataSource.js b/Source/DataSources/CzmlDataSource.js
index bb627fe2df09..7562cc37f7ed 100644
--- a/Source/DataSources/CzmlDataSource.js
+++ b/Source/DataSources/CzmlDataSource.js
@@ -1626,6 +1626,11 @@ define([
processPacketData(Boolean, ellipsoid, 'show', ellipsoidData.show, interval, sourceUri, entityCollection);
processPacketData(Cartesian3, ellipsoid, 'radii', ellipsoidData.radii, interval, sourceUri, entityCollection);
+ processPacketData(Cartesian3, ellipsoid, 'innerRadii', ellipsoidData.innerRadii, interval, sourceUri, entityCollection);
+ processPacketData(Number, ellipsoid, 'minimumClock', ellipsoidData.minimumClock, interval, sourceUri, entityCollection);
+ processPacketData(Number, ellipsoid, 'maximumClock', ellipsoidData.maximumClock, interval, sourceUri, entityCollection);
+ processPacketData(Number, ellipsoid, 'minimumCone', ellipsoidData.minimumCone, interval, sourceUri, entityCollection);
+ processPacketData(Number, ellipsoid, 'maximumCone', ellipsoidData.maximumCone, interval, sourceUri, entityCollection);
processPacketData(HeightReference, ellipsoid, 'heightReference', ellipsoidData.heightReference, interval, sourceUri, entityCollection);
processPacketData(Boolean, ellipsoid, 'fill', ellipsoidData.fill, interval, sourceUri, entityCollection);
processMaterialPacketData(ellipsoid, 'material', ellipsoidData.material, interval, sourceUri, entityCollection);
diff --git a/Source/DataSources/EllipsoidGeometryUpdater.js b/Source/DataSources/EllipsoidGeometryUpdater.js
index bb615a74f6d0..0f539f81a19c 100644
--- a/Source/DataSources/EllipsoidGeometryUpdater.js
+++ b/Source/DataSources/EllipsoidGeometryUpdater.js
@@ -70,6 +70,11 @@ define([
this.id = entity;
this.vertexFormat = undefined;
this.radii = undefined;
+ this.innerRadii = undefined;
+ this.minimumClock = undefined;
+ this.maximumClock = undefined;
+ this.minimumCone = undefined;
+ this.maximumCone = undefined;
this.stackPartitions = undefined;
this.slicePartitions = undefined;
this.subdivisions = undefined;
@@ -231,6 +236,11 @@ define([
var options = this._options;
options.vertexFormat = this._materialProperty instanceof ColorMaterialProperty ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat;
options.radii = ellipsoid.radii.getValue(Iso8601.MINIMUM_VALUE, options.radii);
+ options.innerRadii = Property.getValueOrUndefined(ellipsoid.innerRadii, options.radii);
+ options.minimumClock = Property.getValueOrUndefined(ellipsoid.minimumClock, Iso8601.MINIMUM_VALUE);
+ options.maximumClock = Property.getValueOrUndefined(ellipsoid.maximumClock, Iso8601.MINIMUM_VALUE);
+ options.minimumCone = Property.getValueOrUndefined(ellipsoid.minimumCone, Iso8601.MINIMUM_VALUE);
+ options.maximumCone = Property.getValueOrUndefined(ellipsoid.maximumCone, Iso8601.MINIMUM_VALUE);
options.stackPartitions = Property.getValueOrUndefined(ellipsoid.stackPartitions, Iso8601.MINIMUM_VALUE);
options.slicePartitions = Property.getValueOrUndefined(ellipsoid.slicePartitions, Iso8601.MINIMUM_VALUE);
options.subdivisions = Property.getValueOrUndefined(ellipsoid.subdivisions, Iso8601.MINIMUM_VALUE);
@@ -344,6 +354,18 @@ define([
options.subdivisions = subdivisions;
options.offsetAttribute = offsetAttribute;
options.radii = in3D ? unitSphere : radii;
+ var innerRadii = Property.getValueOrDefault(ellipsoid.innerRadii, time, radii, new Cartesian3());
+ if (in3D) {
+ var mag = Cartesian3.magnitude(radii);
+ var innerRadiiUnit = new Cartesian3(innerRadii.x/mag, innerRadii.y/mag, innerRadii.z/mag);
+ options.innerRadii = innerRadiiUnit;
+ } else {
+ options.innerRadii = innerRadii;
+ }
+ options.minimumClock = Property.getValueOrUndefined(ellipsoid.minimumClock, time);
+ options.maximumClock = Property.getValueOrUndefined(ellipsoid.maximumClock, time);
+ options.minimumCone = Property.getValueOrUndefined(ellipsoid.minimumCone, time);
+ options.maximumCone = Property.getValueOrUndefined(ellipsoid.maximumCone, time);
var appearance = new MaterialAppearance({
material : material,
diff --git a/Source/DataSources/EllipsoidGraphics.js b/Source/DataSources/EllipsoidGraphics.js
index 1998cf42d976..64390aa26fcb 100644
--- a/Source/DataSources/EllipsoidGraphics.js
+++ b/Source/DataSources/EllipsoidGraphics.js
@@ -23,6 +23,13 @@ define([
* @constructor
*
* @param {Object} [options] Object with the following properties:
+ * @param {Property} [options.heightReference] A Property specifying what the height from the entity position is relative to.
+ * @param {Property} [options.radii] A {@link Cartesian3} Property specifying the radii of the ellipsoid.
+ * @param {Property} [options.innerRadii] A {@link Cartesian3} Property specifying the inner radii of the ellipsoid.
+ * @param {Property} [options.minimumClock=0.0] A Property specifying the minimum clock angle of the ellipsoid.
+ * @param {Property} [options.maximumClock=2*PI] A Property specifying the maximum clock angle of the ellipsoid.
+ * @param {Property} [options.minimumCone=0.0] A Property specifying the minimum cone angle of the ellipsoid.
+ * @param {Property} [options.maximumCone=PI] A Property specifying the maximum cone angle of the ellipsoid.
* @param {Property} [options.show=true] A boolean Property specifying the visibility of the ellipsoid.
* @param {Property} [options.radii] A {@link Cartesian3} Property specifying the radii of the ellipsoid.
* @param {Property} [options.heightReference=HeightReference.NONE] A Property specifying what the height from the entity position is relative to.
@@ -45,6 +52,11 @@ define([
this._showSubscription = undefined;
this._radii = undefined;
this._radiiSubscription = undefined;
+ this._innerRadii = undefined;
+ this._minimumClock = undefined;
+ this._maximumClock = undefined;
+ this._minimumCone = undefined;
+ this._maximumCone = undefined;
this._heightReference = undefined;
this._heightReferenceSubscription = undefined;
this._fill = undefined;
@@ -100,6 +112,46 @@ define([
*/
radii : createPropertyDescriptor('radii'),
+ /**
+ * Gets or sets the {@link Cartesian3} {@link Property} specifying the inner radii of the ellipsoid.
+ * @memberof EllipsoidGraphics.prototype
+ * @type {Property}
+ * @default radii
+ */
+ innerRadii : createPropertyDescriptor('innerRadii'),
+
+ /**
+ * Gets or sets the Property specifying the minimum clock angle of the ellipsoid.
+ * @memberof EllipsoidGraphics.prototype
+ * @type {Property}
+ * @default 0.0
+ */
+ minimumClock : createPropertyDescriptor('minimumClock'),
+
+ /**
+ * Gets or sets the Property specifying the maximum clock angle of the ellipsoid.
+ * @memberof EllipsoidGraphics.prototype
+ * @type {Property}
+ * @default 2*PI
+ */
+ maximumClock : createPropertyDescriptor('maximumClock'),
+
+ /**
+ * Gets or sets the Property specifying the minimum cone angle of the ellipsoid.
+ * @memberof EllipsoidGraphics.prototype
+ * @type {Property}
+ * @default 0.0
+ */
+ minimumCone : createPropertyDescriptor('minimumCone'),
+
+ /**
+ * Gets or sets the Property specifying the maximum cone angle of the ellipsoid.
+ * @memberof EllipsoidGraphics.prototype
+ * @type {Property}
+ * @default PI
+ */
+ maximumCone : createPropertyDescriptor('maximumCone'),
+
/**
* Gets or sets the Property specifying the {@link HeightReference}.
* @memberof EllipsoidGraphics.prototype
@@ -157,7 +209,7 @@ define([
stackPartitions : createPropertyDescriptor('stackPartitions'),
/**
- * Gets or sets the Property specifying the number of radial slices.
+ * Gets or sets the Property specifying the number of radial slices per 360 degrees.
* @memberof EllipsoidGraphics.prototype
* @type {Property}
* @default 64
@@ -201,6 +253,11 @@ define([
}
result.show = this.show;
result.radii = this.radii;
+ result.innerRadii = this.innerRadii;
+ result.minimumClock = this.minimumClock;
+ result.maximumClock = this.maximumClock;
+ result.minimumCone = this.minimumCone;
+ result.maximumCone = this.maximumCone;
result.heightReference = this.heightReference;
result.fill = this.fill;
result.material = this.material;
@@ -230,6 +287,11 @@ define([
this.show = defaultValue(this.show, source.show);
this.radii = defaultValue(this.radii, source.radii);
+ this.innerRadii = defaultValue(this.innerRadii, source.innerRadii);
+ this.minimumClock = defaultValue(this.minimumClock, source.minimumClock);
+ this.maximumClock = defaultValue(this.maximumClock, source.maximumClock);
+ this.minimumCone = defaultValue(this.minimumCone, source.minimumCone);
+ this.maximumCone = defaultValue(this.maximumCone, source.maximumCone);
this.heightReference = defaultValue(this.heightReference, source.heightReference);
this.fill = defaultValue(this.fill, source.fill);
this.material = defaultValue(this.material, source.material);
diff --git a/Specs/Core/EllipsoidGeometrySpec.js b/Specs/Core/EllipsoidGeometrySpec.js
index a4339dc1a8d8..7d087f2a82cf 100644
--- a/Specs/Core/EllipsoidGeometrySpec.js
+++ b/Specs/Core/EllipsoidGeometrySpec.js
@@ -14,22 +14,21 @@ define([
CesiumMath,
VertexFormat,
createPackableSpecs) {
- 'use strict';
+ 'use strict';
describe('Core/EllipsoidGeometry', function() {
-
it('constructor rounds floating-point slicePartitions', function() {
var m = new EllipsoidGeometry({
- slicePartitions: 3.5,
- stackPartitions: 3
+ slicePartitions : 3.5,
+ stackPartitions : 3
});
expect(m._slicePartitions).toEqual(4);
});
it('constructor rounds floating-point stackPartitions', function() {
var m = new EllipsoidGeometry({
- slicePartitions: 3,
- stackPartitions: 3.5
+ slicePartitions : 3,
+ stackPartitions : 3.5
});
expect(m._stackPartitions).toEqual(4);
});
@@ -53,12 +52,14 @@ describe('Core/EllipsoidGeometry', function() {
it('computes positions', function() {
var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
vertexFormat : VertexFormat.POSITION_ONLY,
- slicePartitions: 3,
- stackPartitions: 3
+ slicePartitions : 3,
+ stackPartitions : 3
}));
- var numVertices = 16; // 4 rows * 4 positions
- var numTriangles = 12; //3 top + 3 bottom + 6 around the sides
+ // The vertices are 6x6 because an additional slice and stack are added
+ // and the first and last clock and cone angles are duplicated (3 + 1 + 2 = 6)
+ var numVertices = 36; // 6 rows * 6 positions
+ var numTriangles = 18; // 6 top + 6 bottom + 6 around the sides
expect(m.attributes.position.values.length).toEqual(numVertices * 3);
expect(m.indices.length).toEqual(numTriangles * 3);
expect(m.boundingSphere.radius).toEqual(1);
@@ -67,12 +68,12 @@ describe('Core/EllipsoidGeometry', function() {
it('computes offset attribute', function() {
var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
vertexFormat : VertexFormat.POSITION_ONLY,
- slicePartitions: 3,
- stackPartitions: 3,
- offsetAttribute: GeometryOffsetAttribute.ALL
+ slicePartitions : 3,
+ stackPartitions : 3,
+ offsetAttribute : GeometryOffsetAttribute.ALL
}));
- var numVertices = 16;
+ var numVertices = 36;
expect(m.attributes.position.values.length).toEqual(numVertices * 3);
var offset = m.attributes.applyOffset.values;
@@ -85,12 +86,12 @@ describe('Core/EllipsoidGeometry', function() {
it('compute all vertex attributes', function() {
var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
vertexFormat : VertexFormat.ALL,
- slicePartitions: 3,
- stackPartitions: 3
+ slicePartitions : 3,
+ stackPartitions : 3
}));
- var numVertices = 16;
- var numTriangles = 12;
+ var numVertices = 36;
+ var numTriangles = 18;
expect(m.attributes.position.values.length).toEqual(numVertices * 3);
expect(m.attributes.st.values.length).toEqual(numVertices * 2);
expect(m.attributes.normal.values.length).toEqual(numVertices * 3);
@@ -102,8 +103,8 @@ describe('Core/EllipsoidGeometry', function() {
it('computes attributes for a unit sphere', function() {
var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
vertexFormat : VertexFormat.ALL,
- slicePartitions: 3,
- stackPartitions: 3
+ slicePartitions : 3,
+ stackPartitions : 3
}));
var positions = m.attributes.position.values;
@@ -111,7 +112,7 @@ describe('Core/EllipsoidGeometry', function() {
var tangents = m.attributes.tangent.values;
var bitangents = m.attributes.bitangent.values;
- for ( var i = 0; i < positions.length; i += 3) {
+ for (var i = 0; i < positions.length; i += 3) {
var position = Cartesian3.fromArray(positions, i);
var normal = Cartesian3.fromArray(normals, i);
var tangent = Cartesian3.fromArray(tangents, i);
@@ -124,7 +125,122 @@ describe('Core/EllipsoidGeometry', function() {
}
});
- it('undefined is returned if the x, y, or z radii are equal or less than zero', function() {
+ it('computes positions with inner surface', function() {
+ var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ slicePartitions : 3,
+ stackPartitions : 3,
+ innerRadii : new Cartesian3(0.5, 0.5, 0.5)
+ }));
+
+ var numVertices = 72; // 6 rows * 6 positions * 2 surfaces
+ var numTriangles = 36; // (6 top + 6 bottom + 6 around the sides) * 2 surfaces
+ expect(m.attributes.position.values.length).toEqual(numVertices * 3);
+ expect(m.indices.length).toEqual(numTriangles * 3);
+ expect(m.boundingSphere.radius).toEqual(1);
+ });
+
+ it('computes positions with inner surface and partial clock range', function() {
+ var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ slicePartitions : 4,
+ stackPartitions : 4,
+ innerRadii : new Cartesian3(0.5, 0.5, 0.5),
+ minimumClock : CesiumMath.toRadians(90.0),
+ maximumClock : CesiumMath.toRadians(270.0)
+ }));
+
+ var numVertices = 70;
+ var numTriangles = 48;
+ expect(m.attributes.position.values.length).toEqual(numVertices * 3);
+ expect(m.indices.length).toEqual(numTriangles * 3);
+ expect(m.boundingSphere.radius).toEqual(1);
+ });
+
+ it('computes positions with inner surface and partial clock range and open top', function() {
+ var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ slicePartitions : 4,
+ stackPartitions : 4,
+ innerRadii : new Cartesian3(0.5, 0.5, 0.5),
+ minimumClock : CesiumMath.toRadians(90.0),
+ maximumClock : CesiumMath.toRadians(270.0),
+ minimumCone : CesiumMath.toRadians(30.0)
+ }));
+
+ var numVertices = 60;
+ var numTriangles = 40;
+ expect(m.attributes.position.values.length).toEqual(numVertices * 3);
+ expect(m.indices.length).toEqual(numTriangles * 3);
+ expect(m.boundingSphere.radius).toEqual(1);
+ });
+
+ it('computes partitions to default to 2 if less than 2', function() {
+ var geometry = new EllipsoidGeometry({
+ radii : new Cartesian3(0.5, 0.5, 0.5)
+ });
+
+ geometry._slicePartitions = 0;
+ geometry._stackPartitions = 0;
+
+ var m = EllipsoidGeometry.createGeometry(geometry);
+
+ expect(m.indices.length).toEqual(6);
+ });
+
+ it('negates normals on an ellipsoid', function() {
+ var negatedNormals = 0;
+
+ var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
+ vertexFormat : VertexFormat.ALL,
+ radii : new Cartesian3(1.0, 1.0, 1.0),
+ innerRadii : new Cartesian3(0.5, 0.5, 0.5),
+ minimumCone : CesiumMath.toRadians(60.0),
+ maximumCone : CesiumMath.toRadians(140.0)
+ }));
+
+ var positions = m.attributes.position.values;
+ var normals = m.attributes.normal.values;
+
+ for (var i = 0; i < positions.length; i += 3) {
+ var normal = Cartesian3.fromArray(normals, i);
+
+ if (normal.x < 0 && normal.y < 0 && normal.z < 0) {
+ negatedNormals++;
+ }
+ }
+
+ expect(negatedNormals).toEqual(496);
+ });
+
+ it('computes the unit ellipsoid', function() {
+ var ellipsoid = EllipsoidGeometry.getUnitEllipsoid();
+ expect(ellipsoid).toBeDefined();
+ expect(ellipsoid.boundingSphere.radius).toEqual(1);
+
+ expect(EllipsoidGeometry.getUnitEllipsoid()).toBe(ellipsoid);
+ });
+
+ it('computes positions with inner surface and partial clock range and open top and bottom', function() {
+ var m = EllipsoidGeometry.createGeometry(new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ slicePartitions : 4,
+ stackPartitions : 4,
+ innerRadii : new Cartesian3(0.5, 0.5, 0.5),
+ minimumClock : CesiumMath.toRadians(90.0),
+ maximumClock : CesiumMath.toRadians(270.0),
+ minimumCone : CesiumMath.toRadians(30.0),
+ maximumCone : CesiumMath.toRadians(120.0)
+ }));
+
+ var numVertices = 50;
+ var numTriangles = 32;
+ expect(m.attributes.position.values.length).toEqual(numVertices * 3);
+ expect(m.indices.length).toEqual(numTriangles * 3);
+ expect(m.boundingSphere.radius).toEqual(1);
+ });
+
+ it('undefined is returned if the x, y, or z radii or innerRadii are equal or less than zero', function() {
var ellipsoid0 = new EllipsoidGeometry({
vertexFormat : VertexFormat.POSITION_ONLY,
radii : new Cartesian3(0.0, 500000.0, 500000.0)
@@ -149,6 +265,36 @@ describe('Core/EllipsoidGeometry', function() {
vertexFormat : VertexFormat.POSITION_ONLY,
radii : new Cartesian3(1000000.0, 500000.0, -10.0)
});
+ var ellipsoid6 = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(0.0, 100000.0, 100000.0)
+ });
+ var ellipsoid7 = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(100000.0, 0.0, 100000.0)
+ });
+ var ellipsoid8 = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(100000.0, 100000.0, 0.0)
+ });
+ var ellipsoid9 = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(-10.0, 100000.0, 100000.0)
+ });
+ var ellipsoid10 = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(100000.0, -10.0, 100000.0)
+ });
+ var ellipsoid11 = new EllipsoidGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(100000.0, 100000.0, -10.0)
+ });
var geometry0 = EllipsoidGeometry.createGeometry(ellipsoid0);
var geometry1 = EllipsoidGeometry.createGeometry(ellipsoid1);
@@ -156,6 +302,12 @@ describe('Core/EllipsoidGeometry', function() {
var geometry3 = EllipsoidGeometry.createGeometry(ellipsoid3);
var geometry4 = EllipsoidGeometry.createGeometry(ellipsoid4);
var geometry5 = EllipsoidGeometry.createGeometry(ellipsoid5);
+ var geometry6 = EllipsoidGeometry.createGeometry(ellipsoid6);
+ var geometry7 = EllipsoidGeometry.createGeometry(ellipsoid7);
+ var geometry8 = EllipsoidGeometry.createGeometry(ellipsoid8);
+ var geometry9 = EllipsoidGeometry.createGeometry(ellipsoid9);
+ var geometry10 = EllipsoidGeometry.createGeometry(ellipsoid10);
+ var geometry11 = EllipsoidGeometry.createGeometry(ellipsoid11);
expect(geometry0).toBeUndefined();
expect(geometry1).toBeUndefined();
@@ -163,15 +315,34 @@ describe('Core/EllipsoidGeometry', function() {
expect(geometry3).toBeUndefined();
expect(geometry4).toBeUndefined();
expect(geometry5).toBeUndefined();
+ expect(geometry6).toBeUndefined();
+ expect(geometry7).toBeUndefined();
+ expect(geometry8).toBeUndefined();
+ expect(geometry9).toBeUndefined();
+ expect(geometry10).toBeUndefined();
+ expect(geometry11).toBeUndefined();
});
var ellipsoidgeometry = new EllipsoidGeometry({
vertexFormat : VertexFormat.POSITION_ONLY,
radii : new Cartesian3(1.0, 2.0, 3.0),
- slicePartitions: 3,
- stackPartitions: 3
+ innerRadii : new Cartesian3(0.5, 0.6, 0.7),
+ minimumClock : 0.1,
+ maximumClock : 0.2,
+ minimumCone : 0.3,
+ maximumCone : 0.4,
+ slicePartitions : 3,
+ stackPartitions : 3
});
- var packedInstance = [1.0, 2.0, 3.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 3.0, -1.0];
+ var packedInstance = [
+ 1.0, 2.0, 3.0,
+ 0.5, 0.6, 0.7,
+ 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.1, 0.2,
+ 0.3, 0.4,
+ 3.0, 3.0,
+ -1
+ ];
createPackableSpecs(EllipsoidGeometry, ellipsoidgeometry, packedInstance);
});
});
diff --git a/Specs/Core/EllipsoidOutlineGeometrySpec.js b/Specs/Core/EllipsoidOutlineGeometrySpec.js
index 9f259625d00b..57e7a92f9f94 100644
--- a/Specs/Core/EllipsoidOutlineGeometrySpec.js
+++ b/Specs/Core/EllipsoidOutlineGeometrySpec.js
@@ -3,12 +3,14 @@ define([
'Core/Cartesian3',
'Core/EllipsoidOutlineGeometry',
'Core/GeometryOffsetAttribute',
+ 'Core/Math',
'Specs/createPackableSpecs'
], function(
arrayFill,
Cartesian3,
EllipsoidOutlineGeometry,
GeometryOffsetAttribute,
+ CesiumMath,
createPackableSpecs) {
'use strict';
@@ -38,6 +40,14 @@ describe('Core/EllipsoidOutlineGeometry', function() {
}).toThrowDeveloperError();
});
+ it('constructor throws if offset attribute is equal to GeometryOffsetAttribute.TOP', function () {
+ expect(function() {
+ return new EllipsoidOutlineGeometry({
+ offsetAttribute: GeometryOffsetAttribute.TOP
+ });
+ }).toThrowDeveloperError();
+ });
+
it('constructor rounds floating-point slicePartitions', function() {
var m = new EllipsoidOutlineGeometry({
slicePartitions: 3.5,
@@ -67,13 +77,30 @@ describe('Core/EllipsoidOutlineGeometry', function() {
it('computes positions', function() {
var m = EllipsoidOutlineGeometry.createGeometry(new EllipsoidOutlineGeometry({
- stackPartitions : 3,
+ stackPartitions: 3,
slicePartitions: 3,
subdivisions: 3
}));
- expect(m.attributes.position.values.length).toEqual(14 * 3);
- expect(m.indices.length).toEqual(15 * 2);
+ expect(m.attributes.position.values.length).toEqual(24 * 3);
+ expect(m.indices.length).toEqual(16 * 2);
+ expect(m.boundingSphere.radius).toEqual(1);
+ });
+
+ it('computes positions for partial ellipsoid', function() {
+ var m = EllipsoidOutlineGeometry.createGeometry(new EllipsoidOutlineGeometry({
+ innerRadii: new Cartesian3(0.5, 0.5, 0.5),
+ minimumClock: CesiumMath.toRadians(90.0),
+ maximumClock: CesiumMath.toRadians(270.0),
+ minimumCone: CesiumMath.toRadians(30.0),
+ maximumCone: CesiumMath.toRadians(120.0),
+ stackPartitions: 3,
+ slicePartitions: 3,
+ subdivisions: 3
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(24 * 3);
+ expect(m.indices.length).toEqual(20 * 2);
expect(m.boundingSphere.radius).toEqual(1);
});
@@ -85,7 +112,7 @@ describe('Core/EllipsoidOutlineGeometry', function() {
offsetAttribute: GeometryOffsetAttribute.ALL
}));
- var numVertices = 14;
+ var numVertices = 24;
expect(m.attributes.position.values.length).toEqual(numVertices * 3);
var offset = m.attributes.applyOffset.values;
@@ -95,6 +122,19 @@ describe('Core/EllipsoidOutlineGeometry', function() {
expect(offset).toEqual(expected);
});
+ it('computes partitions to default to 2 if less than 2', function() {
+ var geometry = new EllipsoidOutlineGeometry({
+ radii: new Cartesian3(0.5, 0.5, 0.5)
+ });
+
+ geometry._slicePartitions = 0;
+ geometry._stackPartitions = 0;
+
+ var m = EllipsoidOutlineGeometry.createGeometry(geometry);
+
+ expect(m.indices.length).toEqual(1016);
+ });
+
it('undefined is returned if the x, y, or z radii are equal or less than zero', function() {
var ellipsoidOutline0 = new EllipsoidOutlineGeometry({
radii : new Cartesian3(0.0, 500000.0, 500000.0)
@@ -114,6 +154,30 @@ describe('Core/EllipsoidOutlineGeometry', function() {
var ellipsoidOutline5 = new EllipsoidOutlineGeometry({
radii : new Cartesian3(1000000.0, 500000.0, -10.0)
});
+ var ellipsoidOutline6 = new EllipsoidOutlineGeometry({
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(0.0, 100000.0, 100000.0)
+ });
+ var ellipsoidOutline7 = new EllipsoidOutlineGeometry({
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(100000.0, 0.0, 100000.0)
+ });
+ var ellipsoidOutline8 = new EllipsoidOutlineGeometry({
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(100000.0, 100000.0, 0.0)
+ });
+ var ellipsoidOutline9 = new EllipsoidOutlineGeometry({
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(-10.0, 100000.0, 100000.0)
+ });
+ var ellipsoidOutline10 = new EllipsoidOutlineGeometry({
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(100000.0, -10.0, 100000.0)
+ });
+ var ellipsoidOutline11 = new EllipsoidOutlineGeometry({
+ radii : new Cartesian3(500000.0, 500000.0, 500000.0),
+ innerRadii : new Cartesian3(100000.0, 100000.0, -10.0)
+ });
var geometry0 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline0);
var geometry1 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline1);
@@ -121,6 +185,12 @@ describe('Core/EllipsoidOutlineGeometry', function() {
var geometry3 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline3);
var geometry4 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline4);
var geometry5 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline5);
+ var geometry6 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline6);
+ var geometry7 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline7);
+ var geometry8 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline8);
+ var geometry9 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline9);
+ var geometry10 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline10);
+ var geometry11 = EllipsoidOutlineGeometry.createGeometry(ellipsoidOutline11);
expect(geometry0).toBeUndefined();
expect(geometry1).toBeUndefined();
@@ -128,15 +198,33 @@ describe('Core/EllipsoidOutlineGeometry', function() {
expect(geometry3).toBeUndefined();
expect(geometry4).toBeUndefined();
expect(geometry5).toBeUndefined();
+ expect(geometry6).toBeUndefined();
+ expect(geometry7).toBeUndefined();
+ expect(geometry8).toBeUndefined();
+ expect(geometry9).toBeUndefined();
+ expect(geometry10).toBeUndefined();
+ expect(geometry11).toBeUndefined();
});
var ellipsoidgeometry = new EllipsoidOutlineGeometry({
radii : new Cartesian3(1.0, 2.0, 3.0),
- slicePartitions: 3,
- stackPartitions: 3,
+ innerRadii : new Cartesian3(0.5, 0.6, 0.7),
+ minimumClock : 0.1,
+ maximumClock : 0.2,
+ minimumCone : 0.3,
+ maximumCone : 0.4,
+ slicePartitions : 3,
+ stackPartitions : 3,
subdivisions: 3
});
- var packedInstance = [1.0, 2.0, 3.0, 3.0, 3.0, 3.0, -1.0];
+ var packedInstance = [
+ 1.0, 2.0, 3.0,
+ 0.5, 0.6, 0.7,
+ 0.1, 0.2,
+ 0.3, 0.4,
+ 3.0, 3.0, 3.0,
+ -1
+ ];
createPackableSpecs(EllipsoidOutlineGeometry, ellipsoidgeometry, packedInstance);
});
});
diff --git a/Specs/Core/SphereGeometrySpec.js b/Specs/Core/SphereGeometrySpec.js
index 7be449f831b8..77037e6a5af3 100644
--- a/Specs/Core/SphereGeometrySpec.js
+++ b/Specs/Core/SphereGeometrySpec.js
@@ -38,8 +38,12 @@ describe('Core/SphereGeometry', function() {
slicePartitions: 3
}));
- expect(m.attributes.position.values.length).toEqual(16 * 3); // 4 positions * 4 rows
- expect(m.indices.length).toEqual(12 * 3); //3 top + 3 bottom + 2 triangles * 3 sides
+ // The vertices are 6x6 because an additional slice and stack are added
+ // and the first and last clock and cone angles are duplicated (3 + 1 + 2 = 6)
+ var numVertices = 36; // 6 rows * 6 positions
+ var numTriangles = 18; // 6 top + 6 bottom + 6 around the sides
+ expect(m.attributes.position.values.length).toEqual(numVertices * 3); // 4 positions * 4 rows
+ expect(m.indices.length).toEqual(numTriangles * 3); //3 top + 3 bottom + 2 triangles * 3 sides
expect(m.boundingSphere.radius).toEqual(1);
});
@@ -51,8 +55,8 @@ describe('Core/SphereGeometry', function() {
slicePartitions: 3
}));
- var numVertices = 16;
- var numTriangles = 12;
+ var numVertices = 36;
+ var numTriangles = 18;
expect(m.attributes.position.values.length).toEqual(numVertices * 3);
expect(m.attributes.st.values.length).toEqual(numVertices * 2);
expect(m.attributes.normal.values.length).toEqual(numVertices * 3);
@@ -104,7 +108,8 @@ describe('Core/SphereGeometry', function() {
stackPartitions : 3,
slicePartitions: 3
});
- var packedInstance = [1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 3.0, -1.0];
+ // Adding TWO_PI and PI here for maximum clock/cone and other options from partial ellipsoids
+ var packedInstance = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, CesiumMath.TWO_PI, 0.0, CesiumMath.PI, 3.0, 3.0, -1.0];
createPackableSpecs(SphereGeometry, sphere, packedInstance);
});
});
diff --git a/Specs/Core/SphereOutlineGeometrySpec.js b/Specs/Core/SphereOutlineGeometrySpec.js
index 53a0d6e8df48..984d531d35b6 100644
--- a/Specs/Core/SphereOutlineGeometrySpec.js
+++ b/Specs/Core/SphereOutlineGeometrySpec.js
@@ -1,9 +1,11 @@
define([
'Core/SphereOutlineGeometry',
- 'Specs/createPackableSpecs'
+ 'Specs/createPackableSpecs',
+ 'Core/Math'
], function(
SphereOutlineGeometry,
- createPackableSpecs) {
+ createPackableSpecs,
+ CesiumMath) {
'use strict';
describe('Core/SphereOutlineGeometry', function() {
@@ -39,7 +41,7 @@ describe('Core/SphereOutlineGeometry', function() {
subdivisions: 2
}));
- expect(m.attributes.position.values.length).toEqual(6 * 3);
+ expect(m.attributes.position.values.length).toEqual(12 * 3);
expect(m.indices.length).toEqual(6 * 2);
expect(m.boundingSphere.radius).toEqual(1);
});
@@ -60,7 +62,7 @@ describe('Core/SphereOutlineGeometry', function() {
slicePartitions: 3,
subdivisions: 2
});
- var packedInstance = [1.0, 1.0, 1.0, 3.0, 3.0, 2.0, -1.0];
+ var packedInstance = [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, CesiumMath.TWO_PI, 0.0, CesiumMath.PI, 3.0, 3.0, 2.0, -1.0 ];
createPackableSpecs(SphereOutlineGeometry, sphere, packedInstance);
});
});
diff --git a/Specs/Core/barycentricCoordinatesSpec.js b/Specs/Core/barycentricCoordinatesSpec.js
index 89d602c884a8..8c72f9ed20f8 100644
--- a/Specs/Core/barycentricCoordinatesSpec.js
+++ b/Specs/Core/barycentricCoordinatesSpec.js
@@ -50,6 +50,12 @@ describe('Core/barycentricCoordinates', function() {
expect(barycentricCoordinates(point, p0, p1, p2)).toEqualEpsilon(new Cartesian3(scalar, scalar, scalar), CesiumMath.EPSILON14);
});
+ it('evaluates without throwing a NaN', function() {
+ var point = Cartesian3.multiplyByScalar(Cartesian3.add(p1, p1, p1), 0.5, new Cartesian3());
+ var coord = barycentricCoordinates(point, p0, p1, p2);
+ expect(coord.z).not.toBeNaN();
+ });
+
it('evaluates with equal length sides', function() {
var p0 = new Cartesian3(9635312487071484, 13827945400273020, -16479219993905144);
var p1 = new Cartesian3(12832234.180639317, -10455085.701705107, 750010.7274386138);
diff --git a/Specs/DataSources/EllipsoidGeometryUpdaterSpec.js b/Specs/DataSources/EllipsoidGeometryUpdaterSpec.js
index 88be95e2dccf..4b6e9173b877 100644
--- a/Specs/DataSources/EllipsoidGeometryUpdaterSpec.js
+++ b/Specs/DataSources/EllipsoidGeometryUpdaterSpec.js
@@ -4,6 +4,7 @@ define([
'Core/ColorGeometryInstanceAttribute',
'Core/GeometryOffsetAttribute',
'Core/JulianDate',
+ 'Core/Math',
'Core/Quaternion',
'Core/TimeIntervalCollection',
'DataSources/ColorMaterialProperty',
@@ -26,6 +27,7 @@ define([
ColorGeometryInstanceAttribute,
GeometryOffsetAttribute,
JulianDate,
+ CesiumMath,
Quaternion,
TimeIntervalCollection,
ColorMaterialProperty,
@@ -137,6 +139,11 @@ describe('DataSources/EllipsoidGeometryUpdater', function() {
it('Creates geometry with expected properties', function() {
var options = {
radii : new Cartesian3(1, 2, 3),
+ innerRadii: new Cartesian3(0.5, 1, 1.5),
+ minimumClock: CesiumMath.toRadians(90.0),
+ maximumClock: CesiumMath.toRadians(270.0),
+ minimumCone: CesiumMath.toRadians(45.0),
+ maximumCone: CesiumMath.toRadians(90.0),
stackPartitions : 32,
slicePartitions : 64,
subdivisions : 15
@@ -151,6 +158,11 @@ describe('DataSources/EllipsoidGeometryUpdater', function() {
ellipsoid.radii = new ConstantProperty(options.radii);
ellipsoid.stackPartitions = new ConstantProperty(options.stackPartitions);
ellipsoid.slicePartitions = new ConstantProperty(options.slicePartitions);
+ ellipsoid.innerRadii = new ConstantProperty(options.innerRadii);
+ ellipsoid.minimumClock = new ConstantProperty(options.minimumClock);
+ ellipsoid.maximumClock = new ConstantProperty(options.maximumClock);
+ ellipsoid.minimumCone = new ConstantProperty(options.minimumCone);
+ ellipsoid.maximumCone = new ConstantProperty(options.maximumCone);
ellipsoid.subdivisions = new ConstantProperty(options.subdivisions);
entity.ellipsoid = ellipsoid;
@@ -162,6 +174,11 @@ describe('DataSources/EllipsoidGeometryUpdater', function() {
geometry = instance.geometry;
expect(geometry._center).toEqual(options.center);
expect(geometry._radii).toEqual(options.radii);
+ expect(geometry._innerRadii).toEqual(options.innerRadii);
+ expect(geometry._minimumClock).toEqual(options.minimumClock);
+ expect(geometry._maximumClock).toEqual(options.maximumClock);
+ expect(geometry._minimumCone).toEqual(options.minimumCone);
+ expect(geometry._maximumCone).toEqual(options.maximumCone);
expect(geometry._stackPartitions).toEqual(options.stackPartitions);
expect(geometry._slicePartitions).toEqual(options.slicePartitions);
expect(geometry._offsetAttribute).toBeUndefined();
@@ -170,6 +187,11 @@ describe('DataSources/EllipsoidGeometryUpdater', function() {
geometry = instance.geometry;
expect(geometry._center).toEqual(options.center);
expect(geometry._radii).toEqual(options.radii);
+ expect(geometry._innerRadii).toEqual(options.innerRadii);
+ expect(geometry._minimumClock).toEqual(options.minimumClock);
+ expect(geometry._maximumClock).toEqual(options.maximumClock);
+ expect(geometry._minimumCone).toEqual(options.minimumCone);
+ expect(geometry._maximumCone).toEqual(options.maximumCone);
expect(geometry._stackPartitions).toEqual(options.stackPartitions);
expect(geometry._slicePartitions).toEqual(options.slicePartitions);
expect(geometry._subdivisions).toEqual(options.subdivisions);
@@ -310,6 +332,31 @@ describe('DataSources/EllipsoidGeometryUpdater', function() {
updater.destroy();
});
+ it('Inner radii should be set when not in 3D mode', function() {
+ var ellipsoid = new EllipsoidGraphics();
+ ellipsoid.radii = createDynamicProperty(new Cartesian3(1, 2, 3));
+ ellipsoid.innerRadii = createDynamicProperty(new Cartesian3(0.5, 1, 1.5));
+ // Turns 3d mode path off
+ ellipsoid.heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND);
+ ellipsoid.material = new ColorMaterialProperty(Color.RED);
+
+ var entity = new Entity();
+ entity.position = createDynamicProperty(Cartesian3.fromDegrees(0, 0, 0));
+ entity.orientation = createDynamicProperty(Quaternion.IDENTITY);
+ entity.ellipsoid = ellipsoid;
+
+ var updater = new EllipsoidGeometryUpdater(entity, scene);
+ var primitives = scene.primitives;
+
+ var dynamicUpdater = updater.createDynamicUpdater(primitives, new PrimitiveCollection());
+ dynamicUpdater.update(time);
+
+ scene.initializeFrame();
+ scene.render();
+
+ expect(dynamicUpdater._options.innerRadii).toEqual(ellipsoid.innerRadii.getValue());
+ });
+
it('dynamic ellipsoid fast path updates attributes', function() {
var ellipsoid = new EllipsoidGraphics();
ellipsoid.show = createDynamicProperty(true);
diff --git a/Specs/DataSources/EllipsoidGraphicsSpec.js b/Specs/DataSources/EllipsoidGraphicsSpec.js
index 6f78ec106ad7..b2f4489e1bbb 100644
--- a/Specs/DataSources/EllipsoidGraphicsSpec.js
+++ b/Specs/DataSources/EllipsoidGraphicsSpec.js
@@ -67,6 +67,11 @@ describe('DataSources/EllipsoidGraphics', function() {
var source = new EllipsoidGraphics();
source.material = new ColorMaterialProperty();
source.radii = new ConstantProperty();
+ source.innerRadii = new ConstantProperty();
+ source.minimumClock = new ConstantProperty();
+ source.maximumClock = new ConstantProperty();
+ source.minimumCone = new ConstantProperty();
+ source.maximumCone = new ConstantProperty();
source.show = new ConstantProperty();
source.stackPartitions = new ConstantProperty();
source.slicePartitions = new ConstantProperty();
@@ -83,6 +88,11 @@ describe('DataSources/EllipsoidGraphics', function() {
expect(target.material).toBe(source.material);
expect(target.radii).toBe(source.radii);
+ expect(target.innerRadii).toBe(source.innerRadii);
+ expect(target.minimumClock).toBe(source.minimumClock);
+ expect(target.maximumClock).toBe(source.maximumClock);
+ expect(target.minimumCone).toBe(source.minimumCone);
+ expect(target.maximumCone).toBe(source.maximumCone);
expect(target.show).toBe(source.show);
expect(target.stackPartitions).toBe(source.stackPartitions);
expect(target.slicePartitions).toBe(source.slicePartitions);
@@ -100,6 +110,11 @@ describe('DataSources/EllipsoidGraphics', function() {
var material = new ColorMaterialProperty();
var radii = new ConstantProperty();
+ var innerRadii = new ConstantProperty();
+ var minimumClock = new ConstantProperty();
+ var maximumClock = new ConstantProperty();
+ var minimumCone = new ConstantProperty();
+ var maximumCone = new ConstantProperty();
var show = new ConstantProperty();
var stackPartitions = new ConstantProperty();
var slicePartitions = new ConstantProperty();
@@ -114,6 +129,11 @@ describe('DataSources/EllipsoidGraphics', function() {
var target = new EllipsoidGraphics();
target.material = material;
target.radii = radii;
+ target.innerRadii = innerRadii;
+ target.minimumClock = minimumClock;
+ target.maximumClock = maximumClock;
+ target.minimumCone = minimumCone;
+ target.maximumCone = maximumCone;
target.show = show;
target.stackPartitions = stackPartitions;
target.slicePartitions = slicePartitions;
@@ -130,6 +150,11 @@ describe('DataSources/EllipsoidGraphics', function() {
expect(target.material).toBe(material);
expect(target.radii).toBe(radii);
+ expect(target.innerRadii).toBe(innerRadii);
+ expect(target.minimumClock).toBe(minimumClock);
+ expect(target.maximumClock).toBe(maximumClock);
+ expect(target.minimumCone).toBe(minimumCone);
+ expect(target.maximumCone).toBe(maximumCone);
expect(target.show).toBe(show);
expect(target.stackPartitions).toBe(stackPartitions);
expect(target.slicePartitions).toBe(slicePartitions);
@@ -170,6 +195,21 @@ describe('DataSources/EllipsoidGraphics', function() {
expect(result.outlineWidth).toBe(source.outlineWidth);
expect(result.shadows).toBe(source.shadows);
expect(result.distanceDisplayCondition).toBe(source.distanceDisplayCondition);
+
+ // Clone with source passed
+ result = source.clone(source);
+ expect(result.material).toBe(source.material);
+ expect(result.radii).toBe(source.radii);
+ expect(result.show).toBe(source.show);
+ expect(result.stackPartitions).toBe(source.stackPartitions);
+ expect(result.slicePartitions).toBe(source.slicePartitions);
+ expect(result.subdivisions).toBe(source.subdivisions);
+ expect(result.fill).toBe(source.fill);
+ expect(result.outline).toBe(source.outline);
+ expect(result.outlineColor).toBe(source.outlineColor);
+ expect(result.outlineWidth).toBe(source.outlineWidth);
+ expect(result.shadows).toBe(source.shadows);
+ expect(result.distanceDisplayCondition).toBe(source.distanceDisplayCondition);
});
it('merge throws if source undefined', function() {