diff --git a/Apps/Sandcastle/gallery/Geometry and Appearances.html b/Apps/Sandcastle/gallery/Geometry and Appearances.html index f68921c0bb3c..c2bb668e7846 100644 --- a/Apps/Sandcastle/gallery/Geometry and Appearances.html +++ b/Apps/Sandcastle/gallery/Geometry and Appearances.html @@ -778,50 +778,81 @@ }) })); - var corridor = new Cesium.CorridorGeometry({ - positions : ellipsoid.cartographicArrayToCartesianArray([ - Cesium.Cartographic.fromDegrees(-120.0, 45.0), - Cesium.Cartographic.fromDegrees(-125.0, 50.0), - Cesium.Cartographic.fromDegrees(-125.0, 55.0) - ]), - width : 100000, - vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT + positions = ellipsoid.cartographicArrayToCartesianArray([ + Cesium.Cartographic.fromDegrees(-120.0, 45.0), + Cesium.Cartographic.fromDegrees(-125.0, 50.0), + Cesium.Cartographic.fromDegrees(-125.0, 55.0) + ]); + var width = 100000; + + var corridor = new Cesium.GeometryInstance({ + geometry: new Cesium.CorridorGeometry({ + positions : positions, + width : width, + vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT + }), + attributes : { + color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 1.0})) + } }); - var corridorInstance = new Cesium.GeometryInstance({ - geometry: corridor, + var extrudedCorridor = new Cesium.GeometryInstance({ + geometry: new Cesium.CorridorGeometry({ + positions : positions, + width : width, + vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT, + height: 300000, + extrudedHeight: 400000 + }), attributes : { color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.7})) } }); - var extrudedCorridor = new Cesium.CorridorGeometry({ - positions : ellipsoid.cartographicArrayToCartesianArray([ - Cesium.Cartographic.fromDegrees(-120.0, 45.0), - Cesium.Cartographic.fromDegrees(-125.0, 50.0), - Cesium.Cartographic.fromDegrees(-125.0, 55.0) - ]), - width : 100000, - vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT, - height: 300000, - extrudedHeight: 400000 + var corridorOutline = new Cesium.GeometryInstance({ + geometry: new Cesium.CorridorOutlineGeometry( { + positions: positions, + width: width, + height: 700000 + }), + attributes: { + color : solidWhite + } }); - var extrudedCorridorInstance = new Cesium.GeometryInstance({ - geometry: extrudedCorridor, + var corridorFill = new Cesium.GeometryInstance({ + geometry: new Cesium.CorridorGeometry({ + positions : positions, + width : width, + vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT, + height: 700000 + }), attributes : { color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.7})) } }); primitives.add(new Cesium.Primitive({ - geometryInstances : [corridorInstance, extrudedCorridorInstance], + geometryInstances : [corridor, extrudedCorridor, corridorFill], appearance : new Cesium.PerInstanceColorAppearance({ translucent : true, closed : true, faceForward : true }) })); + + primitives.add(new Cesium.Primitive({ + geometryInstances : corridorOutline, + appearance : new Cesium.PerInstanceColorAppearance({ + flat : true, + renderState : { + depthTest : { + enabled : true + }, + lineWidth : Math.min(4.0, scene.getContext().getMaximumAliasedLineWidth()) + } + }) + })); Sandcastle.finishedLoading(); }); diff --git a/CHANGES.md b/CHANGES.md index d3105e56c035..f85e84649901 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,9 @@ Change Log Beta Releases ------------- +### b21 - 2013-10-01 +* Added `CorridorOutlineGeometry`. + ### b20 - 2013-09-03 _This releases fixes 2D and other issues with Chrome 29.0.1547.57 ([#1002](https://github.com/AnalyticalGraphicsInc/cesium/issues/1002) and [#1047](https://github.com/AnalyticalGraphicsInc/cesium/issues/1047))._ diff --git a/Source/Core/CornerType.js b/Source/Core/CornerType.js index 63eeb7ccc65e..8029fa6ff164 100644 --- a/Source/Core/CornerType.js +++ b/Source/Core/CornerType.js @@ -7,6 +7,11 @@ define(['../Core/Enumeration'], function(Enumeration) { */ var CornerType = { /** + * ___ + * ( ___ + * | | + * + * Corner is circular. * @type {Enumeration} * @constant * @default 0 @@ -14,6 +19,11 @@ define(['../Core/Enumeration'], function(Enumeration) { ROUNDED : new Enumeration(0, 'ROUNDED'), /** + * ______ + * | ___ + * | | + * + * Corner point is the intersection of adjacent edges. * @type {Enumeration} * @constant * @default 1 @@ -21,6 +31,11 @@ define(['../Core/Enumeration'], function(Enumeration) { MITERED : new Enumeration(1, 'MITERED'), /** + * ___ + * / ___ + * | | + * + * Corner is clipped. * @type {Enumeration} * @constant * @default 2 diff --git a/Source/Core/CorridorGeometry.js b/Source/Core/CorridorGeometry.js index 00c8d346682c..40144bb74be4 100644 --- a/Source/Core/CorridorGeometry.js +++ b/Source/Core/CorridorGeometry.js @@ -1,96 +1,51 @@ /*global define*/ define([ + './defaultValue', './defined', './DeveloperError', - './Cartesian2', './Cartesian3', './CornerType', + './CorridorGeometryLibrary', './ComponentDatatype', './Ellipsoid', - './EllipsoidTangentPlane', './Geometry', './IndexDatatype', './Math', - './Matrix3', './PolylinePipeline', './PrimitiveType', - './Quaternion', - './defaultValue', './BoundingSphere', './GeometryAttribute', './GeometryAttributes', './VertexFormat' ], function( + defaultValue, defined, DeveloperError, - Cartesian2, Cartesian3, CornerType, + CorridorGeometryLibrary, ComponentDatatype, Ellipsoid, - EllipsoidTangentPlane, Geometry, IndexDatatype, CesiumMath, - Matrix3, PolylinePipeline, PrimitiveType, - Quaternion, - defaultValue, BoundingSphere, GeometryAttribute, GeometryAttributes, VertexFormat) { "use strict"; - var scaleArray2 = [new Cartesian3(), new Cartesian3()]; - var cartesian1 = new Cartesian3(); var cartesian2 = new Cartesian3(); var cartesian3 = new Cartesian3(); var cartesian4 = new Cartesian3(); var cartesian5 = new Cartesian3(); var cartesian6 = new Cartesian3(); - var cartesian7 = new Cartesian3(); - var cartesian8 = new Cartesian3(); - var cartesian9 = new Cartesian3(); - var cartesian10 = new Cartesian3(); var scratch1 = new Cartesian3(); var scratch2 = new Cartesian3(); - var scratch3 = new Cartesian3(); - var scratch4 = 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; - } - - function addAttribute(attribute, value, front, back) { - var x = value.x; - var y = value.y; - var z = value.z; - if (defined(front)) { - attribute[front] = x; - attribute[front + 1] = y; - attribute[front + 2] = z; - } - if (defined(back)) { - attribute[back] = z; - attribute[back - 1] = y; - attribute[back - 2] = x; - } - } function addNormals(attr, normal, left, front, back, vertexFormat) { var normals = attr.normals; @@ -98,93 +53,22 @@ define([ var binormals = attr.binormals; var forward = Cartesian3.cross(left, normal, scratch1).normalize(scratch1); if (vertexFormat.normal) { - addAttribute(normals, normal, front, back); + CorridorGeometryLibrary.addAttribute(normals, normal, front, back); } if (vertexFormat.binormal) { - addAttribute(binormals, left, front, back); + CorridorGeometryLibrary.addAttribute(binormals, left, front, back); } if (vertexFormat.tangent) { - addAttribute(tangents, forward, front, back); + CorridorGeometryLibrary.addAttribute(tangents, forward, front, back); } } - var posScratch = new Cartesian3(); - function scaleToSurface(positions, ellipsoid) { - for ( var i = 0; i < positions.length; i += 3) { - posScratch = Cartesian3.fromArray(positions, i, posScratch); - posScratch = ellipsoid.scaleToGeodeticSurface(posScratch, posScratch); - positions[i] = posScratch.x; - positions[i + 1] = posScratch.y; - positions[i + 2] = posScratch.z; - } - } - - var quaterion = new Quaternion(); - var rotMatrix = new Matrix3(); - function computeRoundCorner(cornerPoint, startPoint, endPoint, cornerType, leftIsOutside, ellipsoid) { - var angle = Cartesian3.angleBetween(startPoint.subtract(cornerPoint, scratch1), endPoint.subtract(cornerPoint, scratch2)); - var granularity = (cornerType.value === CornerType.BEVELED.value) ? 1 : Math.ceil(angle / CesiumMath.toRadians(5)) + 1; - - var size = granularity * 3; - var array = new Array(size); - - array[size - 3] = endPoint.x; - array[size - 2] = endPoint.y; - array[size - 1] = endPoint.z; - - var m; - if (leftIsOutside) { - m = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(cornerPoint, angle / granularity, quaterion), rotMatrix); - } else { - m = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(cornerPoint.negate(scratch1), angle / granularity, quaterion), rotMatrix); - } - - var index = 0; - startPoint = startPoint.clone(scratch1); - for ( var i = 0; i < granularity; i++) { - startPoint = m.multiplyByVector(startPoint, startPoint); - array[index++] = startPoint.x; - array[index++] = startPoint.y; - array[index++] = startPoint.z; - } - - return array; - } - - function addEndCaps(calculatedPositions, width, ellipsoid) { - var cornerPoint = cartesian1; - var startPoint = cartesian2; - var endPoint = cartesian3; - - var leftEdge = calculatedPositions[1]; - startPoint = Cartesian3.fromArray(calculatedPositions[1], leftEdge.length - 3, startPoint); - endPoint = Cartesian3.fromArray(calculatedPositions[0], 0, endPoint); - cornerPoint = startPoint.add(endPoint, cornerPoint).multiplyByScalar(0.5, cornerPoint); - var firstEndCap = computeRoundCorner(cornerPoint, startPoint, endPoint, CornerType.ROUNDED, false, ellipsoid); - - var length = calculatedPositions.length - 1; - var rightEdge = calculatedPositions[length - 1]; - leftEdge = calculatedPositions[length]; - startPoint = Cartesian3.fromArray(rightEdge, rightEdge.length - 3, startPoint); - endPoint = Cartesian3.fromArray(leftEdge, 0, endPoint); - cornerPoint = startPoint.add(endPoint, cornerPoint).multiplyByScalar(0.5, cornerPoint); - var lastEndCap = computeRoundCorner(cornerPoint, startPoint, endPoint, CornerType.ROUNDED, false, ellipsoid); - - return [firstEndCap, lastEndCap]; - } - - function computeMiteredCorner(position, startPoint, leftCornerDirection, lastPoint, leftIsOutside, granularity, ellipsoid) { - var cornerPoint = scratch1; - if (leftIsOutside) { - cornerPoint = Cartesian3.add(position, leftCornerDirection, cornerPoint); - } else { - leftCornerDirection = leftCornerDirection.negate(leftCornerDirection); - cornerPoint = Cartesian3.add(position, leftCornerDirection, cornerPoint); - } - return [cornerPoint.x, cornerPoint.y, cornerPoint.z, lastPoint.x, lastPoint.y, lastPoint.z]; - } - - function combine(positions, corners, computedLefts, computedNormals, vertexFormat, endPositions, ellipsoid) { + function combine(computedPositions, vertexFormat, ellipsoid) { + var positions = computedPositions.positions; + var corners = computedPositions.corners; + var endPositions = computedPositions.endPositions; + var computedLefts = computedPositions.lefts; + var computedNormals = computedPositions.normals; var attributes = new GeometryAttributes(); var corner; var leftCount = 0; @@ -252,8 +136,8 @@ define([ for (i = 0; i < halfLength; i++) { leftPos = Cartesian3.fromArray(firstEndPositions, (halfLength - 1 - i) * 3, leftPos); rightPos = Cartesian3.fromArray(firstEndPositions, (halfLength + i) * 3, rightPos); - addAttribute(finalPositions, rightPos, front); - addAttribute(finalPositions, leftPos, undefined, back); + CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front); + CorridorGeometryLibrary.addAttribute(finalPositions, leftPos, undefined, back); addNormals(attr, normal, left, front, back, vertexFormat); LL = front / 3; @@ -329,7 +213,7 @@ define([ indices[index++] = pivot; indices[index++] = start - j - 1; indices[index++] = start - j; - addAttribute(finalPositions, outsidePoint, undefined, back); + CorridorGeometryLibrary.addAttribute(finalPositions, outsidePoint, undefined, back); previousPoint = Cartesian3.fromArray(finalPositions, (start - j - 1) * 3, previousPoint); nextPoint = Cartesian3.fromArray(finalPositions, pivot * 3, nextPoint); left = previousPoint.subtract(nextPoint, left).normalize(left); @@ -352,7 +236,7 @@ define([ indices[index++] = pivot; indices[index++] = start + j; indices[index++] = start + j + 1; - addAttribute(finalPositions, outsidePoint, front); + CorridorGeometryLibrary.addAttribute(finalPositions, outsidePoint, front); previousPoint = Cartesian3.fromArray(finalPositions, pivot * 3, previousPoint); nextPoint = Cartesian3.fromArray(finalPositions, (start + j) * 3, nextPoint); left = previousPoint.subtract(nextPoint, left).normalize(left); @@ -411,8 +295,8 @@ define([ for (i = 0; i < halfLength; i++) { leftPos = Cartesian3.fromArray(lastEndPositions, (endPositionLength - i - 1) * 3, leftPos); rightPos = Cartesian3.fromArray(lastEndPositions, i * 3, rightPos); - addAttribute(finalPositions, leftPos, undefined, back); - addAttribute(finalPositions, rightPos, front); + CorridorGeometryLibrary.addAttribute(finalPositions, leftPos, undefined, back); + CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front); addNormals(attr, normal, left, front, back, vertexFormat); LR = front / 3; @@ -526,144 +410,10 @@ define([ return { attributes : attributes, - indices : indices, - boundingSphere : BoundingSphere.fromVertices(finalPositions) + indices : indices }; } - function addShiftedPositions(positions, left, scalar, calculatedPositions) { - var rightPositions = new Array(positions.length); - var leftPositions = new Array(positions.length); - var scaledLeft = left.multiplyByScalar(scalar, scratch1); - var scaledRight = scaledLeft.negate(scratch2); - var rightIndex = 0; - var leftIndex = positions.length - 1; - - for (var i = 0; i < positions.length; i += 3) { - var pos = Cartesian3.fromArray(positions, i, scratch3); - var rightPos = pos.add(scaledRight, scratch4); - rightPositions[rightIndex++] = rightPos.x; - rightPositions[rightIndex++] = rightPos.y; - rightPositions[rightIndex++] = rightPos.z; - - var leftPos = pos.add(scaledLeft, scratch4); - leftPositions[leftIndex--] = leftPos.z; - leftPositions[leftIndex--] = leftPos.y; - leftPositions[leftIndex--] = leftPos.x; - } - calculatedPositions.push(rightPositions, leftPositions); - - return calculatedPositions; - } - - function computePositions(params) { - var granularity = params.granularity; - var positions = params.positions; - var width = params.width / 2; - var ellipsoid = params.ellipsoid; - var cornerType = params.cornerType; - var normal = cartesian1; - var forward = cartesian2; - var backward = cartesian3; - var left = cartesian4; - var cornerDirection = cartesian5; - var startPoint = cartesian6; - var previousPos = cartesian7; - var rightPos = cartesian8; - var leftPos = cartesian9; - var center = cartesian10; - var calculatedPositions = []; - var calculatedLefts = []; - var calculatedNormals = []; - var position = positions[0]; //add first point - var nextPosition = positions[1]; - - forward = Cartesian3.subtract(nextPosition, position, forward).normalize(forward); - normal = ellipsoid.geodeticSurfaceNormal(position, normal); - left = normal.cross(forward, left).normalize(left); - calculatedLefts.push(left.x, left.y, left.z); - calculatedNormals.push(normal.x, normal.y, normal.z); - previousPos = Cartesian3.clone(position, previousPos); - position = nextPosition; - backward = forward.negate(backward); - - var subdividedPositions; - var corners = []; - var i; - var length = positions.length; - for (i = 1; i < length - 1; i++) { // add middle points and corners - normal = ellipsoid.geodeticSurfaceNormal(position, normal); - nextPosition = positions[i + 1]; - forward = Cartesian3.subtract(nextPosition, position, forward).normalize(forward); - cornerDirection = forward.add(backward, cornerDirection).normalize(cornerDirection); - var doCorner = !Cartesian3.equalsEpsilon(cornerDirection.negate(scratch1), normal, CesiumMath.EPSILON2); - if (doCorner) { - cornerDirection = cornerDirection.cross(normal, cornerDirection); - cornerDirection = normal.cross(cornerDirection, cornerDirection); - var scalar = width / Math.max(0.25, (Cartesian3.cross(cornerDirection, backward, scratch1).magnitude())); - var leftIsOutside = angleIsGreaterThanPi(forward, backward, position, ellipsoid); - cornerDirection = cornerDirection.multiplyByScalar(scalar, cornerDirection, cornerDirection); - if (leftIsOutside) { - rightPos = Cartesian3.add(position, cornerDirection, rightPos); - center = rightPos.add(left.multiplyByScalar(width, center), center); - leftPos = rightPos.add(left.multiplyByScalar(width * 2, leftPos), leftPos); - scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); - scaleArray2[1] = Cartesian3.clone(center, scaleArray2[1]); - subdividedPositions = PolylinePipeline.scaleToSurface(scaleArray2, granularity, ellipsoid); - calculatedPositions = addShiftedPositions(subdividedPositions, left, width, calculatedPositions); - calculatedLefts.push(left.x, left.y, left.z); - calculatedNormals.push(normal.x, normal.y, normal.z); - startPoint = leftPos.clone(startPoint); - left = normal.cross(forward, left).normalize(left); - leftPos = rightPos.add(left.multiplyByScalar(width * 2, leftPos), leftPos); - previousPos = rightPos.add(left.multiplyByScalar(width, previousPos), previousPos); - if (cornerType.value === CornerType.ROUNDED.value || cornerType.value === CornerType.BEVELED.value) { - corners.push({leftPositions : computeRoundCorner(rightPos, startPoint, leftPos, cornerType, leftIsOutside, ellipsoid)}); - } else { - corners.push({leftPositions : computeMiteredCorner(position, startPoint, cornerDirection.negate(cornerDirection), leftPos, leftIsOutside, granularity, ellipsoid)}); - } - } else { - leftPos = Cartesian3.add(position, cornerDirection, leftPos); - center = leftPos.add(left.multiplyByScalar(width, center).negate(center), center); - rightPos = leftPos.add(left.multiplyByScalar(width * 2, rightPos).negate(rightPos), rightPos); - scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); - scaleArray2[1] = Cartesian3.clone(center, scaleArray2[1]); - subdividedPositions = PolylinePipeline.scaleToSurface(scaleArray2, granularity, ellipsoid); - calculatedPositions = addShiftedPositions(subdividedPositions, left, width, calculatedPositions); - calculatedLefts.push(left.x, left.y, left.z); - calculatedNormals.push(normal.x, normal.y, normal.z); - startPoint = rightPos.clone(startPoint); - left = normal.cross(forward, left).normalize(left); - rightPos = leftPos.add(left.multiplyByScalar(width * 2, rightPos).negate(rightPos), rightPos); - previousPos = leftPos.add(left.multiplyByScalar(width, previousPos).negate(previousPos), previousPos); - if (cornerType.value === CornerType.ROUNDED.value || cornerType.value === CornerType.BEVELED.value) { - corners.push({rightPositions : computeRoundCorner(leftPos, startPoint, rightPos, cornerType, leftIsOutside, ellipsoid)}); - } else { - corners.push({rightPositions : computeMiteredCorner(position, startPoint, cornerDirection, rightPos, leftIsOutside, granularity, ellipsoid)}); - } - } - - backward = forward.negate(backward); - } - position = nextPosition; - } - - normal = ellipsoid.geodeticSurfaceNormal(position, normal); - scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); - scaleArray2[1] = Cartesian3.clone(position, scaleArray2[1]); - subdividedPositions = PolylinePipeline.scaleToSurface(scaleArray2, granularity, ellipsoid); - calculatedPositions = addShiftedPositions(subdividedPositions, left, width, calculatedPositions); - calculatedLefts.push(left.x, left.y, left.z); - calculatedNormals.push(normal.x, normal.y, normal.z); - - var endPositions; - if (cornerType.value === CornerType.ROUNDED.value) { - endPositions = addEndCaps(calculatedPositions, width, ellipsoid); - } - - return combine(calculatedPositions, corners, calculatedLefts, calculatedNormals, params.vertexFormat, endPositions, ellipsoid); - } - function extrudedAttributes(attributes, vertexFormat) { if (!vertexFormat.normal && !vertexFormat.binormal && !vertexFormat.tangent && !vertexFormat.st) { return attributes; @@ -700,26 +450,26 @@ define([ previousPosition = previousPosition.subtract(topPosition, previousPosition); normal = bottomPosition.cross(previousPosition, normal).normalize(normal); if (vertexFormat.normal) { - addAttribute(normals, normal, attrIndexOffset); - addAttribute(normals, normal, attrIndexOffset + 3); - addAttribute(normals, normal, attrIndex); - addAttribute(normals, normal, attrIndex + 3); + CorridorGeometryLibrary.addAttribute(normals, normal, attrIndexOffset); + CorridorGeometryLibrary.addAttribute(normals, normal, attrIndexOffset + 3); + CorridorGeometryLibrary.addAttribute(normals, normal, attrIndex); + CorridorGeometryLibrary.addAttribute(normals, normal, attrIndex + 3); } if (vertexFormat.tangent || vertexFormat.binormal) { binormal = Cartesian3.fromArray(topNormals, i, binormal); if (vertexFormat.binormal) { - addAttribute(binormals, binormal, attrIndexOffset); - addAttribute(binormals, binormal, attrIndexOffset + 3); - addAttribute(binormals, binormal, attrIndex); - addAttribute(binormals, binormal, attrIndex + 3); + CorridorGeometryLibrary.addAttribute(binormals, binormal, attrIndexOffset); + CorridorGeometryLibrary.addAttribute(binormals, binormal, attrIndexOffset + 3); + CorridorGeometryLibrary.addAttribute(binormals, binormal, attrIndex); + CorridorGeometryLibrary.addAttribute(binormals, binormal, attrIndex + 3); } if (vertexFormat.tangent) { tangent = binormal.cross(normal, tangent).normalize(tangent); - addAttribute(tangents, tangent, attrIndexOffset); - addAttribute(tangents, tangent, attrIndexOffset + 3); - addAttribute(tangents, tangent, attrIndex); - addAttribute(tangents, tangent, attrIndex + 3); + CorridorGeometryLibrary.addAttribute(tangents, tangent, attrIndexOffset); + CorridorGeometryLibrary.addAttribute(tangents, tangent, attrIndexOffset + 3); + CorridorGeometryLibrary.addAttribute(tangents, tangent, attrIndex); + CorridorGeometryLibrary.addAttribute(tangents, tangent, attrIndex + 3); } } attrIndex += 6; @@ -801,22 +551,21 @@ define([ return wallPositions; } - function computePositionsExtruded(params) { - var vertexFormat = params.vertexFormat; - params.vertexFormat = new VertexFormat({ + function computePositionsExtruded(params, vertexFormat) { + var topVertexFormat = new VertexFormat({ position : vertexFormat.positon, normal : (vertexFormat.normal || vertexFormat.binormal), tangent : vertexFormat.tangent, binormal : (vertexFormat.normal || vertexFormat.binormal), st : vertexFormat.st }); - var attr = computePositions(params); + var ellipsoid = params.ellipsoid; + var computedPositions = CorridorGeometryLibrary.computePositions(params); + var attr = combine(computedPositions, topVertexFormat, ellipsoid); var height = params.height; var extrudedHeight = params.extrudedHeight; - var ellipsoid = params.ellipsoid; var attributes = attr.attributes; var indices = attr.indices; - var boundingSphere = attr.boundingSphere; var positions = attributes.position.values; var length = positions.length; var newPositions = new Float64Array(length * 6); @@ -831,7 +580,6 @@ define([ newPositions.set(positions); newPositions.set(extrudedPositions, length); newPositions.set(wallPositions, length * 2); - boundingSphere = BoundingSphere.fromVertices(positions, undefined, 3, boundingSphere); attributes.position.values = newPositions; length /= 3; @@ -868,8 +616,7 @@ define([ return { attributes : attributes, - indices : newIndices, - boundingSphere : boundingSphere + indices : newIndices }; } @@ -948,11 +695,11 @@ define([ var vertexFormat = corridorGeometry._vertexFormat; var params = { ellipsoid : ellipsoid, - vertexFormat : vertexFormat, positions : cleanPositions, width : corridorGeometry._width, cornerType : corridorGeometry._cornerType, - granularity : corridorGeometry._granularity + granularity : corridorGeometry._granularity, + saveAttributes: true }; var attr; if (extrude) { @@ -961,23 +708,23 @@ define([ height = h; params.height = height; params.extrudedHeight = extrudedHeight; - attr = computePositionsExtruded(params); + attr = computePositionsExtruded(params, vertexFormat); } else { - attr = computePositions(params); - if (!vertexFormat.position) { - attr.attributes.position.values = undefined; - } else { - attr.attributes.position.values = new Float64Array(PolylinePipeline.scaleToGeodeticHeight(attr.attributes.position.values, height, ellipsoid, attr.attributes.position.values)); - } - + var computedPositions = CorridorGeometryLibrary.computePositions(params); + attr = combine(computedPositions, vertexFormat, ellipsoid); + attr.attributes.position.values = PolylinePipeline.scaleToGeodeticHeight(attr.attributes.position.values, height, ellipsoid, attr.attributes.position.values); } var attributes = attr.attributes; + var boundingSphere = BoundingSphere.fromVertices(attributes.position.values, undefined, 3); + if (!vertexFormat.position) { + attr.attributes.position.values = undefined; + } return new Geometry({ attributes : attributes, indices : attr.indices, primitiveType : PrimitiveType.TRIANGLES, - boundingSphere : attr.boundingSphere + boundingSphere : boundingSphere }); }; diff --git a/Source/Core/CorridorGeometryLibrary.js b/Source/Core/CorridorGeometryLibrary.js new file mode 100644 index 000000000000..ca85eba490da --- /dev/null +++ b/Source/Core/CorridorGeometryLibrary.js @@ -0,0 +1,297 @@ +/*global define*/ +define([ + './defined', + './Cartesian2', + './Cartesian3', + './CornerType', + './EllipsoidTangentPlane', + './PolylinePipeline', + './Matrix3', + './Quaternion', + './Math' + ], function( + defined, + Cartesian2, + Cartesian3, + CornerType, + EllipsoidTangentPlane, + PolylinePipeline, + Matrix3, + Quaternion, + CesiumMath) { + "use strict"; + + /** + * @private + */ + var CorridorGeometryLibrary = {}; + + var scratch1 = new Cartesian3(); + var scratch2 = new Cartesian3(); + var scratch3 = new Cartesian3(); + var scratch4 = new Cartesian3(); + + var scaleArray2 = [new Cartesian3(), new Cartesian3()]; + + var cartesian1 = new Cartesian3(); + var cartesian2 = new Cartesian3(); + var cartesian3 = new Cartesian3(); + var cartesian4 = new Cartesian3(); + var cartesian5 = new Cartesian3(); + var cartesian6 = new Cartesian3(); + var cartesian7 = new Cartesian3(); + var cartesian8 = new Cartesian3(); + 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) { + var angle = Cartesian3.angleBetween(startPoint.subtract(cornerPoint, scratch1), endPoint.subtract(cornerPoint, scratch2)); + var granularity = (cornerType.value === CornerType.BEVELED.value) ? 1 : Math.ceil(angle / CesiumMath.toRadians(5)) + 1; + + var size = granularity * 3; + var array = new Array(size); + + array[size - 3] = endPoint.x; + array[size - 2] = endPoint.y; + array[size - 1] = endPoint.z; + + var m; + if (leftIsOutside) { + m = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(cornerPoint, angle / granularity, quaterion), rotMatrix); + } else { + m = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(cornerPoint.negate(scratch1), angle / granularity, quaterion), rotMatrix); + } + + var index = 0; + startPoint = startPoint.clone(scratch1); + for ( var i = 0; i < granularity; i++) { + startPoint = m.multiplyByVector(startPoint, startPoint); + array[index++] = startPoint.x; + array[index++] = startPoint.y; + array[index++] = startPoint.z; + } + + return array; + } + + function addEndCaps (calculatedPositions, width, ellipsoid) { + var cornerPoint = cartesian1; + var startPoint = cartesian2; + var endPoint = cartesian3; + + var leftEdge = calculatedPositions[1]; + startPoint = Cartesian3.fromArray(calculatedPositions[1], leftEdge.length - 3, startPoint); + endPoint = Cartesian3.fromArray(calculatedPositions[0], 0, endPoint); + cornerPoint = startPoint.add(endPoint, cornerPoint).multiplyByScalar(0.5, cornerPoint); + var firstEndCap = computeRoundCorner(cornerPoint, startPoint, endPoint, CornerType.ROUNDED, false, ellipsoid); + + var length = calculatedPositions.length - 1; + var rightEdge = calculatedPositions[length - 1]; + leftEdge = calculatedPositions[length]; + startPoint = Cartesian3.fromArray(rightEdge, rightEdge.length - 3, startPoint); + endPoint = Cartesian3.fromArray(leftEdge, 0, endPoint); + cornerPoint = startPoint.add(endPoint, cornerPoint).multiplyByScalar(0.5, cornerPoint); + var lastEndCap = computeRoundCorner(cornerPoint, startPoint, endPoint, CornerType.ROUNDED, false, ellipsoid); + + return [firstEndCap, lastEndCap]; + } + + function computeMiteredCorner (position, startPoint, leftCornerDirection, lastPoint, leftIsOutside, granularity, ellipsoid) { + var cornerPoint = scratch1; + if (leftIsOutside) { + cornerPoint = Cartesian3.add(position, leftCornerDirection, cornerPoint); + } else { + leftCornerDirection = leftCornerDirection.negate(leftCornerDirection); + cornerPoint = Cartesian3.add(position, leftCornerDirection, cornerPoint); + } + return [cornerPoint.x, cornerPoint.y, cornerPoint.z, lastPoint.x, lastPoint.y, lastPoint.z]; + } + + function addShiftedPositions (positions, left, scalar, calculatedPositions) { + var rightPositions = new Array(positions.length); + var leftPositions = new Array(positions.length); + var scaledLeft = left.multiplyByScalar(scalar, scratch1); + var scaledRight = scaledLeft.negate(scratch2); + var rightIndex = 0; + var leftIndex = positions.length - 1; + + for (var i = 0; i < positions.length; i += 3) { + var pos = Cartesian3.fromArray(positions, i, scratch3); + var rightPos = pos.add(scaledRight, scratch4); + rightPositions[rightIndex++] = rightPos.x; + rightPositions[rightIndex++] = rightPos.y; + rightPositions[rightIndex++] = rightPos.z; + + var leftPos = pos.add(scaledLeft, scratch4); + leftPositions[leftIndex--] = leftPos.z; + leftPositions[leftIndex--] = leftPos.y; + leftPositions[leftIndex--] = leftPos.x; + } + calculatedPositions.push(rightPositions, leftPositions); + + return calculatedPositions; + } + + /** + * @private + */ + CorridorGeometryLibrary.addAttribute = function (attribute, value, front, back) { + var x = value.x; + var y = value.y; + var z = value.z; + if (defined(front)) { + attribute[front] = x; + attribute[front + 1] = y; + attribute[front + 2] = z; + } + if (defined(back)) { + attribute[back] = z; + attribute[back - 1] = y; + attribute[back - 2] = x; + } + }; + + /** + * @private + */ + CorridorGeometryLibrary.computePositions = function (params) { + var granularity = params.granularity; + var positions = params.positions; + var width = params.width / 2; + var ellipsoid = params.ellipsoid; + var cornerType = params.cornerType; + var saveAttributes = params.saveAttributes; + var normal = cartesian1; + var forward = cartesian2; + var backward = cartesian3; + var left = cartesian4; + var cornerDirection = cartesian5; + var startPoint = cartesian6; + var previousPos = cartesian7; + var rightPos = cartesian8; + var leftPos = cartesian9; + var center = cartesian10; + var calculatedPositions = []; + var calculatedLefts = (saveAttributes) ? [] : undefined; + var calculatedNormals = (saveAttributes) ? [] : undefined; + var position = positions[0]; //add first point + var nextPosition = positions[1]; + + forward = Cartesian3.subtract(nextPosition, position, forward).normalize(forward); + normal = ellipsoid.geodeticSurfaceNormal(position, normal); + left = normal.cross(forward, left).normalize(left); + if (saveAttributes) { + calculatedLefts.push(left.x, left.y, left.z); + calculatedNormals.push(normal.x, normal.y, normal.z); + } + previousPos = Cartesian3.clone(position, previousPos); + position = nextPosition; + backward = forward.negate(backward); + + var subdividedPositions; + var corners = []; + var i; + var length = positions.length; + for (i = 1; i < length - 1; i++) { // add middle points and corners + normal = ellipsoid.geodeticSurfaceNormal(position, normal); + nextPosition = positions[i + 1]; + forward = Cartesian3.subtract(nextPosition, position, forward).normalize(forward); + cornerDirection = forward.add(backward, cornerDirection).normalize(cornerDirection); + var doCorner = !Cartesian3.equalsEpsilon(cornerDirection.negate(scratch1), normal, CesiumMath.EPSILON2); + if (doCorner) { + cornerDirection = cornerDirection.cross(normal, cornerDirection); + cornerDirection = normal.cross(cornerDirection, cornerDirection); + var scalar = width / Math.max(0.25, (Cartesian3.cross(cornerDirection, backward, scratch1).magnitude())); + var leftIsOutside = angleIsGreaterThanPi(forward, backward, position, ellipsoid); + cornerDirection = cornerDirection.multiplyByScalar(scalar, cornerDirection, cornerDirection); + if (leftIsOutside) { + rightPos = Cartesian3.add(position, cornerDirection, rightPos); + center = rightPos.add(left.multiplyByScalar(width, center), center); + leftPos = rightPos.add(left.multiplyByScalar(width * 2, leftPos), leftPos); + scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); + scaleArray2[1] = Cartesian3.clone(center, scaleArray2[1]); + subdividedPositions = PolylinePipeline.scaleToSurface(scaleArray2, granularity, ellipsoid); + calculatedPositions = addShiftedPositions(subdividedPositions, left, width, calculatedPositions); + if (saveAttributes) { + calculatedLefts.push(left.x, left.y, left.z); + calculatedNormals.push(normal.x, normal.y, normal.z); + } + startPoint = leftPos.clone(startPoint); + left = normal.cross(forward, left).normalize(left); + leftPos = rightPos.add(left.multiplyByScalar(width * 2, leftPos), leftPos); + previousPos = rightPos.add(left.multiplyByScalar(width, previousPos), previousPos); + if (cornerType.value === CornerType.ROUNDED.value || cornerType.value === CornerType.BEVELED.value) { + corners.push({leftPositions : computeRoundCorner(rightPos, startPoint, leftPos, cornerType, leftIsOutside, ellipsoid)}); + } else { + corners.push({leftPositions : computeMiteredCorner(position, startPoint, cornerDirection.negate(cornerDirection), leftPos, leftIsOutside, granularity, ellipsoid)}); + } + } else { + leftPos = Cartesian3.add(position, cornerDirection, leftPos); + center = leftPos.add(left.multiplyByScalar(width, center).negate(center), center); + rightPos = leftPos.add(left.multiplyByScalar(width * 2, rightPos).negate(rightPos), rightPos); + scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); + scaleArray2[1] = Cartesian3.clone(center, scaleArray2[1]); + subdividedPositions = PolylinePipeline.scaleToSurface(scaleArray2, granularity, ellipsoid); + calculatedPositions = addShiftedPositions(subdividedPositions, left, width, calculatedPositions); + if (saveAttributes) { + calculatedLefts.push(left.x, left.y, left.z); + calculatedNormals.push(normal.x, normal.y, normal.z); + } + startPoint = rightPos.clone(startPoint); + left = normal.cross(forward, left).normalize(left); + rightPos = leftPos.add(left.multiplyByScalar(width * 2, rightPos).negate(rightPos), rightPos); + previousPos = leftPos.add(left.multiplyByScalar(width, previousPos).negate(previousPos), previousPos); + if (cornerType.value === CornerType.ROUNDED.value || cornerType.value === CornerType.BEVELED.value) { + corners.push({rightPositions : computeRoundCorner(leftPos, startPoint, rightPos, cornerType, leftIsOutside, ellipsoid)}); + } else { + corners.push({rightPositions : computeMiteredCorner(position, startPoint, cornerDirection, rightPos, leftIsOutside, granularity, ellipsoid)}); + } + } + backward = forward.negate(backward); + } + position = nextPosition; + } + + normal = ellipsoid.geodeticSurfaceNormal(position, normal); + scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); + scaleArray2[1] = Cartesian3.clone(position, scaleArray2[1]); + subdividedPositions = PolylinePipeline.scaleToSurface(scaleArray2, granularity, ellipsoid); + calculatedPositions = addShiftedPositions(subdividedPositions, left, width, calculatedPositions); + if (saveAttributes) { + calculatedLefts.push(left.x, left.y, left.z); + calculatedNormals.push(normal.x, normal.y, normal.z); + } + + var endPositions; + if (cornerType.value === CornerType.ROUNDED.value) { + endPositions = addEndCaps(calculatedPositions, width, ellipsoid); + } + + return { + positions: calculatedPositions, + corners: corners, + lefts: calculatedLefts, + normals: calculatedNormals, + endPositions: endPositions + }; + }; + + return CorridorGeometryLibrary; +}); \ No newline at end of file diff --git a/Source/Core/CorridorOutlineGeometry.js b/Source/Core/CorridorOutlineGeometry.js new file mode 100644 index 000000000000..16d6ad6768da --- /dev/null +++ b/Source/Core/CorridorOutlineGeometry.js @@ -0,0 +1,405 @@ +/*global define*/ +define([ + './defined', + './DeveloperError', + './Cartesian3', + './CornerType', + './CorridorGeometryLibrary', + './ComponentDatatype', + './Ellipsoid', + './Geometry', + './IndexDatatype', + './Math', + './PolylinePipeline', + './PrimitiveType', + './defaultValue', + './BoundingSphere', + './GeometryAttribute', + './GeometryAttributes' + ], function( + defined, + DeveloperError, + Cartesian3, + CornerType, + CorridorGeometryLibrary, + ComponentDatatype, + Ellipsoid, + Geometry, + IndexDatatype, + CesiumMath, + PolylinePipeline, + PrimitiveType, + defaultValue, + BoundingSphere, + GeometryAttribute, + GeometryAttributes) { + "use strict"; + + var cartesian1 = new Cartesian3(); + var cartesian2 = new Cartesian3(); + var cartesian3 = new Cartesian3(); + + function combine(computedPositions, ellipsoid, cornerType) { + var wallIndices = []; + var positions = computedPositions.positions; + var corners = computedPositions.corners; + var endPositions = computedPositions.endPositions; + var attributes = new GeometryAttributes(); + var corner; + var leftCount = 0; + var rightCount = 0; + var i; + var indicesLength = 0; + var length; + for (i = 0; i < positions.length; i += 2) { + length = positions[i].length - 3; + leftCount += length; //subtracting 3 to account for duplicate points at corners + indicesLength += length / 3 * 4; + rightCount += positions[i + 1].length - 3; + } + leftCount += 3; //add back count for end positions + rightCount += 3; + for (i = 0; i < corners.length; i++) { + corner = corners[i]; + var leftSide = corners[i].leftPositions; + if (defined(leftSide)) { + length = leftSide.length; + leftCount += length; + indicesLength += length / 3 * 2; + } else { + length = corners[i].rightPositions.length; + rightCount += length; + indicesLength += length / 3 * 2; + } + } + + var addEndPositions = defined(endPositions); + var endPositionLength; + if (addEndPositions) { + endPositionLength = endPositions[0].length - 3; + leftCount += endPositionLength; + rightCount += endPositionLength; + endPositionLength /= 3; + indicesLength += endPositionLength * 4; + } + var size = leftCount + rightCount; + var finalPositions = new Float64Array(size); + var front = 0; + var back = size - 1; + var UL, LL, UR, LR; + var rightPos, leftPos; + var halfLength = endPositionLength / 2; + + var indices = IndexDatatype.createTypedArray(size / 3, indicesLength + 4); + var index = 0; + + indices[index++] = front / 3; + indices[index++] = (back - 2) / 3; + if (addEndPositions) { // add rounded end + wallIndices.push(front / 3); + leftPos = cartesian1; + rightPos = cartesian2; + var firstEndPositions = endPositions[0]; + for (i = 0; i < halfLength; i++) { + leftPos = Cartesian3.fromArray(firstEndPositions, (halfLength - 1 - i) * 3, leftPos); + rightPos = Cartesian3.fromArray(firstEndPositions, (halfLength + i) * 3, rightPos); + CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front); + CorridorGeometryLibrary.addAttribute(finalPositions, leftPos, undefined, back); + + LL = front / 3; + LR = LL + 1; + UL = (back - 2) / 3; + UR = UL - 1; + indices[index++] = UL; + indices[index++] = UR; + indices[index++] = LL; + indices[index++] = LR; + + front += 3; + back -= 3; + } + } + + var posIndex = 0; + var rightEdge = positions[posIndex++]; //add first two edges + var leftEdge = positions[posIndex++]; + finalPositions.set(rightEdge, front); + finalPositions.set(leftEdge, back - leftEdge.length + 1); + + length = leftEdge.length - 3; + wallIndices.push(front / 3, (back - 2) / 3); + for (i = 0; i < length; i += 3) { + LL = front / 3; + LR = LL + 1; + UL = (back - 2) / 3; + UR = UL - 1; + indices[index++] = UL; + indices[index++] = UR; + indices[index++] = LL; + indices[index++] = LR; + + front += 3; + back -= 3; + } + + for (i = 0; i < corners.length; i++) { + var j; + corner = corners[i]; + var l = corner.leftPositions; + var r = corner.rightPositions; + var start; + var outsidePoint = cartesian3; + if (defined(l)) { + back -= 3; + start = UR; + wallIndices.push(LR); + for (j = 0; j < l.length / 3; j++) { + outsidePoint = Cartesian3.fromArray(l, j * 3, outsidePoint); + indices[index++] = start - j - 1; + indices[index++] = start - j; + CorridorGeometryLibrary.addAttribute(finalPositions, outsidePoint, undefined, back); + back -= 3; + } + wallIndices.push(start - Math.floor(l.length / 6)); + if (cornerType.value === CornerType.BEVELED.value) { + wallIndices.push((back - 2) / 3 + 1); + } + front += 3; + } else { + front += 3; + start = LR; + wallIndices.push(UR); + for (j = 0; j < r.length / 3; j++) { + outsidePoint = Cartesian3.fromArray(r, j * 3, outsidePoint); + indices[index++] = start + j; + indices[index++] = start + j + 1; + CorridorGeometryLibrary.addAttribute(finalPositions, outsidePoint, front); + front += 3; + } + wallIndices.push(start + Math.floor(r.length / 6)); + if (cornerType.value === CornerType.BEVELED.value) { + wallIndices.push(front / 3 - 1); + } + back -= 3; + } + rightEdge = positions[posIndex++]; + leftEdge = positions[posIndex++]; + rightEdge.splice(0, 3); //remove duplicate points added by corner + leftEdge.splice(leftEdge.length - 3, 3); + finalPositions.set(rightEdge, front); + finalPositions.set(leftEdge, back - leftEdge.length + 1); + length = leftEdge.length - 3; + + for (j = 0; j < leftEdge.length; j += 3) { + LR = front / 3; + LL = LR - 1; + UR = (back - 2) / 3; + UL = UR + 1; + indices[index++] = UL; + indices[index++] = UR; + indices[index++] = LL; + indices[index++] = LR; + front += 3; + back -= 3; + } + front -= 3; + back += 3; + wallIndices.push(front / 3, (back - 2) / 3); + } + + if (addEndPositions) { // add rounded end + front += 3; + back -= 3; + leftPos = cartesian1; + rightPos = cartesian2; + var lastEndPositions = endPositions[1]; + for (i = 0; i < halfLength; i++) { + leftPos = Cartesian3.fromArray(lastEndPositions, (endPositionLength - i - 1) * 3, leftPos); + rightPos = Cartesian3.fromArray(lastEndPositions, i * 3, rightPos); + CorridorGeometryLibrary.addAttribute(finalPositions, leftPos, undefined, back); + CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front); + + LR = front / 3; + LL = LR - 1; + UR = (back - 2) / 3; + UL = UR + 1; + indices[index++] = UL; + indices[index++] = UR; + indices[index++] = LL; + indices[index++] = LR; + + front += 3; + back -= 3; + } + + wallIndices.push(front / 3); + } else { + wallIndices.push(front / 3, (back - 2) / 3); + } + indices[index++] = front / 3; + indices[index++] = (back - 2) / 3; + + attributes.position = new GeometryAttribute({ + componentDatatype : ComponentDatatype.DOUBLE, + componentsPerAttribute : 3, + values : finalPositions + }); + + return { + attributes : attributes, + indices : indices, + wallIndices : wallIndices + }; + } + + function computePositionsExtruded(params) { + var ellipsoid = params.ellipsoid; + var computedPositions = CorridorGeometryLibrary.computePositions(params); + var attr = combine(computedPositions, ellipsoid, params.cornerType); + var wallIndices = attr.wallIndices; + var height = params.height; + var extrudedHeight = params.extrudedHeight; + var attributes = attr.attributes; + var indices = attr.indices; + var positions = attributes.position.values; + var length = positions.length; + var extrudedPositions = new Float64Array(length); + extrudedPositions.set(positions); + var newPositions = new Float64Array(length * 2); + + positions = PolylinePipeline.scaleToGeodeticHeight(positions, height, ellipsoid, positions); + extrudedPositions = PolylinePipeline.scaleToGeodeticHeight(extrudedPositions, extrudedHeight, ellipsoid, extrudedPositions); + newPositions.set(positions); + newPositions.set(extrudedPositions, length); + attributes.position.values = newPositions; + + length /= 3; + var i; + var iLength = indices.length; + var newIndices = IndexDatatype.createTypedArray(newPositions.length / 3, (iLength + wallIndices.length) * 2); + newIndices.set(indices); + var index = iLength; + for (i = 0; i < iLength; i += 2) { // bottom indices + var v0 = indices[i]; + var v1 = indices[i + 1]; + newIndices[index++] = v0 + length; + newIndices[index++] = v1 + length; + } + + var UL, LL; + for (i = 0; i < wallIndices.length; i++) { //wall indices + UL = wallIndices[i]; + LL = UL + length; + newIndices[index++] = UL; + newIndices[index++] = LL; + } + + return { + attributes : attributes, + indices : newIndices + }; + } + + /** + * A description of a corridor outline. + * + * @alias CorridorOutlineGeometry + * @constructor + * + * @param {Array} options.positions An array of {Cartesain3} positions that define the center of the corridor outline. + * @param {Number} options.width The distance between the edges of the corridor outline. + * @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 {Number} [options.extrudedHeight] The distance between the ellipsoid surface and the extrusion. + * @param {Boolean} [options.cornerType = CornerType.ROUNDED] Determines the style of the corners. + * + * @exception {DeveloperError} options.positions is required. + * @exception {DeveloperError} options.width is required. + * + * @see CorridorOutlineGeometry#createGeometry + * + * @example + * var corridor = new CorridorOutlineGeometry({ + * positions : ellipsoid.cartographicArrayToCartesianArray([ + * Cartographic.fromDegrees(-72.0, 40.0), + * Cartographic.fromDegrees(-70.0, 35.0) + * ]), + * width : 100000 + * }); + */ + var CorridorOutlineGeometry = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + var positions = options.positions; + if (!defined(positions)) { + throw new DeveloperError('options.positions is required.'); + } + var width = options.width; + if (!defined(width)) { + throw new DeveloperError('options.width is required.'); + } + + this._positions = positions; + this._width = width; + this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); + this._height = defaultValue(options.height, 0); + this._extrudedHeight = defaultValue(options.extrudedHeight, this._height); + this._cornerType = defaultValue(options.cornerType, CornerType.ROUNDED); + this._granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE); + this._workerName = 'createCorridorOutlineGeometry'; + }; + + /** + * Computes the geometric representation of a corridor, including its vertices, indices, and a bounding sphere. + * @memberof CorridorOutlineGeometry + * + * @param {CorridorOutlineGeometry} corridorOutlineGeometry A description of the corridor. + * + * @returns {Geometry} The computed vertices and indices. + * + * @exception {DeveloperError} Count of unique positions must be greater than 1. + */ + CorridorOutlineGeometry.createGeometry = function(corridorOutlineGeometry) { + var positions = corridorOutlineGeometry._positions; + var height = corridorOutlineGeometry._height; + var extrudedHeight = corridorOutlineGeometry._extrudedHeight; + var extrude = (height !== extrudedHeight); + var cleanPositions = PolylinePipeline.removeDuplicates(positions); + if (cleanPositions.length < 2) { + throw new DeveloperError('Count of unique positions must be greater than 1.'); + } + var ellipsoid = corridorOutlineGeometry._ellipsoid; + var params = { + ellipsoid : ellipsoid, + positions : cleanPositions, + width : corridorOutlineGeometry._width, + cornerType : corridorOutlineGeometry._cornerType, + granularity : corridorOutlineGeometry._granularity, + saveAttributes : false + }; + var attr; + if (extrude) { + var h = Math.max(height, extrudedHeight); + extrudedHeight = Math.min(height, extrudedHeight); + height = h; + params.height = height; + params.extrudedHeight = extrudedHeight; + attr = computePositionsExtruded(params); + } else { + var computedPositions = CorridorGeometryLibrary.computePositions(params); + attr = combine(computedPositions, ellipsoid, params.cornerType); + attr.attributes.position.values = PolylinePipeline.scaleToGeodeticHeight(attr.attributes.position.values, height, ellipsoid, attr.attributes.position.values); + } + var attributes = attr.attributes; + var boundingSphere = BoundingSphere.fromVertices(attributes.position.values, undefined, 3); + + return new Geometry({ + attributes : attributes, + indices : attr.indices, + primitiveType : PrimitiveType.LINES, + boundingSphere : boundingSphere + }); + }; + + return CorridorOutlineGeometry; +}); \ No newline at end of file diff --git a/Source/Workers/createCorridorOutlineGeometry.js b/Source/Workers/createCorridorOutlineGeometry.js new file mode 100644 index 000000000000..14d5bedb6bd6 --- /dev/null +++ b/Source/Workers/createCorridorOutlineGeometry.js @@ -0,0 +1,28 @@ +/*global define*/ +define([ + '../Core/CorridorOutlineGeometry', + '../Core/Ellipsoid', + '../Scene/PrimitivePipeline', + './createTaskProcessorWorker' + ], function( + CorridorOutlineGeometry, + Ellipsoid, + PrimitivePipeline, + createTaskProcessorWorker) { + "use strict"; + + function createCorridorOutlineGeometry(parameters, transferableObjects) { + var corridorOutlineGeometry = parameters.geometry; + corridorOutlineGeometry._ellipsoid = Ellipsoid.clone(corridorOutlineGeometry._ellipsoid); + + var geometry = CorridorOutlineGeometry.createGeometry(corridorOutlineGeometry); + PrimitivePipeline.transferGeometry(geometry, transferableObjects); + + return { + geometry : geometry, + index : parameters.index + }; + } + + return createTaskProcessorWorker(createCorridorOutlineGeometry); +}); diff --git a/Specs/Core/CorridorOutlineGeometrySpec.js b/Specs/Core/CorridorOutlineGeometrySpec.js new file mode 100644 index 000000000000..a6a67ccf0fd7 --- /dev/null +++ b/Specs/Core/CorridorOutlineGeometrySpec.js @@ -0,0 +1,144 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/CorridorOutlineGeometry', + 'Core/CornerType', + 'Core/Cartesian3', + 'Core/Cartographic', + 'Core/Ellipsoid', + 'Core/Math' + ], function( + CorridorOutlineGeometry, + CornerType, + Cartesian3, + Cartographic, + Ellipsoid, + CesiumMath) { + "use strict"; + /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ + + it('throws without positions', function() { + expect(function() { + return new CorridorOutlineGeometry({}); + }).toThrow(); + }); + + it('throws without 2 unique positions', function() { + expect(function() { + var ellipsoid = Ellipsoid.WGS84; + return CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : ellipsoid.cartographicArrayToCartesianArray([ + Cartographic.fromDegrees(90.0, -30.0), + Cartographic.fromDegrees(90.0, -30.0) + ]), + width: 10000 + })); + }).toThrow(); + }); + + it('throws without width', function() { + expect(function() { + return new CorridorOutlineGeometry({ + positions: [new Cartesian3()] + }); + }).toThrow(); + }); + + it('computes positions', function() { + var ellipsoid = Ellipsoid.WGS84; + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : ellipsoid.cartographicArrayToCartesianArray([ + Cartographic.fromDegrees(90.0, -30.0), + Cartographic.fromDegrees(90.0, -35.0) + ]), + cornerType: CornerType.MITERED, + width : 30000 + })); + + expect(m.attributes.position.values.length).toEqual(3 * 12); + expect(m.indices.length).toEqual(2 * 12); + }); + + it('computes positions extruded', function() { + var ellipsoid = Ellipsoid.WGS84; + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : ellipsoid.cartographicArrayToCartesianArray([ + Cartographic.fromDegrees(90.0, -30.0), + Cartographic.fromDegrees(90.0, -35.0) + ]), + cornerType: CornerType.MITERED, + width : 30000, + extrudedHeight: 30000 + })); + + expect(m.attributes.position.values.length).toEqual(3 * 24); + expect(m.indices.length).toEqual(2 * 12 * 2 + 8); + }); + + it('computes right turn', function() { + var ellipsoid = Ellipsoid.WGS84; + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : ellipsoid.cartographicArrayToCartesianArray([ + Cartographic.fromDegrees(90.0, -30.0), + Cartographic.fromDegrees(90.0, -31.0), + Cartographic.fromDegrees(91.0, -31.0) + ]), + cornerType: CornerType.MITERED, + width : 30000 + })); + + expect(m.attributes.position.values.length).toEqual(3 * 8); + expect(m.indices.length).toEqual(2 * 8); + }); + + it('computes left turn', function() { + var ellipsoid = Ellipsoid.WGS84; + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : ellipsoid.cartographicArrayToCartesianArray([ + Cartographic.fromDegrees(90.0, -30.0), + Cartographic.fromDegrees(90.0, -31.0), + Cartographic.fromDegrees(89.0, -31.0) + ]), + cornerType: CornerType.MITERED, + width : 30000 + })); + + expect(m.attributes.position.values.length).toEqual(3 * 8); + expect(m.indices.length).toEqual(2 * 8); + }); + + it('computes with rounded corners', function() { + var ellipsoid = Ellipsoid.WGS84; + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : 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, + width : 30000 + })); + + var endCaps = 180/5*2; + var corners = 90/5*2; + expect(m.attributes.position.values.length).toEqual(3 * (11 + endCaps + corners)); + expect(m.indices.length).toEqual(2 * (11 + endCaps + corners)); + }); + + it('computes with beveled corners', function() { + var ellipsoid = Ellipsoid.WGS84; + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : 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, + width : 30000 + })); + + expect(m.attributes.position.values.length).toEqual(3 * 10); + expect(m.indices.length).toEqual(2 * 10); + }); +}); \ No newline at end of file