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() {