diff --git a/Apps/CesiumViewer/CesiumViewer.js b/Apps/CesiumViewer/CesiumViewer.js
index 473e2aca2e7f..b92c90c373a6 100644
--- a/Apps/CesiumViewer/CesiumViewer.js
+++ b/Apps/CesiumViewer/CesiumViewer.js
@@ -141,4 +141,4 @@ define([
}
}
}
-});
+});
\ No newline at end of file
diff --git a/Apps/Sandcastle/gallery/Geometry and Appearances.html b/Apps/Sandcastle/gallery/Geometry and Appearances.html
index 3636191074c2..ce5a68761208 100644
--- a/Apps/Sandcastle/gallery/Geometry and Appearances.html
+++ b/Apps/Sandcastle/gallery/Geometry and Appearances.html
@@ -28,13 +28,10 @@
"use strict";
Cesium.Math.setRandomNumberSeed(1234);
-
var viewer = new Cesium.Viewer('cesiumContainer');
-
var scene = viewer.scene;
var primitives = scene.getPrimitives();
var ellipsoid = viewer.centralBody.getEllipsoid();
-
var solidWhite = Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE);
// Combine instances for an extent, polygon, ellipse, and circle into a single primitive.
@@ -44,7 +41,7 @@
geometry : new Cesium.ExtentGeometry({
extent : extent,
vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
- stRotation: Cesium.Math.toRadians(45)
+ stRotation : Cesium.Math.toRadians(45)
})
});
var extentOutlineInstance = new Cesium.GeometryInstance({
@@ -63,7 +60,7 @@
Cesium.Cartographic.fromDegrees(-97.0, 21.0),
Cesium.Cartographic.fromDegrees(-97.0, 25.0)
]);
-
+
var polygonInstance = new Cesium.GeometryInstance({
geometry : Cesium.PolygonGeometry.fromPositions({
positions : positions,
@@ -89,7 +86,7 @@
semiMinorAxis : semiMinorAxis,
semiMajorAxis : semiMajorAxis,
rotation : rotation,
- stRotation: Cesium.Math.toRadians(22),
+ stRotation : Cesium.Math.toRadians(22),
vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT
})
});
@@ -111,7 +108,7 @@
geometry : new Cesium.CircleGeometry({
center : center,
radius : radius,
- stRotation: Cesium.Math.toRadians(90),
+ stRotation : Cesium.Math.toRadians(90),
vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT
})
});
@@ -182,7 +179,7 @@
semiMajorAxis : semiMajorAxis,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
height : height,
- rotation: rotation,
+ rotation : rotation,
extrudedHeight : extrudedHeight
}),
attributes : {
@@ -195,7 +192,7 @@
semiMinorAxis : semiMinorAxis,
semiMajorAxis : semiMajorAxis,
height : height,
- rotation: rotation,
+ rotation : rotation,
extrudedHeight : extrudedHeight
}),
attributes : {
@@ -219,7 +216,7 @@
polygonHierarchy : polygonHierarchy,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
extrudedHeight : extrudedHeight,
- height: height
+ height : height
}),
attributes : {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 1.0}))
@@ -229,7 +226,7 @@
geometry : new Cesium.PolygonOutlineGeometry({
polygonHierarchy : polygonHierarchy,
extrudedHeight : extrudedHeight,
- height: height
+ height : height
}),
attributes : {
color : solidWhite
@@ -241,7 +238,8 @@
var topRadius = 150000.0;
var bottomRadius = 150000.0;
var modelMatrix = Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
- ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-70.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, 100000.0));
+ ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-70.0, 45.0))),
+ new Cesium.Cartesian3(0.0, 0.0, 100000.0));
var cylinderInstance = new Cesium.GeometryInstance({
geometry : new Cesium.CylinderGeometry({
length : length,
@@ -328,11 +326,11 @@
for (i = 0; i < 5; ++i) {
height = 100000.0 + (200000.0 * i);
boxModelMatrix = Cesium.Matrix4.multiplyByUniformScale(Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
- ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-106.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
+ ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-106.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
ellipsoidModelMatrix = Cesium.Matrix4.multiplyByUniformScale(Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
- ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-102.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
+ ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-102.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
sphereModelMatrix = Cesium.Matrix4.multiplyByUniformScale(Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
- ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-98.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
+ ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-98.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
instances.push(new Cesium.GeometryInstance({
geometry : boxGeometry,
@@ -407,25 +405,24 @@
Cesium.Cartographic.fromDegrees(-75.0, 50.0)
]);
var maximumHeights = [500000, 1000000, 500000];
- var minimumHeights = [0, 500000, 0];
+ var minimumHeights = [0, 500000, 0];
var wallInstance = new Cesium.GeometryInstance({
geometry : new Cesium.WallGeometry({
positions : positions,
- maximumHeights: maximumHeights,
- minimumHeights: minimumHeights
-
+ maximumHeights : maximumHeights,
+ minimumHeights : minimumHeights
}),
attributes : {
- color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha:0.7}))
+ color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.7}))
}
});
var wallOutlineInstance = new Cesium.GeometryInstance({
geometry : new Cesium.WallOutlineGeometry({
positions : positions,
- maximumHeights: maximumHeights,
- minimumHeights: minimumHeights
+ maximumHeights : maximumHeights,
+ minimumHeights : minimumHeights
}),
attributes : {
color : new Cesium.ColorGeometryInstanceAttribute(0.7, 0.7, 0.7, 1.0)
@@ -594,7 +591,7 @@
topRadius = 0.0;
bottomRadius = 200000.0;
modelMatrix = Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
- ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-70.0, 40.0))), new Cesium.Cartesian3(0.0, 0.0, 200000.0));
+ ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-70.0, 40.0))), new Cesium.Cartesian3(0.0, 0.0, 200000.0));
cylinderInstance = new Cesium.GeometryInstance({
geometry : new Cesium.CylinderGeometry({
length : length,
@@ -635,7 +632,7 @@
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes : {
- color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha:0.5}))
+ color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.5}))
}
}));
@@ -646,7 +643,7 @@
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes : {
- color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha:0.5}))
+ color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.5}))
}
}));
}
@@ -681,12 +678,11 @@
for (i = 0; i < 5; ++i) {
height = 100000.0 + (200000.0 * i);
boxModelMatrix = Cesium.Matrix4.multiplyByUniformScale(Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
- ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-108.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
+ ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-108.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
ellipsoidModelMatrix = Cesium.Matrix4.multiplyByUniformScale(Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
- ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-104.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
+ ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-104.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
sphereModelMatrix = Cesium.Matrix4.multiplyByUniformScale(Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
- ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-100.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
-
+ ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-100.0, 45.0))), new Cesium.Cartesian3(0.0, 0.0, height)), 90000.0);
instances.push(new Cesium.GeometryInstance({
geometry : boxGeometry,
modelMatrix : boxModelMatrix,
@@ -719,7 +715,7 @@
closed : true
})
}));
-
+
positions = [];
var colors = [];
for (i = 0; i < 40; ++i) {
@@ -746,7 +742,7 @@
}
})
}));
-
+
// create a polyline with a material
positions = [];
for (i = 0; i < 40; ++i) {
@@ -765,7 +761,7 @@
material : Cesium.Material.fromType(Cesium.Material.PolylineGlowType)
})
}));
-
+
// create a polyline with per segment colors
positions = [];
colors = [];
@@ -785,7 +781,7 @@
}),
appearance : new Cesium.PolylineColorAppearance()
}));
-
+
// create a polyline with per vertex colors
positions = [];
colors = [];
@@ -831,7 +827,7 @@
faceForward : true
})
}));
-
+
positions = ellipsoid.cartographicArrayToCartesianArray([
Cesium.Cartographic.fromDegrees(-120.0, 45.0),
Cesium.Cartographic.fromDegrees(-125.0, 50.0),
@@ -840,7 +836,7 @@
var width = 100000;
var corridor = new Cesium.GeometryInstance({
- geometry: new Cesium.CorridorGeometry({
+ geometry : new Cesium.CorridorGeometry({
positions : positions,
width : width,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
@@ -849,43 +845,43 @@
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 1.0}))
}
});
-
+
var extrudedCorridor = new Cesium.GeometryInstance({
- geometry: new Cesium.CorridorGeometry({
+ geometry : new Cesium.CorridorGeometry({
positions : positions,
width : width,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
- height: 300000,
- extrudedHeight: 400000
+ height : 300000,
+ extrudedHeight : 400000
}),
attributes : {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.7}))
}
});
-
+
var corridorOutline = new Cesium.GeometryInstance({
- geometry: new Cesium.CorridorOutlineGeometry( {
- positions: positions,
- width: width,
- height: 700000
+ geometry : new Cesium.CorridorOutlineGeometry({
+ positions : positions,
+ width : width,
+ height : 700000
}),
- attributes: {
+ attributes : {
color : solidWhite
}
});
-
+
var corridorFill = new Cesium.GeometryInstance({
- geometry: new Cesium.CorridorGeometry({
+ geometry : new Cesium.CorridorGeometry({
positions : positions,
width : width,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
- height: 700000
+ height : 700000
}),
attributes : {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.7}))
}
});
-
+
primitives.add(new Cesium.Primitive({
geometryInstances : [corridor, extrudedCorridor, corridorFill],
appearance : new Cesium.PerInstanceColorAppearance({
@@ -894,7 +890,7 @@
faceForward : true
})
}));
-
+
primitives.add(new Cesium.Primitive({
geometryInstances : corridorOutline,
appearance : new Cesium.PerInstanceColorAppearance({
@@ -908,6 +904,94 @@
})
}));
+ function starPositions(arms, rOuter, rInner) {
+ var angle = Math.PI / arms;
+ var pos = [];
+ for ( var i = 0; i < 2 * arms; i++) {
+ var r = (i % 2) === 0 ? rOuter : rInner;
+ var p = new Cesium.Cartesian2(Math.cos(i * angle) * r, Math.sin(i * angle) * r);
+ pos.push(p);
+ }
+ return pos;
+ }
+
+ positions = ellipsoid.cartographicArrayToCartesianArray([
+ Cesium.Cartographic.fromDegrees(-102.0, 15.0, 100000.0),
+ Cesium.Cartographic.fromDegrees(-105.0, 20.0, 200000.0),
+ Cesium.Cartographic.fromDegrees(-110.0, 20.0, 100000.0)
+ ]);
+ var polylineVolumeFill = new Cesium.GeometryInstance({
+ geometry : new Cesium.PolylineVolumeGeometry({
+ polylinePositions : positions,
+ vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
+ shapePositions : starPositions(7, 30000.0, 20000.0)
+ }),
+ attributes : {
+ color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 1.0}))
+ }
+ });
+
+ var polylineVolumeOutline = new Cesium.GeometryInstance({
+ geometry : new Cesium.PolylineVolumeOutlineGeometry({
+ polylinePositions : positions,
+ shapePositions : starPositions(7, 30000.0, 20000.0)
+ }),
+ attributes : {
+ color : solidWhite
+ }
+ });
+
+ var polylineVolume = new Cesium.GeometryInstance({
+ geometry : new Cesium.PolylineVolumeGeometry({
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cesium.Cartographic.fromDegrees(-102.0, 15.0),
+ Cesium.Cartographic.fromDegrees(-105.0, 20.0),
+ Cesium.Cartographic.fromDegrees(-110.0, 20.0)
+ ]),
+ vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
+ shapePositions : starPositions(7, 30000, 20000)
+ }),
+ attributes : {
+ color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 1.0}))
+ }
+ });
+
+ var tubeGeometry = new Cesium.GeometryInstance({
+ geometry : new Cesium.PolylineVolumeGeometry({
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cesium.Cartographic.fromDegrees(-104.0, 13.0),
+ Cesium.Cartographic.fromDegrees(-107.0, 18.0),
+ Cesium.Cartographic.fromDegrees(-112.0, 18.0)
+ ]),
+ vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
+ shapePositions : Cesium.Shapes.compute2DCircle(40000.0)
+ }),
+ attributes : {
+ color : solidWhite
+ }
+ });
+
+ primitives.add(new Cesium.Primitive({
+ geometryInstances : [tubeGeometry, polylineVolume, polylineVolumeFill],
+ appearance : new Cesium.PerInstanceColorAppearance({
+ translucent : false,
+ closed : true
+ })
+ }));
+
+ primitives.add(new Cesium.Primitive({
+ geometryInstances : polylineVolumeOutline,
+ appearance : new Cesium.PerInstanceColorAppearance({
+ flat : true,
+ renderState : {
+ depthTest : {
+ enabled : true
+ },
+ lineWidth : 1.0
+ }
+ })
+ }));
+
Sandcastle.finishedLoading();
});
diff --git a/Apps/Sandcastle/gallery/Polyline Volume Outline.html b/Apps/Sandcastle/gallery/Polyline Volume Outline.html
new file mode 100644
index 000000000000..404a1456d1dc
--- /dev/null
+++ b/Apps/Sandcastle/gallery/Polyline Volume Outline.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+Loading...
+
+
+
+
diff --git a/Apps/Sandcastle/gallery/Polyline Volume Outline.jpg b/Apps/Sandcastle/gallery/Polyline Volume Outline.jpg
new file mode 100644
index 000000000000..887c643052ea
Binary files /dev/null and b/Apps/Sandcastle/gallery/Polyline Volume Outline.jpg differ
diff --git a/Apps/Sandcastle/gallery/Polyline Volume.html b/Apps/Sandcastle/gallery/Polyline Volume.html
new file mode 100644
index 000000000000..3ebbd663cd24
--- /dev/null
+++ b/Apps/Sandcastle/gallery/Polyline Volume.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+Loading...
+
+
+
+
diff --git a/Apps/Sandcastle/gallery/Polyline Volume.jpg b/Apps/Sandcastle/gallery/Polyline Volume.jpg
new file mode 100644
index 000000000000..e1bf23942f4a
Binary files /dev/null and b/Apps/Sandcastle/gallery/Polyline Volume.jpg differ
diff --git a/CHANGES.md b/CHANGES.md
index 2c975d022876..dbdced5a0057 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -32,6 +32,8 @@ Beta Releases
// Use properties and functions in p.id
}
* Added `Scene.drillPick` to return list of objects each containing 1 primitive at a screen space position.
+* Added `PolylineVolumeGeometry` and `PolylineVolumeGeometryOutline`.
+* Added `Shapes.compute2DCircle`.
### b21 - 2013-10-01
diff --git a/Source/Core/CorridorGeometryLibrary.js b/Source/Core/CorridorGeometryLibrary.js
index fa5660e2fa8d..a41dd9bcaaf4 100644
--- a/Source/Core/CorridorGeometryLibrary.js
+++ b/Source/Core/CorridorGeometryLibrary.js
@@ -6,6 +6,7 @@ define([
'./CornerType',
'./EllipsoidTangentPlane',
'./PolylinePipeline',
+ './PolylineVolumeGeometryLibrary',
'./Matrix3',
'./Quaternion',
'./Math'
@@ -16,6 +17,7 @@ define([
CornerType,
EllipsoidTangentPlane,
PolylinePipeline,
+ PolylineVolumeGeometryLibrary,
Matrix3,
Quaternion,
CesiumMath) {
@@ -44,24 +46,9 @@ define([
var cartesian9 = new Cartesian3();
var cartesian10 = new Cartesian3();
- var originScratch = new Cartesian3();
- var nextScratch = new Cartesian3();
- var prevScratch = new Cartesian3();
- function angleIsGreaterThanPi (forward, backward, position, ellipsoid) {
- var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid);
- var origin = tangentPlane.projectPointOntoPlane(position, originScratch);
- var next = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, forward, nextScratch), nextScratch);
- var prev = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, backward, prevScratch), prevScratch);
-
- prev = Cartesian2.subtract(prev, origin, prev);
- next = Cartesian2.subtract(next, origin, next);
-
- return ((prev.x * next.y) - (prev.y * next.x)) >= 0.0;
- }
-
var quaterion = new Quaternion();
var rotMatrix = new Matrix3();
- function computeRoundCorner (cornerPoint, startPoint, endPoint, cornerType, leftIsOutside, ellipsoid) {
+ function computeRoundCorner (cornerPoint, startPoint, endPoint, cornerType, leftIsOutside) {
var angle = Cartesian3.angleBetween(Cartesian3.subtract(startPoint, cornerPoint, scratch1), Cartesian3.subtract(endPoint, cornerPoint, scratch2));
var granularity = (cornerType.value === CornerType.BEVELED.value) ? 1 : Math.ceil(angle / CesiumMath.toRadians(5)) + 1;
@@ -100,7 +87,7 @@ define([
startPoint = Cartesian3.fromArray(calculatedPositions[1], leftEdge.length - 3, startPoint);
endPoint = Cartesian3.fromArray(calculatedPositions[0], 0, endPoint);
cornerPoint = Cartesian3.multiplyByScalar(Cartesian3.add(startPoint, endPoint, cornerPoint), 0.5, cornerPoint);
- var firstEndCap = computeRoundCorner(cornerPoint, startPoint, endPoint, CornerType.ROUNDED, false, ellipsoid);
+ var firstEndCap = computeRoundCorner(cornerPoint, startPoint, endPoint, CornerType.ROUNDED, false);
var length = calculatedPositions.length - 1;
var rightEdge = calculatedPositions[length - 1];
@@ -108,12 +95,12 @@ define([
startPoint = Cartesian3.fromArray(rightEdge, rightEdge.length - 3, startPoint);
endPoint = Cartesian3.fromArray(leftEdge, 0, endPoint);
cornerPoint = Cartesian3.multiplyByScalar(Cartesian3.add(startPoint, endPoint, cornerPoint), 0.5, cornerPoint);
- var lastEndCap = computeRoundCorner(cornerPoint, startPoint, endPoint, CornerType.ROUNDED, false, ellipsoid);
+ var lastEndCap = computeRoundCorner(cornerPoint, startPoint, endPoint, CornerType.ROUNDED, false);
return [firstEndCap, lastEndCap];
}
- function computeMiteredCorner (position, startPoint, leftCornerDirection, lastPoint, leftIsOutside, granularity, ellipsoid) {
+ function computeMiteredCorner (position, leftCornerDirection, lastPoint, leftIsOutside) {
var cornerPoint = scratch1;
if (leftIsOutside) {
cornerPoint = Cartesian3.add(position, leftCornerDirection, cornerPoint);
@@ -168,14 +155,22 @@ define([
}
};
+ function scaleToSurface(positions, ellipsoid) {
+ for(var i = 0; i < positions.length; i++) {
+ positions[i] = ellipsoid.scaleToGeodeticSurface(positions[i], positions[i]);
+ }
+ return positions;
+ }
+
/**
* @private
*/
CorridorGeometryLibrary.computePositions = function (params) {
var granularity = params.granularity;
var positions = params.positions;
- var width = params.width / 2;
var ellipsoid = params.ellipsoid;
+ positions = scaleToSurface(positions, ellipsoid);
+ var width = params.width / 2;
var cornerType = params.cornerType;
var saveAttributes = params.saveAttributes;
var normal = cartesian1;
@@ -219,7 +214,7 @@ define([
cornerDirection = Cartesian3.cross(cornerDirection, normal, cornerDirection);
cornerDirection = Cartesian3.cross(normal, cornerDirection, cornerDirection);
var scalar = width / Math.max(0.25, Cartesian3.magnitude(Cartesian3.cross(cornerDirection, backward, scratch1)));
- var leftIsOutside = angleIsGreaterThanPi(forward, backward, position, ellipsoid);
+ var leftIsOutside = PolylineVolumeGeometryLibrary.angleIsGreaterThanPi(forward, backward, position, ellipsoid);
cornerDirection = Cartesian3.multiplyByScalar(cornerDirection, scalar, cornerDirection);
if (leftIsOutside) {
rightPos = Cartesian3.add(position, cornerDirection, rightPos);
@@ -238,9 +233,9 @@ define([
leftPos = Cartesian3.add(rightPos, Cartesian3.multiplyByScalar(left, width * 2, leftPos), leftPos);
previousPos = Cartesian3.add(rightPos, Cartesian3.multiplyByScalar(left, width, previousPos), previousPos);
if (cornerType.value === CornerType.ROUNDED.value || cornerType.value === CornerType.BEVELED.value) {
- corners.push({leftPositions : computeRoundCorner(rightPos, startPoint, leftPos, cornerType, leftIsOutside, ellipsoid)});
+ corners.push({leftPositions : computeRoundCorner(rightPos, startPoint, leftPos, cornerType, leftIsOutside)});
} else {
- corners.push({leftPositions : computeMiteredCorner(position, startPoint, Cartesian3.negate(cornerDirection, cornerDirection), leftPos, leftIsOutside, granularity, ellipsoid)});
+ corners.push({leftPositions : computeMiteredCorner(position, Cartesian3.negate(cornerDirection, cornerDirection), leftPos, leftIsOutside)});
}
} else {
leftPos = Cartesian3.add(position, cornerDirection, leftPos);
@@ -259,9 +254,9 @@ define([
rightPos = Cartesian3.add(leftPos, Cartesian3.negate(Cartesian3.multiplyByScalar(left, width * 2, rightPos), rightPos), rightPos);
previousPos = Cartesian3.add(leftPos, Cartesian3.negate(Cartesian3.multiplyByScalar(left, width, previousPos), previousPos), previousPos);
if (cornerType.value === CornerType.ROUNDED.value || cornerType.value === CornerType.BEVELED.value) {
- corners.push({rightPositions : computeRoundCorner(leftPos, startPoint, rightPos, cornerType, leftIsOutside, ellipsoid)});
+ corners.push({rightPositions : computeRoundCorner(leftPos, startPoint, rightPos, cornerType, leftIsOutside)});
} else {
- corners.push({rightPositions : computeMiteredCorner(position, startPoint, cornerDirection, rightPos, leftIsOutside, granularity, ellipsoid)});
+ corners.push({rightPositions : computeMiteredCorner(position, cornerDirection, rightPos, leftIsOutside)});
}
}
backward = Cartesian3.negate(forward, backward);
diff --git a/Source/Core/GeometryAttributes.js b/Source/Core/GeometryAttributes.js
index b9e740ce5b46..75563221096a 100644
--- a/Source/Core/GeometryAttributes.js
+++ b/Source/Core/GeometryAttributes.js
@@ -11,7 +11,7 @@ define(['./defaultValue'], function(defaultValue) {
* into the vertex buffer for better rendering performance.
*
*
- * @alias VertexFormat
+ * @alias GeometryAttributes
* @constructor
*/
var GeometryAttributes = function(options) {
diff --git a/Source/Core/PolygonPipeline.js b/Source/Core/PolygonPipeline.js
index 43f0b48c97a0..dcf49e404d3d 100644
--- a/Source/Core/PolygonPipeline.js
+++ b/Source/Core/PolygonPipeline.js
@@ -387,7 +387,7 @@ define([
var after = getNextVertex(a1i, pArray, AFTER);
var s1 = Cartesian2.subtract(pArray[before].position, a1.position);
- var s2 = Cartesian2.subtract(pArray[after].position, a1.position);
+ var s2 = Cartesian2.subtract(pArray[after].position, a1.position);
var cut = Cartesian2.subtract(a2.position, a1.position);
// Convert to 3-dimensional so we can use cross product
@@ -515,7 +515,7 @@ define([
}
var s1 = Cartesian2.subtract(pArray[before].position, pArray[index].position);
- var s2 = Cartesian2.subtract(pArray[after].position, pArray[index].position);
+ var s2 = Cartesian2.subtract(pArray[after].position, pArray[index].position);
// Convert to 3-dimensional so we can use cross product
s1 = new Cartesian3(s1.x, s1.y, 0.0);
diff --git a/Source/Core/PolylinePipeline.js b/Source/Core/PolylinePipeline.js
index 8859d44504b6..abca38d11c6a 100644
--- a/Source/Core/PolylinePipeline.js
+++ b/Source/Core/PolylinePipeline.js
@@ -46,16 +46,20 @@ define([
var carto1 = new Cartographic();
var carto2 = new Cartographic();
var cartesian = new Cartesian3();
+ var scaleFirst = new Cartesian3();
+ var scaleLast = new Cartesian3();
var ellipsoidGeodesic = new EllipsoidGeodesic();
//Returns subdivided line scaled to ellipsoid surface starting at p1 and ending at p2.
//Result includes p1, but not include p2. This function is called for a sequence of line segments,
//and this prevents duplication of end point.
function generateCartesianArc(p1, p2, granularity, ellipsoid) {
- var separationAngle = Cartesian3.angleBetween(p1, p2);
+ var first = ellipsoid.scaleToGeodeticSurface(p1, scaleFirst);
+ var last = ellipsoid.scaleToGeodeticSurface(p2, scaleLast);
+ var separationAngle = Cartesian3.angleBetween(first, last);
var numPoints = Math.ceil(separationAngle/granularity);
var result = new Array(numPoints*3);
- var start = ellipsoid.cartesianToCartographic(p1, carto1);
- var end = ellipsoid.cartesianToCartographic(p2, carto2);
+ var start = ellipsoid.cartesianToCartographic(first, carto1);
+ var end = ellipsoid.cartesianToCartographic(last, carto2);
ellipsoidGeodesic.setEndPoints(start, end);
var surfaceDistanceBetweenPoints = ellipsoidGeodesic.getSurfaceDistance() / (numPoints);
diff --git a/Source/Core/PolylineVolumeGeometry.js b/Source/Core/PolylineVolumeGeometry.js
new file mode 100644
index 000000000000..70d315be8055
--- /dev/null
+++ b/Source/Core/PolylineVolumeGeometry.js
@@ -0,0 +1,260 @@
+/*global define*/
+define([
+ './defined',
+ './DeveloperError',
+ './Cartesian3',
+ './CornerType',
+ './ComponentDatatype',
+ './Ellipsoid',
+ './Geometry',
+ './GeometryPipeline',
+ './IndexDatatype',
+ './Math',
+ './PolygonPipeline',
+ './PolylineVolumeGeometryLibrary',
+ './PrimitiveType',
+ './defaultValue',
+ './BoundingSphere',
+ './BoundingRectangle',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './VertexFormat',
+ './WindingOrder'
+ ], function(
+ defined,
+ DeveloperError,
+ Cartesian3,
+ CornerType,
+ ComponentDatatype,
+ Ellipsoid,
+ Geometry,
+ GeometryPipeline,
+ IndexDatatype,
+ CesiumMath,
+ PolygonPipeline,
+ PolylineVolumeGeometryLibrary,
+ PrimitiveType,
+ defaultValue,
+ BoundingSphere,
+ BoundingRectangle,
+ GeometryAttribute,
+ GeometryAttributes,
+ VertexFormat,
+ WindingOrder) {
+ "use strict";
+
+ function computeAttributes(combinedPositions, shape, boundingRectangle, vertexFormat, ellipsoid) {
+ var attributes = new GeometryAttributes();
+ if (vertexFormat.position) {
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : combinedPositions
+ });
+ }
+ var shapeLength = shape.length;
+ var vertexCount = combinedPositions.length / 3;
+ var length = (vertexCount - shapeLength * 2) / (shapeLength * 2);
+ var firstEndIndices = PolygonPipeline.triangulate(shape);
+
+ var indicesCount = (length - 1) * (shapeLength) * 6 + firstEndIndices.length * 2;
+ var indices = IndexDatatype.createTypedArray(vertexCount, indicesCount);
+ var i, j;
+ var ll, ul, ur, lr;
+ var offset = shapeLength * 2;
+ var index = 0;
+ for (i = 0; i < length - 1; i++) {
+ for (j = 0; j < shapeLength - 1; j++) {
+ ll = j * 2 + i * shapeLength * 2;
+ lr = ll + offset;
+ ul = ll + 1;
+ ur = ul + offset;
+
+ indices[index++] = ul;
+ indices[index++] = ll;
+ indices[index++] = ur;
+ indices[index++] = ur;
+ indices[index++] = ll;
+ indices[index++] = lr;
+ }
+ ll = shapeLength * 2 - 2 + i * shapeLength * 2;
+ ul = ll + 1;
+ ur = ul + offset;
+ lr = ll + offset;
+
+ indices[index++] = ul;
+ indices[index++] = ll;
+ indices[index++] = ur;
+ indices[index++] = ur;
+ indices[index++] = ll;
+ indices[index++] = lr;
+ }
+
+ if (vertexFormat.st || vertexFormat.tangent || vertexFormat.binormal) { // st required for tangent/binormal calculation
+ var st = new Float32Array(vertexCount * 2);
+ var lengthSt = 1 / (length - 1);
+ var heightSt = 1 / (boundingRectangle.height);
+ var heightOffset = boundingRectangle.height / 2;
+ var s, t;
+ var stindex = 0;
+ for (i = 0; i < length; i++) {
+ s = i * lengthSt;
+ t = heightSt * (shape[0].y + heightOffset);
+ st[stindex++] = s;
+ st[stindex++] = t;
+ for (j = 1; j < shapeLength; j++) {
+ t = heightSt * (shape[j].y + heightOffset);
+ st[stindex++] = s;
+ st[stindex++] = t;
+ st[stindex++] = s;
+ st[stindex++] = t;
+ }
+ t = heightSt * (shape[0].y + heightOffset);
+ st[stindex++] = s;
+ st[stindex++] = t;
+ }
+ for (j = 0; j < shapeLength; j++) {
+ s = 0;
+ t = heightSt * (shape[j].y + heightOffset);
+ st[stindex++] = s;
+ st[stindex++] = t;
+ }
+ for (j = 0; j < shapeLength; j++) {
+ s = (length - 1) * lengthSt;
+ t = heightSt * (shape[j].y + heightOffset);
+ st[stindex++] = s;
+ st[stindex++] = t;
+ }
+
+ attributes.st = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.FLOAT,
+ componentsPerAttribute : 2,
+ values : new Float32Array(st)
+ });
+ }
+
+ var endOffset = vertexCount - shapeLength * 2;
+ for (i = 0; i < firstEndIndices.length; i += 3) {
+ var v0 = firstEndIndices[i] + endOffset;
+ var v1 = firstEndIndices[i + 1] + endOffset;
+ var v2 = firstEndIndices[i + 2] + endOffset;
+
+ indices[index++] = v0;
+ indices[index++] = v1;
+ indices[index++] = v2;
+ indices[index++] = v2 + shapeLength;
+ indices[index++] = v1 + shapeLength;
+ indices[index++] = v0 + shapeLength;
+ }
+
+ var geometry = new Geometry({
+ attributes : attributes,
+ indices : indices,
+ boundingSphere : BoundingSphere.fromVertices(combinedPositions),
+ primitiveType : PrimitiveType.TRIANGLES
+ });
+
+ if (vertexFormat.normal) {
+ geometry = GeometryPipeline.computeNormal(geometry);
+ }
+
+ if (vertexFormat.tangent || vertexFormat.binormal) {
+ geometry = GeometryPipeline.computeBinormalAndTangent(geometry);
+ if (!vertexFormat.tangent) {
+ geometry.attributes.tangent = undefined;
+ }
+ if (!vertexFormat.binormal) {
+ geometry.attributes.binormal = undefined;
+ }
+ if (!vertexFormat.st) {
+ geometry.attributes.st = undefined;
+ }
+ }
+
+ return geometry;
+ }
+
+ /**
+ * A description of a polyline with a volume (a 2D shape extruded along a polyline).
+ *
+ * @alias PolylineVolumeGeometry
+ * @constructor
+ *
+ * @param {Array} options.polylinePositions An array of {@link Cartesain3} positions that define the center of the polyline volume.
+ * @param {Number} options.shapePositions An array of {@link Cartesian2} positions that define the shape to be extruded along the polyline
+ * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference.
+ * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
+ * @param {Number} [options.height=0] The distance between the ellipsoid surface and the positions.
+ * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
+ * @param {Boolean} [options.cornerType = CornerType.ROUNDED] Determines the style of the corners.
+ *
+ * @exception {DeveloperError} options.polylinePositions is required.
+ * @exception {DeveloperError} options.shapePositions is required.
+ *
+ * @see PolylineVolumeGeometry#createGeometry
+ *
+ * @example
+ * var volume = new PolylineVolumeGeometry({
+ * vertexFormat : VertexFormat.POSITION_ONLY,
+ * polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(-72.0, 40.0),
+ * Cartographic.fromDegrees(-70.0, 35.0)
+ * ]),
+ * shapePositions : Shapes.compute2DCircle(100000.0)
+ * });
+ */
+ var PolylineVolumeGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+ var positions = options.polylinePositions;
+ if (!defined(positions)) {
+ throw new DeveloperError('options.polylinePositions is required.');
+ }
+ var shape = options.shapePositions;
+ if (!defined(shape)) {
+ throw new DeveloperError('options.shapePositions is required.');
+ }
+
+ this._positions = positions;
+ this._shape = shape;
+ this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
+ this._height = defaultValue(options.height, 0.0);
+ this._cornerType = defaultValue(options.cornerType, CornerType.ROUNDED);
+ this._vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
+ this._granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
+ this._workerName = 'createPolylineVolumeGeometry';
+ };
+
+ /**
+ * Computes the geometric representation of a polyline with a volume, including its vertices, indices, and a bounding sphere.
+ * @memberof PolylineVolumeGeometry
+ *
+ * @param {PolylineVolumeGeometry} polylineVolumeGeometry A description of the polyline volume.
+ *
+ * @returns {Geometry} The computed vertices and indices.
+ *
+ * @exception {DeveloperError} Count of unique polyline positions must be greater than 1.
+ * @exception {DeveloperError} Count of unique shape positions must be at least 3.
+ */
+ var brScratch = new BoundingRectangle();
+ PolylineVolumeGeometry.createGeometry = function(polylineVolumeGeometry) {
+ var positions = polylineVolumeGeometry._positions;
+ var cleanPositions = PolylineVolumeGeometryLibrary.removeDuplicatesFromPositions(positions, polylineVolumeGeometry._ellipsoid);
+ if (cleanPositions.length < 2) {
+ throw new DeveloperError('Count of unique polyline positions must be greater than 1.');
+ }
+ var shape2D = polylineVolumeGeometry._shape;
+ shape2D = PolylineVolumeGeometryLibrary.removeDuplicatesFromShape(shape2D);
+ if (shape2D.length < 3) {
+ throw new DeveloperError('Count of unique shape positions must be at least 3.');
+ }
+ if (PolygonPipeline.computeWindingOrder2D(shape2D).value === WindingOrder.CLOCKWISE.value) {
+ shape2D.reverse();
+ }
+ var boundingRectangle = BoundingRectangle.fromPoints(shape2D, brScratch);
+
+ var computedPositions = PolylineVolumeGeometryLibrary.computePositions(cleanPositions, shape2D, boundingRectangle, polylineVolumeGeometry, true);
+ return computeAttributes(computedPositions, shape2D, boundingRectangle, polylineVolumeGeometry._vertexFormat, polylineVolumeGeometry._ellipsoid);
+ };
+
+ return PolylineVolumeGeometry;
+});
\ No newline at end of file
diff --git a/Source/Core/PolylineVolumeGeometryLibrary.js b/Source/Core/PolylineVolumeGeometryLibrary.js
new file mode 100644
index 000000000000..a5960cb31abb
--- /dev/null
+++ b/Source/Core/PolylineVolumeGeometryLibrary.js
@@ -0,0 +1,419 @@
+/*global define*/
+define([
+ './defined',
+ './Cartesian2',
+ './Cartesian3',
+ './Cartesian4',
+ './Cartographic',
+ './CornerType',
+ './DeveloperError',
+ './EllipsoidTangentPlane',
+ './PolylinePipeline',
+ './Matrix3',
+ './Matrix4',
+ './Quaternion',
+ './Transforms',
+ './Math'
+ ], function(
+ defined,
+ Cartesian2,
+ Cartesian3,
+ Cartesian4,
+ Cartographic,
+ CornerType,
+ DeveloperError,
+ EllipsoidTangentPlane,
+ PolylinePipeline,
+ Matrix3,
+ Matrix4,
+ Quaternion,
+ Transforms,
+ CesiumMath) {
+ "use strict";
+
+ var scratch2Array = [new Cartesian3(), new Cartesian3()];
+ var scratchCartesian1 = new Cartesian3();
+ var scratchCartesian2 = new Cartesian3();
+ var scratchCartesian3 = new Cartesian3();
+ var scratchCartesian4 = new Cartesian3();
+ var scratchCartesian5 = new Cartesian3();
+ var scratchCartesian6 = new Cartesian3();
+ var scratchCartesian7 = new Cartesian3();
+ var scratchCartesian8 = new Cartesian3();
+ var scratchCartesian9 = new Cartesian3();
+
+ var scratch1 = new Cartesian3();
+ var scratch2 = new Cartesian3();
+
+ /**
+ * @private
+ */
+ var PolylineVolumeGeometryLibrary = {};
+
+ var cartographic = new Cartographic();
+ function scaleToSurface(positions, ellipsoid) {
+ var heights = new Array(positions.length);
+ for ( var i = 0; i < positions.length; i++) {
+ var pos = positions[i];
+ cartographic = ellipsoid.cartesianToCartographic(pos, cartographic);
+ heights[i] = cartographic.height;
+ positions[i] = ellipsoid.scaleToGeodeticSurface(pos, pos);
+ }
+ return heights;
+ }
+
+ function subdivideHeights(points, h0, h1, granularity) {
+ var p0 = points[0];
+ var p1 = points[1];
+ var angleBetween = Cartesian3.angleBetween(p0, p1);
+ var numPoints = Math.ceil(angleBetween / granularity);
+ var heights = new Array(numPoints);
+ var i;
+ if (h0 === h1) {
+ for (i = 0; i < numPoints; i++) {
+ heights[i] = h0;
+ }
+ heights.push(h1);
+ return heights;
+ }
+
+ var dHeight = h1 - h0;
+ var heightPerVertex = dHeight / (numPoints);
+
+ for (i = 1; i < numPoints; i++) {
+ var h = h0 + i * heightPerVertex;
+ heights[i] = h;
+ }
+
+ heights[0] = h0;
+ heights.push(h1);
+ return heights;
+ }
+
+ function computeRotationAngle(start, end, position, ellipsoid) {
+ var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid);
+ var next = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, start, nextScratch), nextScratch);
+ var prev = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, end, prevScratch), prevScratch);
+ var angle = Cartesian2.angleBetween(next, prev);
+
+ return (prev.x * next.y - prev.y * next.x >= 0.0) ? -angle : angle;
+ }
+
+ var negativeX = new Cartesian4(-1, 0, 0, 0);
+ var transform = new Matrix4();
+ var translation = new Matrix4();
+ var rotationZ = new Matrix3();
+ var scaleMatrix = Matrix3.IDENTITY.clone();
+ var westScratch = new Cartesian4();
+ var finalPosScratch = new Cartesian4();
+ var heightCartesian = new Cartesian3();
+ function addPosition(center, left, shape, finalPositions, ellipsoid, height, xScalar, repeat) {
+ var west = westScratch;
+ var finalPosition = finalPosScratch;
+ transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, transform);
+
+ west = Matrix4.multiplyByVector(transform, negativeX, west);
+ west = Cartesian3.normalize(west, west);
+ var angle = computeRotationAngle(west, left, center, ellipsoid);
+ rotationZ = Matrix3.fromRotationZ(angle, rotationZ);
+
+ heightCartesian.z = height;
+ transform = Matrix4.multiply(transform, Matrix4.fromRotationTranslation(rotationZ, heightCartesian, translation), transform);
+ var scale = scaleMatrix;
+ scale[0] = xScalar;
+
+ for ( var j = 0; j < repeat; j++) {
+ for ( var i = 0; i < shape.length; i += 3) {
+ finalPosition = Cartesian3.fromArray(shape, i, finalPosition);
+ finalPosition = Matrix3.multiplyByVector(scale, finalPosition, finalPosition);
+ finalPosition = Matrix4.multiplyByPoint(transform, finalPosition, finalPosition);
+ finalPositions.push(finalPosition.x, finalPosition.y, finalPosition.z);
+ }
+ }
+
+ return finalPositions;
+ }
+
+ var centerScratch = new Cartesian3();
+ function addPositions(centers, left, shape, finalPositions, ellipsoid, heights, xScalar) {
+ for ( var i = 0; i < centers.length; i += 3) {
+ var center = Cartesian3.fromArray(centers, i, centerScratch);
+ finalPositions = addPosition(center, left, shape, finalPositions, ellipsoid, heights[i / 3], xScalar, 1);
+ }
+ return finalPositions;
+ }
+
+ function convertShapeTo3DDuplicate(shape2D, boundingRectangle) { //orientate 2D shape to XZ plane center at (0, 0, 0), duplicate points
+ var length = shape2D.length;
+ var shape = new Array(length * 6);
+ var index = 0;
+ var xOffset = boundingRectangle.x + boundingRectangle.width / 2;
+ var yOffset = boundingRectangle.y + boundingRectangle.height / 2;
+
+ var point = shape2D[0];
+ shape[index++] = point.x - xOffset;
+ shape[index++] = 0.0;
+ shape[index++] = point.y - yOffset;
+ for ( var i = 1; i < length; i++) {
+ point = shape2D[i];
+ var x = point.x - xOffset;
+ var z = point.y - yOffset;
+ shape[index++] = x;
+ shape[index++] = 0.0;
+ shape[index++] = z;
+
+ shape[index++] = x;
+ shape[index++] = 0.0;
+ shape[index++] = z;
+ }
+ point = shape2D[0];
+ shape[index++] = point.x - xOffset;
+ shape[index++] = 0.0;
+ shape[index++] = point.y - yOffset;
+
+ return shape;
+ }
+
+ function convertShapeTo3D(shape2D, boundingRectangle) { //orientate 2D shape to XZ plane center at (0, 0, 0)
+ var length = shape2D.length;
+ var shape = new Array(length * 3);
+ var index = 0;
+ var xOffset = boundingRectangle.x + boundingRectangle.width / 2;
+ var yOffset = boundingRectangle.y + boundingRectangle.height / 2;
+
+ for ( var i = 0; i < length; i++) {
+ shape[index++] = shape2D[i].x - xOffset;
+ shape[index++] = 0;
+ shape[index++] = shape2D[i].y - yOffset;
+ }
+
+ return shape;
+ }
+
+ var quaterion = new Quaternion();
+ var startPointScratch = new Cartesian3();
+ var rotMatrix = new Matrix3();
+ function computeRoundCorner(pivot, startPoint, endPoint, cornerType, leftIsOutside, ellipsoid, finalPositions, shape, height, duplicatePoints) {
+ var angle = Cartesian3.angleBetween(Cartesian3.subtract(startPoint, pivot, scratch1), Cartesian3.subtract(endPoint, pivot, scratch2));
+ var granularity = (cornerType.value === CornerType.BEVELED.value) ? 0 : Math.ceil(angle / CesiumMath.toRadians(5));
+
+ var m;
+ if (leftIsOutside) {
+ m = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(pivot, angle / (granularity + 1), quaterion), rotMatrix);
+ } else {
+ m = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(Cartesian3.negate(pivot, scratch1), angle / (granularity + 1), quaterion), rotMatrix);
+ }
+
+ var left;
+ var surfacePoint;
+ startPoint = Cartesian3.clone(startPoint, startPointScratch);
+ if (granularity > 0) {
+ var repeat = duplicatePoints ? 2 : 1;
+ for ( var i = 0; i < granularity; i++) {
+ startPoint = Matrix3.multiplyByVector(m, startPoint, startPoint);
+ left = Cartesian3.subtract(startPoint, pivot, scratch1);
+ left = Cartesian3.normalize(left, left);
+ if (!leftIsOutside) {
+ left = Cartesian3.negate(left, left);
+ }
+ surfacePoint = ellipsoid.scaleToGeodeticSurface(startPoint, scratch2);
+ finalPositions = addPosition(surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, repeat);
+ }
+ } else {
+ left = Cartesian3.subtract(startPoint, pivot, scratch1);
+ left = Cartesian3.normalize(left, left);
+ if (!leftIsOutside) {
+ left = Cartesian3.negate(left, left);
+ }
+ surfacePoint = ellipsoid.scaleToGeodeticSurface(startPoint, scratch2);
+ finalPositions = addPosition(surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, 1);
+
+ endPoint = Cartesian3.clone(endPoint, startPointScratch);
+ left = Cartesian3.subtract(endPoint, pivot, scratch1);
+ left = Cartesian3.normalize(left, left);
+ if (!leftIsOutside) {
+ left = Cartesian3.negate(left, left);
+ }
+ surfacePoint = ellipsoid.scaleToGeodeticSurface(endPoint, scratch2);
+ finalPositions = addPosition(surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, 1);
+ }
+
+ return finalPositions;
+ }
+
+ PolylineVolumeGeometryLibrary.removeDuplicatesFromShape = function(shapePositions) {
+ var length = shapePositions.length;
+ var cleanedPositions = [];
+ for ( var i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) {
+ var v0 = shapePositions[i0];
+ var v1 = shapePositions[i1];
+
+ if (!Cartesian2.equals(v0, v1)) {
+ cleanedPositions.push(v1); // Shallow copy!
+ }
+ }
+
+ return cleanedPositions;
+ };
+
+ var nextScratch = new Cartesian3();
+ var prevScratch = new Cartesian3();
+ PolylineVolumeGeometryLibrary.angleIsGreaterThanPi = function(forward, backward, position, ellipsoid) {
+ var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid);
+ var next = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, forward, nextScratch), nextScratch);
+ var prev = tangentPlane.projectPointOntoPlane(Cartesian3.add(position, backward, prevScratch), prevScratch);
+
+ return ((prev.x * next.y) - (prev.y * next.x)) >= 0.0;
+ };
+
+ function latLonEquals(c0, c1) {
+ return ((CesiumMath.equalsEpsilon(c0.latitude, c1.latitude, CesiumMath.EPSILON6)) && (CesiumMath.equalsEpsilon(c0.longitude, c1.longitude, CesiumMath.EPSILON6)));
+ }
+ var carto0 = new Cartographic();
+ var carto1 = new Cartographic();
+ PolylineVolumeGeometryLibrary.removeDuplicatesFromPositions = function(positions, ellipsoid) {
+ var length = positions.length;
+ if (length < 2) {
+ return positions.slice(0);
+ }
+
+ var cleanedPositions = [];
+ cleanedPositions.push(positions[0]);
+
+ for (var i = 1; i < length; ++i) {
+ var v0 = positions[i - 1];
+ var v1 = positions[i];
+ var c0 = ellipsoid.cartesianToCartographic(v0, carto0);
+ var c1 = ellipsoid.cartesianToCartographic(v1, carto1);
+
+ if (!latLonEquals(c0, c1)) {
+ cleanedPositions.push(v1); // Shallow copy!
+ }
+ }
+
+ return cleanedPositions;
+ };
+
+ PolylineVolumeGeometryLibrary.computePositions = function(positions, shape2D, boundingRectangle, geometry, duplicatePoints) {
+ var ellipsoid = geometry._ellipsoid;
+ var heights = scaleToSurface(positions, ellipsoid);
+ var granularity = geometry._granularity;
+ var cornerType = geometry._cornerType;
+ var shapeForSides = duplicatePoints ? convertShapeTo3DDuplicate(shape2D, boundingRectangle) : convertShapeTo3D(shape2D, boundingRectangle);
+ var shapeForEnds = duplicatePoints ? convertShapeTo3D(shape2D, boundingRectangle) : undefined;
+ var heightOffset = boundingRectangle.height / 2;
+ var width = boundingRectangle.width / 2;
+ var length = positions.length;
+ var finalPositions = [];
+ var ends = duplicatePoints ? [] : undefined;
+
+ var forward = scratchCartesian1;
+ var backward = scratchCartesian2;
+ var cornerDirection = scratchCartesian3;
+ var surfaceNormal = scratchCartesian4;
+ var pivot = scratchCartesian5;
+ var start = scratchCartesian6;
+ var end = scratchCartesian7;
+ var left = scratchCartesian8;
+ var previousPosition = scratchCartesian9;
+
+ var position = positions[0];
+ var nextPosition = positions[1];
+ surfaceNormal = ellipsoid.geodeticSurfaceNormal(position, surfaceNormal);
+ forward = Cartesian3.subtract(nextPosition, position, forward);
+ forward = Cartesian3.normalize(forward, forward);
+ left = Cartesian3.cross(surfaceNormal, forward, left);
+ left = Cartesian3.normalize(left, left);
+ var h0 = heights[0];
+ var h1 = heights[1];
+ if (duplicatePoints) {
+ ends = addPosition(position, left, shapeForEnds, ends, ellipsoid, h0 + heightOffset, 1, 1);
+ }
+ previousPosition = Cartesian3.clone(position, previousPosition);
+ position = nextPosition;
+ backward = Cartesian3.negate(forward, backward);
+ var subdividedHeights;
+ var subdividedPositions;
+ for ( var i = 1; i < length - 1; i++) {
+ var repeat = duplicatePoints ? 2 : 1;
+ nextPosition = positions[i + 1];
+ forward = Cartesian3.subtract(nextPosition, position, forward);
+ forward = Cartesian3.normalize(forward, forward);
+ cornerDirection = Cartesian3.add(forward, backward, cornerDirection);
+ cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection);
+ surfaceNormal = ellipsoid.geodeticSurfaceNormal(position, surfaceNormal);
+ var doCorner = !Cartesian3.equalsEpsilon(Cartesian3.negate(cornerDirection, scratch1), surfaceNormal, CesiumMath.EPSILON2);
+ if (doCorner) {
+ cornerDirection = Cartesian3.cross(cornerDirection, surfaceNormal, cornerDirection);
+ cornerDirection = Cartesian3.cross(surfaceNormal, cornerDirection, cornerDirection);
+ cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection);
+ var scalar = 1 / Math.max(0.25, (Cartesian3.magnitude(Cartesian3.cross(cornerDirection, backward, scratch1))));
+ var leftIsOutside = PolylineVolumeGeometryLibrary.angleIsGreaterThanPi(forward, backward, position, ellipsoid);
+ if (leftIsOutside) {
+ pivot = Cartesian3.add(position, Cartesian3.multiplyByScalar(cornerDirection, scalar * width, cornerDirection), pivot);
+ start = Cartesian3.add(pivot, Cartesian3.multiplyByScalar(left, width, start), start);
+ scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
+ scratch2Array[1] = Cartesian3.clone(start, scratch2Array[1]);
+ subdividedHeights = subdivideHeights(scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity);
+ subdividedPositions = PolylinePipeline.scaleToSurface(scratch2Array);
+ finalPositions = addPositions(subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1);
+ left = Cartesian3.cross(surfaceNormal, forward, left);
+ left = Cartesian3.normalize(left, left);
+ end = Cartesian3.add(pivot, Cartesian3.multiplyByScalar(left, width, end), end);
+ if (cornerType.value === CornerType.ROUNDED.value || cornerType.value === CornerType.BEVELED.value) {
+ computeRoundCorner(pivot, start, end, cornerType, leftIsOutside, ellipsoid, finalPositions, shapeForSides, h1 + heightOffset, duplicatePoints);
+ } else {
+ cornerDirection = Cartesian3.negate(cornerDirection, cornerDirection);
+ finalPositions = addPosition(position, cornerDirection, shapeForSides, finalPositions, ellipsoid, h1 + heightOffset, scalar, repeat);
+ }
+ previousPosition = Cartesian3.clone(end, previousPosition);
+ } else {
+ pivot = Cartesian3.add(position, Cartesian3.multiplyByScalar(cornerDirection, scalar * width, cornerDirection), pivot);
+ start = Cartesian3.add(pivot, Cartesian3.multiplyByScalar(left, -width, start), start);
+ scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
+ scratch2Array[1] = Cartesian3.clone(start, scratch2Array[1]);
+ subdividedHeights = subdivideHeights(scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity);
+ subdividedPositions = PolylinePipeline.scaleToSurface(scratch2Array, granularity, ellipsoid);
+ finalPositions = addPositions(subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1);
+ left = Cartesian3.cross(surfaceNormal, forward, left);
+ left = Cartesian3.normalize(left, left);
+ end = Cartesian3.add(pivot, Cartesian3.multiplyByScalar(left, -width, end), end);
+ if (cornerType.value === CornerType.ROUNDED.value || cornerType.value === CornerType.BEVELED.value) {
+ computeRoundCorner(pivot, start, end, cornerType, leftIsOutside, ellipsoid, finalPositions, shapeForSides, h1 + heightOffset, duplicatePoints);
+ } else {
+ finalPositions = addPosition(position, cornerDirection, shapeForSides, finalPositions, ellipsoid, h1 + heightOffset, scalar, repeat);
+ }
+ previousPosition = Cartesian3.clone(end, previousPosition);
+ }
+ backward = Cartesian3.negate(forward, backward);
+ } else {
+ finalPositions = addPosition(previousPosition, left, shapeForSides, finalPositions, ellipsoid, h0 + heightOffset, 1, 1);
+ previousPosition = position;
+ }
+ h0 = h1;
+ h1 = heights[i + 1];
+ position = nextPosition;
+ }
+
+ scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]);
+ scratch2Array[1] = Cartesian3.clone(position, scratch2Array[1]);
+ subdividedHeights = subdivideHeights(scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity);
+ subdividedPositions = PolylinePipeline.scaleToSurface(scratch2Array, granularity, ellipsoid);
+ finalPositions = addPositions(subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1);
+ if (duplicatePoints) {
+ ends = addPosition(position, left, shapeForEnds, ends, ellipsoid, h1 + heightOffset, 1, 1);
+ }
+
+ length = finalPositions.length;
+ var posLength = duplicatePoints ? length + ends.length : length;
+ var combinedPositions = new Float64Array(posLength);
+ combinedPositions.set(finalPositions);
+ if (duplicatePoints) {
+ combinedPositions.set(ends, length);
+ }
+
+ return combinedPositions;
+ };
+
+ return PolylineVolumeGeometryLibrary;
+});
\ No newline at end of file
diff --git a/Source/Core/PolylineVolumeOutlineGeometry.js b/Source/Core/PolylineVolumeOutlineGeometry.js
new file mode 100644
index 000000000000..28fd389e53d8
--- /dev/null
+++ b/Source/Core/PolylineVolumeOutlineGeometry.js
@@ -0,0 +1,174 @@
+/*global define*/
+define([
+ './defined',
+ './DeveloperError',
+ './Cartesian3',
+ './CornerType',
+ './ComponentDatatype',
+ './Ellipsoid',
+ './Geometry',
+ './GeometryPipeline',
+ './IndexDatatype',
+ './Math',
+ './PolygonPipeline',
+ './PolylineVolumeGeometryLibrary',
+ './PrimitiveType',
+ './defaultValue',
+ './BoundingSphere',
+ './BoundingRectangle',
+ './GeometryAttribute',
+ './GeometryAttributes',
+ './WindingOrder'
+ ], function(
+ defined,
+ DeveloperError,
+ Cartesian3,
+ CornerType,
+ ComponentDatatype,
+ Ellipsoid,
+ Geometry,
+ GeometryPipeline,
+ IndexDatatype,
+ CesiumMath,
+ PolygonPipeline,
+ PolylineVolumeGeometryLibrary,
+ PrimitiveType,
+ defaultValue,
+ BoundingSphere,
+ BoundingRectangle,
+ GeometryAttribute,
+ GeometryAttributes,
+ WindingOrder) {
+ "use strict";
+
+ function computeAttributes(positions, shape, boundingRectangle, ellipsoid) {
+ var attributes = new GeometryAttributes();
+ attributes.position = new GeometryAttribute({
+ componentDatatype : ComponentDatatype.DOUBLE,
+ componentsPerAttribute : 3,
+ values : positions
+ });
+
+ var shapeLength = shape.length;
+ var vertexCount = attributes.position.values.length / 3;
+ var positionLength = positions.length / 3;
+ var shapeCount = positionLength / shapeLength;
+ var indices = IndexDatatype.createTypedArray(vertexCount, 2 * shapeLength * (shapeCount + 1));
+ var i, j;
+ var index = 0;
+ i = 0;
+ var offset = i * shapeLength;
+ for (j = 0; j < shapeLength - 1; j++) {
+ indices[index++] = j + offset;
+ indices[index++] = j + offset + 1;
+ }
+ indices[index++] = shapeLength - 1 + offset;
+ indices[index++] = offset;
+
+ i = shapeCount - 1;
+ offset = i * shapeLength;
+ for (j = 0; j < shapeLength - 1; j++) {
+ indices[index++] = j + offset;
+ indices[index++] = j + offset + 1;
+ }
+ indices[index++] = shapeLength - 1 + offset;
+ indices[index++] = offset;
+
+ for (i = 0; i < shapeCount - 1; i++) {
+ var firstOffset = shapeLength * i;
+ var secondOffset = firstOffset + shapeLength;
+ for (j = 0; j < shapeLength; j++) {
+ indices[index++] = j + firstOffset;
+ indices[index++] = j + secondOffset;
+ }
+ }
+
+ var geometry = new Geometry({
+ attributes : attributes,
+ indices : IndexDatatype.createTypedArray(vertexCount, indices),
+ boundingSphere : BoundingSphere.fromVertices(positions),
+ primitiveType : PrimitiveType.LINES
+ });
+
+ return geometry;
+ }
+
+ /**
+ * A description of a polyline with a volume (a 2D shape extruded along a polyline).
+ *
+ * @alias PolylineVolumeOutlineGeometry
+ * @constructor
+ *
+ * @param {Array} options.polylinePositions An array of {Cartesain3} positions that define the center of the polyline volume.
+ * @param {Number} options.shapePositions An array of {Cartesian2} positions that define the shape to be extruded along the polyline
+ * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference.
+ * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer.
+ * @param {Boolean} [options.cornerType = CornerType.ROUNDED] Determines the style of the corners.
+ *
+ * @exception {DeveloperError} options.polylinePositions is required.
+ * @exception {DeveloperError} options.shapePositions is required.
+ *
+ * @see PolylineVolumeOutlineGeometry#createGeometry
+ *
+ * @example
+ * var volumeOutline = new PolylineVolumeOutlineGeometry({
+ * polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ * Cartographic.fromDegrees(-72.0, 40.0),
+ * Cartographic.fromDegrees(-70.0, 35.0)
+ * ]),
+ * shapePositions : Shapes.compute2DCircle(100000.0)
+ * });
+ */
+ var PolylineVolumeOutlineGeometry = function(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+ var positions = options.polylinePositions;
+ if (!defined(positions)) {
+ throw new DeveloperError('options.polylinePositions is required.');
+ }
+ var shape = options.shapePositions;
+ if (!defined(shape)) {
+ throw new DeveloperError('options.shapePositions is required.');
+ }
+
+ this._positions = positions;
+ this._shape = shape;
+ this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
+ this._cornerType = defaultValue(options.cornerType, CornerType.ROUNDED);
+ this._granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE);
+ this._workerName = 'createPolylineVolumeOutlineGeometry';
+ };
+
+ /**
+ * Computes the geometric representation of the outline of a polyline with a volume, including its vertices, indices, and a bounding sphere.
+ * @memberof PolylineVolumeOutlineGeometry
+ *
+ * @param {PolylineVolumeOutlineGeometry} polylineVolumeOutlineGeometry A description of the polyline volume outline.
+ *
+ * @returns {Geometry} The computed vertices and indices.
+ *
+ * @exception {DeveloperError} Count of unique polyline positions must be greater than 1.
+ * @exception {DeveloperError} Count of unique shape positions must be at least 3.
+ */
+ var brScratch = new BoundingRectangle();
+ PolylineVolumeOutlineGeometry.createGeometry = function(polylineVolumeOutlineGeometry) {
+ var positions = polylineVolumeOutlineGeometry._positions;
+ var cleanPositions = PolylineVolumeGeometryLibrary.removeDuplicatesFromPositions(positions, polylineVolumeOutlineGeometry._ellipsoid);
+ if (cleanPositions.length < 2) {
+ throw new DeveloperError('Count of unique polyline positions must be greater than 1.');
+ }
+ var shape2D = polylineVolumeOutlineGeometry._shape;
+ shape2D = PolylineVolumeGeometryLibrary.removeDuplicatesFromShape(shape2D);
+ if (shape2D.length < 3) {
+ throw new DeveloperError('Count of unique shape positions must be at least 3.');
+ }
+ if (PolygonPipeline.computeWindingOrder2D(shape2D).value === WindingOrder.CLOCKWISE.value) {
+ shape2D.reverse();
+ }
+ var boundingRectangle = BoundingRectangle.fromPoints(shape2D, brScratch);
+
+ var computedPositions = PolylineVolumeGeometryLibrary.computePositions(cleanPositions, shape2D, boundingRectangle, polylineVolumeOutlineGeometry, false);
+ return computeAttributes(computedPositions, shape2D, boundingRectangle, polylineVolumeOutlineGeometry._ellipsoid);
+ };
+
+ return PolylineVolumeOutlineGeometry;
+});
\ No newline at end of file
diff --git a/Source/Core/Shapes.js b/Source/Core/Shapes.js
index e65384b7a407..152d921302d3 100644
--- a/Source/Core/Shapes.js
+++ b/Source/Core/Shapes.js
@@ -4,6 +4,7 @@ define([
'./defined',
'./DeveloperError',
'./Math',
+ './Cartesian2',
'./Cartesian3',
'./Quaternion',
'./Matrix3'
@@ -12,6 +13,7 @@ define([
defined,
DeveloperError,
CesiumMath,
+ Cartesian2,
Cartesian3,
Quaternion,
Matrix3) {
@@ -227,6 +229,29 @@ define([
ellipsePts.push(Cartesian3.clone(ellipsePts[0])); // Duplicates first and last point for polyline
return ellipsePts;
+ },
+
+ /**
+ * Computes a 2D circle about the origin.
+ *
+ * @param {Number} [radius = 1.0] The radius of the circle
+ * @param {Number} [granularity = Cesium.RADIANS_PER_DEGREE*2] The radius of the circle
+ *
+ * @returns The set of points that form the ellipse's boundary.
+ *
+ * @example
+ * var circle = Shapes.compute2DCircle(100000.0);
+ */
+ compute2DCircle : function(radius, granularity) {
+ radius = defaultValue(radius, 1.0);
+ granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE*2);
+ var positions = [];
+ var theta = CesiumMath.toRadians(1.0);
+ var posCount = Math.PI*2/theta;
+ for (var i = 0; i < posCount; i++) {
+ positions.push(new Cartesian2(radius * Math.cos(theta * i), radius * Math.sin(theta * i)));
+ }
+ return positions;
}
};
diff --git a/Source/Workers/createPolylineVolumeGeometry.js b/Source/Workers/createPolylineVolumeGeometry.js
new file mode 100644
index 000000000000..68bbe1bf4e83
--- /dev/null
+++ b/Source/Workers/createPolylineVolumeGeometry.js
@@ -0,0 +1,28 @@
+/*global define*/
+define([
+ '../Core/PolylineVolumeGeometry',
+ '../Core/Ellipsoid',
+ '../Scene/PrimitivePipeline',
+ './createTaskProcessorWorker'
+ ], function(
+ PolylineVolumeGeometry,
+ Ellipsoid,
+ PrimitivePipeline,
+ createTaskProcessorWorker) {
+ "use strict";
+
+ function createPolylineVolumeGeometry(parameters, transferableObjects) {
+ var polylineVolumeGeometry = parameters.geometry;
+ polylineVolumeGeometry._ellipsoid = Ellipsoid.clone(polylineVolumeGeometry._ellipsoid);
+
+ var geometry = PolylineVolumeGeometry.createGeometry(polylineVolumeGeometry);
+ PrimitivePipeline.transferGeometry(geometry, transferableObjects);
+
+ return {
+ geometry : geometry,
+ index : parameters.index
+ };
+ }
+
+ return createTaskProcessorWorker(createPolylineVolumeGeometry);
+});
diff --git a/Source/Workers/createPolylineVolumeOutlineGeometry.js b/Source/Workers/createPolylineVolumeOutlineGeometry.js
new file mode 100644
index 000000000000..7d86c89a3dd5
--- /dev/null
+++ b/Source/Workers/createPolylineVolumeOutlineGeometry.js
@@ -0,0 +1,28 @@
+/*global define*/
+define([
+ '../Core/PolylineVolumeOutlineGeometry',
+ '../Core/Ellipsoid',
+ '../Scene/PrimitivePipeline',
+ './createTaskProcessorWorker'
+ ], function(
+ PolylineVolumeOutlineGeometry,
+ Ellipsoid,
+ PrimitivePipeline,
+ createTaskProcessorWorker) {
+ "use strict";
+
+ function createPolylineVolumeOutlineGeometry(parameters, transferableObjects) {
+ var polylineVolumeOutlineGeometry = parameters.geometry;
+ polylineVolumeOutlineGeometry._ellipsoid = Ellipsoid.clone(polylineVolumeOutlineGeometry._ellipsoid);
+
+ var geometry = PolylineVolumeOutlineGeometry.createGeometry(polylineVolumeOutlineGeometry);
+ PrimitivePipeline.transferGeometry(geometry, transferableObjects);
+
+ return {
+ geometry : geometry,
+ index : parameters.index
+ };
+ }
+
+ return createTaskProcessorWorker(createPolylineVolumeOutlineGeometry);
+});
diff --git a/Specs/Core/PolylineVolumeGeometrySpec.js b/Specs/Core/PolylineVolumeGeometrySpec.js
new file mode 100644
index 000000000000..a62cea5b6bcd
--- /dev/null
+++ b/Specs/Core/PolylineVolumeGeometrySpec.js
@@ -0,0 +1,182 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/PolylineVolumeGeometry',
+ 'Core/CornerType',
+ 'Core/Cartesian2',
+ 'Core/Cartesian3',
+ 'Core/Cartographic',
+ 'Core/Ellipsoid',
+ 'Core/Math',
+ 'Core/VertexFormat'
+ ], function(
+ PolylineVolumeGeometry,
+ CornerType,
+ Cartesian2,
+ Cartesian3,
+ Cartographic,
+ Ellipsoid,
+ CesiumMath,
+ VertexFormat) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+ var shape;
+
+ beforeAll(function() {
+ shape = [new Cartesian2(-10000, -10000), new Cartesian2(10000, -10000), new Cartesian2(10000, 10000), new Cartesian2(-10000, 10000)];
+ });
+
+ it('throws without polyline positions', function() {
+ expect(function() {
+ return new PolylineVolumeGeometry({});
+ }).toThrow();
+ });
+
+ it('throws without shape positions', function() {
+ expect(function() {
+ return new PolylineVolumeGeometry({
+ polylinePositions: [new Cartesian3()]
+ });
+ }).toThrow();
+ });
+
+ it('throws without 2 unique polyline positions', function() {
+ expect(function() {
+ return PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ polylinePositions: [new Cartesian3()],
+ shapePositions: shape
+ }));
+ }).toThrow();
+ });
+
+ it('throws without 3 unique shape positions', function() {
+ expect(function() {
+ return PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ polylinePositions: [Cartesian3.UNIT_X, Cartesian3.UNIT_Y],
+ shapePositions: [Cartesian2.UNIT_X, Cartesian2.UNIT_X, Cartesian2.UNIT_X]
+ }));
+ }).toThrow();
+ });
+
+ it('computes positions', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -35.0)
+ ]),
+ cornerType: CornerType.MITERED,
+ shapePositions: shape
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * (4 * 2 + 4 * 2 * 6));
+ expect(m.indices.length).toEqual(3 * 10 * 2 + 24 * 3);
+ });
+
+ it('computes positions, clockwise shape', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -35.0)
+ ]),
+ cornerType: CornerType.MITERED,
+ shapePositions: shape.reverse()
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * (4 * 2 + 4 * 2 * 6));
+ expect(m.indices.length).toEqual(3 * 10 * 2 + 24 * 3);
+ });
+
+ it('compute all vertex attributes', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ vertexFormat : VertexFormat.ALL,
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -35.0)
+ ]),
+ cornerType: CornerType.MITERED,
+ shapePositions: shape
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * (4 * 2 + 4 * 2 * 6));
+ expect(m.attributes.st.values.length).toEqual(2 * (4 * 2 + 4 * 2 * 6));
+ expect(m.attributes.normal.values.length).toEqual(3 * (4 * 2 + 4 * 2 * 6));
+ expect(m.attributes.tangent.values.length).toEqual(3 * (4 * 2 + 4 * 2 * 6));
+ expect(m.attributes.binormal.values.length).toEqual(3 * (4 * 2 + 4 * 2 * 6));
+ expect(m.indices.length).toEqual(3 * 10 * 2 + 24 * 3);
+ });
+
+ it('computes right turn', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -31.0),
+ Cartographic.fromDegrees(91.0, -31.0)
+ ]),
+ cornerType: CornerType.MITERED,
+ shapePositions: shape
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * (4 * 2 + 4 * 2 * 6));
+ expect(m.indices.length).toEqual(3 * 10 * 2 + 24 * 3);
+ });
+
+ it('computes left turn', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -31.0),
+ Cartographic.fromDegrees(89.0, -31.0)
+ ]),
+ cornerType: CornerType.MITERED,
+ shapePositions: shape
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * (4 * 2 + 4 * 2 * 6));
+ expect(m.indices.length).toEqual(3 * 10 * 2 + 24 * 3);
+ });
+
+ it('computes with rounded corners', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -31.0),
+ Cartographic.fromDegrees(89.0, -31.0),
+ Cartographic.fromDegrees(89.0, -32.0)
+ ]),
+ cornerType: CornerType.ROUNDED,
+ shapePositions: shape
+ }));
+
+ var corners = 90/5 * 2;
+ expect(m.attributes.position.values.length).toEqual(3 * (corners * 4 * 2 * 2 + 4 * 2 * 9));
+ expect(m.indices.length).toEqual(3 * (corners * 4 * 2 * 2 + 4 * 7 * 2 + 4));
+ });
+
+ it('computes with beveled corners', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeGeometry.createGeometry(new PolylineVolumeGeometry({
+ vertexFormat : VertexFormat.POSITION_ONLY,
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -31.0),
+ Cartographic.fromDegrees(89.0, -31.0),
+ Cartographic.fromDegrees(89.0, -32.0)
+ ]),
+ cornerType: CornerType.BEVELED,
+ shapePositions: shape
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * (2 * 8 + 4 * 2 * 9));
+ expect(m.indices.length).toEqual(3 * (8 * 2 + 4 * 7 * 2 + 4));
+ });
+});
diff --git a/Specs/Core/PolylineVolumeOutlineGeometrySpec.js b/Specs/Core/PolylineVolumeOutlineGeometrySpec.js
new file mode 100644
index 000000000000..550d77acd4e3
--- /dev/null
+++ b/Specs/Core/PolylineVolumeOutlineGeometrySpec.js
@@ -0,0 +1,155 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/PolylineVolumeOutlineGeometry',
+ 'Core/CornerType',
+ 'Core/Cartesian2',
+ 'Core/Cartesian3',
+ 'Core/Cartographic',
+ 'Core/Ellipsoid',
+ 'Core/Math'
+ ], function(
+ PolylineVolumeOutlineGeometry,
+ CornerType,
+ Cartesian2,
+ Cartesian3,
+ Cartographic,
+ Ellipsoid,
+ CesiumMath) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ var shape;
+
+ beforeAll(function() {
+ shape = [new Cartesian2(-10000, -10000), new Cartesian2(10000, -10000), new Cartesian2(10000, 10000), new Cartesian2(-10000, 10000)];
+ });
+
+ it('throws without polyline positions', function() {
+ expect(function() {
+ return new PolylineVolumeOutlineGeometry({});
+ }).toThrow();
+ });
+
+ it('throws without shape positions', function() {
+ expect(function() {
+ return new PolylineVolumeOutlineGeometry({
+ polylinePositions: [new Cartesian3()]
+ });
+ }).toThrow();
+ });
+
+ it('throws without 2 unique polyline positions', function() {
+ expect(function() {
+ return PolylineVolumeOutlineGeometry.createGeometry(new PolylineVolumeOutlineGeometry({
+ polylinePositions: [new Cartesian3()],
+ shapePositions: shape
+ }));
+ }).toThrow();
+ });
+
+ it('throws without 3 unique shape positions', function() {
+ expect(function() {
+ return PolylineVolumeOutlineGeometry.createGeometry(new PolylineVolumeOutlineGeometry({
+ polylinePositions: [Cartesian3.UNIT_X, Cartesian3.UNIT_Y],
+ shapePositions: [Cartesian2.UNIT_X, Cartesian2.UNIT_X, Cartesian2.UNIT_X]
+ }));
+ }).toThrow();
+ });
+
+ it('computes positions', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeOutlineGeometry.createGeometry(new PolylineVolumeOutlineGeometry({
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -35.0)
+ ]),
+ shapePositions: shape,
+ cornerType: CornerType.MITERED
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * 6 * 4);
+ expect(m.indices.length).toEqual(2 * 24 + 8);
+ });
+
+ it('computes positions, clockwise shape', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeOutlineGeometry.createGeometry(new PolylineVolumeOutlineGeometry({
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -35.0)
+ ]),
+ shapePositions: shape.reverse(),
+ cornerType: CornerType.MITERED
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * 6 * 4);
+ expect(m.indices.length).toEqual(2 * 24 + 8);
+ });
+
+ it('computes right turn', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeOutlineGeometry.createGeometry(new PolylineVolumeOutlineGeometry({
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -31.0),
+ Cartographic.fromDegrees(91.0, -31.0)
+ ]),
+ cornerType: CornerType.MITERED,
+ shapePositions: shape
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * 5 * 4);
+ expect(m.indices.length).toEqual(2 * 24);
+ });
+
+ it('computes left turn', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeOutlineGeometry.createGeometry(new PolylineVolumeOutlineGeometry({
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -31.0),
+ Cartographic.fromDegrees(89.0, -31.0)
+ ]),
+ cornerType: CornerType.MITERED,
+ shapePositions: shape
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * 5 * 4);
+ expect(m.indices.length).toEqual(2 * 24);
+ });
+
+ it('computes with rounded corners', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeOutlineGeometry.createGeometry(new PolylineVolumeOutlineGeometry({
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -31.0),
+ Cartographic.fromDegrees(89.0, -31.0),
+ Cartographic.fromDegrees(89.0, -32.0)
+ ]),
+ cornerType: CornerType.ROUNDED,
+ shapePositions: shape
+ }));
+
+ var corners = 90/5*2;
+ expect(m.attributes.position.values.length).toEqual(3 * (corners * 4 + 7 * 4));
+ expect(m.indices.length).toEqual(2 * (corners * 4 + 6 * 4 + 8));
+ });
+
+ it('computes with beveled corners', function() {
+ var ellipsoid = Ellipsoid.WGS84;
+ var m = PolylineVolumeOutlineGeometry.createGeometry(new PolylineVolumeOutlineGeometry({
+ polylinePositions : ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(90.0, -30.0),
+ Cartographic.fromDegrees(90.0, -31.0),
+ Cartographic.fromDegrees(89.0, -31.0),
+ Cartographic.fromDegrees(89.0, -32.0)
+ ]),
+ cornerType: CornerType.BEVELED,
+ shapePositions: shape
+ }));
+
+ expect(m.attributes.position.values.length).toEqual(3 * 20 * 2);
+ expect(m.indices.length).toEqual(2 * 20 * 2 + 8);
+ });
+});
\ No newline at end of file
diff --git a/Specs/Core/ShapesSpec.js b/Specs/Core/ShapesSpec.js
index 39fe89161dfe..9f14705da045 100644
--- a/Specs/Core/ShapesSpec.js
+++ b/Specs/Core/ShapesSpec.js
@@ -119,4 +119,18 @@ defineSuite([
Shapes.computeEllipseBoundary(ellipsoid, center, 1.0, 1.0, 0, -1.0);
}).toThrow();
});
+
+ it('compute2DCircle returns a unit circle by default', function() {
+ var points = Shapes.compute2DCircle();
+ expect(points.length).toBeGreaterThan(0);
+ expect(points[0].x).toEqual(1);
+ expect(points[0].y).toEqual(0);
+ });
+
+ it('compute2DCircle returns an circle with radius 5', function() {
+ var points = Shapes.compute2DCircle(5.0);
+ expect(points.length).toBeGreaterThan(0);
+ expect(points[0].x).toEqual(5);
+ expect(points[0].y).toEqual(0);
+ });
});
diff --git a/Specs/Scene/GeometryRenderingSpec.js b/Specs/Scene/GeometryRenderingSpec.js
index bd997dc39e59..5445d23ab8f7 100644
--- a/Specs/Scene/GeometryRenderingSpec.js
+++ b/Specs/Scene/GeometryRenderingSpec.js
@@ -21,11 +21,13 @@ defineSuite([
'Core/ColorGeometryInstanceAttribute',
'Core/GeometryInstanceAttribute',
'Core/ComponentDatatype',
+ 'Core/Cartesian2',
'Core/Cartesian3',
'Core/Matrix4',
'Core/Extent',
'Core/Ellipsoid',
'Core/PrimitiveType',
+ 'Core/PolylineVolumeGeometry',
'Core/Transforms',
'Core/Cartographic',
'Core/BoundingSphere',
@@ -66,11 +68,13 @@ defineSuite([
ColorGeometryInstanceAttribute,
GeometryInstanceAttribute,
ComponentDatatype,
+ Cartesian2,
Cartesian3,
Matrix4,
Extent,
Ellipsoid,
PrimitiveType,
+ PolylineVolumeGeometry,
Transforms,
Cartographic,
BoundingSphere,
@@ -1250,6 +1254,98 @@ defineSuite([
});
}, 'WebGL');
+ describe('PolylineVolumeGeometry', function() {
+ var instance;
+ var geometryHeight;
+ var positions;
+ var shape;
+ beforeAll(function() {
+ positions = ellipsoid.cartographicArrayToCartesianArray([
+ Cartographic.fromDegrees(0.0, -1.0),
+ Cartographic.fromDegrees(0.0, 1.0)
+ ]);
+ shape = [new Cartesian2(-100000, -100000), new Cartesian2(100000, -100000), new Cartesian2(100000, 100000), new Cartesian2(-100000, 100000)];
+ geometryHeight = 150000.0;
+ instance = new GeometryInstance({
+ geometry : new PolylineVolumeGeometry({
+ vertexFormat : PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
+ ellipsoid : ellipsoid,
+ polylinePositions: positions,
+ shapePositions: shape,
+ cornerType: CornerType.MITERED,
+ height: geometryHeight
+ }),
+ id : 'polylineVolume',
+ attributes : {
+ color : new ColorGeometryInstanceAttribute(1.0, 1.0, 0.0, 1.0)
+ }
+ });
+ });
+
+ it('3D', function() {
+ render3D(instance);
+ });
+
+ it('Columbus view', function() {
+ renderCV(instance);
+ });
+
+ it('2D', function() {
+ render2D(instance);
+ });
+
+ it('pick', function() {
+ pickGeometry(instance);
+ });
+
+ it('async', function() {
+ renderAsync(instance);
+ });
+
+ it('renders bottom', function() {
+ var afterView = function(frameState, primitive) {
+ var height = geometryHeight * 0.5;
+ var transform = Matrix4.multiplyByTranslation(
+ Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center),
+ new Cartesian3(0.0, 0.0, height));
+ frameState.camera.controller.rotateDown(CesiumMath.PI, transform);
+ };
+ render3D(instance, afterView);
+ });
+
+ it('renders north wall', function() {
+ var afterView = function(frameState, primitive) {
+ var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center);
+ frameState.camera.controller.rotateDown(-CesiumMath.PI_OVER_TWO, transform);
+ };
+ render3D(instance, afterView);
+ });
+
+ it('renders south wall', function() {
+ var afterView = function(frameState, primitive) {
+ var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center);
+ frameState.camera.controller.rotateDown(CesiumMath.PI_OVER_TWO, transform);
+ };
+ render3D(instance, afterView);
+ });
+
+ it('renders west wall', function() {
+ var afterView = function(frameState, primitive) {
+ var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center);
+ frameState.camera.controller.rotateRight(-CesiumMath.PI_OVER_TWO, transform);
+ };
+ render3D(instance, afterView);
+ });
+
+ it('renders east wall', function() {
+ var afterView = function(frameState, primitive) {
+ var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center);
+ frameState.camera.controller.rotateRight(CesiumMath.PI_OVER_TWO, transform);
+ };
+ render3D(instance, afterView);
+ });
+ }, 'WebGL');
+
describe('SimplePolylineGeometry', function() {
var instance;