Skip to content

Commit

Permalink
Merge pull request #502 from AnalyticalGraphicsInc/wrapLongitude
Browse files Browse the repository at this point in the history
Wrap Longitude
  • Loading branch information
mramato committed Feb 2, 2013
2 parents d8f2ff2 + 77fd8d2 commit ff5b561
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 69 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ Beta Releases

### b14 - 2013-xx-xx

* Added a line segment-ray intersection test to `IntersectionTests`.
* Fixed an issue where a `PolylineCollection` with a model matrix other than the identity would be incorrectly rendered in 2D and Columbus view.

### b13 - 2013-02-01

* Breaking changes:
* The combined `Cesium.js` file and other required files are now created in `Build/Cesium` and `Build/CesiumUnminified` folders.
* The Web Worker files needed when using the combined `Cesium.js` file are now in a `Workers` subdirectory.
Expand Down
71 changes: 71 additions & 0 deletions Source/Core/IntersectionTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -355,5 +355,76 @@ define([
return undefined;
};

var lineSegmentPlaneDifference = new Cartesian3();

/**
* Computes the intersection of a line segment and a plane.
* @memberof IntersectionTests
*
* @param {Cartesian3} endPoint0 An end point of the line segment.
* @param {Cartesian3} endPoint1 The other end point of the line segment.
* @param {Cartesian3} planeNormal The plane normal.
* @param {Number} planeD The distance from the plane to the origin.
* @param {Cartesian3} [result] The object onto which to store the result.
* @returns {Cartesian3} The intersection point or undefined if there is no intersection.
*
* @exception {DeveloperError} endPoint0 is required.
* @exception {DeveloperError} endPoint1 is required.
* @exception {DeveloperError} planeNormal is required.
* @exception {DeveloperError} planeD is required.
*
* @example
* var origin = ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883, 0.0));
* var normal = ellipsoid.geodeticSurfaceNormal(origin);
* var constant = -Cartesian3.dot(normal, origin);
*
* var p0 = new Cartesian3(...);
* var p1 = new Cartesian3(...);
*
* // find the intersection of the line segment from p0 to p1 and the tangent plane at origin.
* var intersection = IntersectionTests.lineSegmentPlane(p0, p1, normal, constant);
*/
IntersectionTests.lineSegmentPlane = function(endPoint0, endPoint1, planeNormal, planeD, result) {
if (typeof endPoint0 === 'undefined') {
throw new DeveloperError('endPoint0 is required.');
}

if (typeof endPoint1 === 'undefined') {
throw new DeveloperError('endPoint1 is required.');
}

if (typeof planeNormal === 'undefined') {
throw new DeveloperError('planeNormal is required.');
}

if (typeof planeD === 'undefined') {
throw new DeveloperError('planeD is required.');
}

var difference = Cartesian3.subtract(endPoint1, endPoint0, lineSegmentPlaneDifference);
var nDotDiff = Cartesian3.dot(planeNormal, difference);

// check if the segment and plane are parallel
if (Math.abs(nDotDiff) < CesiumMath.EPSILON6) {
return undefined;
}

var nDotP0 = Cartesian3.dot(planeNormal, endPoint0);
var t = -(planeD + nDotP0) / nDotDiff;

// intersection only if t is in [0, 1]
if (t < 0.0 || t > 1.0) {
return undefined;
}

// intersection is endPoint0 + t * (endPoint1 - endPoint0)
if (typeof result === 'undefined') {
result = new Cartesian3();
}
Cartesian3.multiplyByScalar(difference, t, result);
Cartesian3.add(endPoint0, result, result);
return result;
};

return IntersectionTests;
});
151 changes: 90 additions & 61 deletions Source/Core/PolylinePipeline.js
Original file line number Diff line number Diff line change
@@ -1,89 +1,118 @@
/*global define*/
define(['./Cartographic',
'./Cartesian3'
define([
'./defaultValue',
'./Cartesian3',
'./Cartesian4',
'./IntersectionTests',
'./Matrix4'
], function(
Cartographic,
Cartesian3) {
defaultValue,
Cartesian3,
Cartesian4,
IntersectionTests,
Matrix4) {
"use strict";

/**
* DOC_TBA
*
* @exports PolylinePipeline
*/
var PolylinePipeline = {
/**
* Breaks a {@link Polyline} into segments such that it does not cross the &plusmn;180 degree meridian of an ellipsoid.
*
* @param {Ellipsoid} ellipsoid The ellipsoid to wrap around.
* @param {Array} positions The polyline's Cartesian positions.
*
* @returns An array of polyline segment objects containing the Cartesian and {@link Cartographic} positions and indices.
*
* @see Polyline
* @see PolylineCollection
*
* @example
* var polylines = new PolylineCollection();
* polylines.add(...);
* var positions = polylines.get(0).getPositions();
* var segments = PolylinePipeline.wrapLongitude(ellipsoid, positions);
*/
wrapLongitude : function(ellipsoid, positions) {
var segments = [];

if (positions && (positions.length > 0)) {
var length = positions.length;

var currentSegment = [{
cartesian : Cartesian3.clone(positions[0]),
cartographic : ellipsoid.cartesianToCartographic(positions[0]),
index : 0
}];

var prev = currentSegment[0].cartographic;

for ( var i = 1; i < length; ++i) {
var cur = ellipsoid.cartesianToCartographic(positions[i]);

if (Math.abs(prev.longitude - cur.longitude) > Math.PI) {
var interpolatedLongitude = prev.longitude < 0.0 ? -Math.PI : Math.PI;
var longitude = cur.longitude + (2.0 * interpolatedLongitude);
var ratio = (interpolatedLongitude - prev.longitude) / (longitude - prev.longitude);
var interpolatedLatitude = prev.latitude + (cur.latitude - prev.latitude) * ratio;
var interpolatedHeight = prev.height + (cur.height - prev.height) * ratio;
var PolylinePipeline = {};

var wrapLongitudeInversMatrix = new Matrix4();
var wrapLongitudeOrigin = new Cartesian4();
var wrapLongitudeXZNormal = new Cartesian4();
var wrapLongitudeYZNormal = new Cartesian4();
var wrapLongitudeIntersection = new Cartesian3();
var wrapLongitudeOffset = new Cartesian3();

/**
* Breaks a {@link Polyline} into segments such that it does not cross the &plusmn;180 degree meridian of an ellipsoid.
* @memberof PolylinePipeline
*
* @param {Array} positions The polyline's Cartesian positions.
* @param {Matrix4} [modelMatrix=Matrix4.IDENTITY] The polyline's model matrix. Assumed to be an affine
* transformation matrix, where the upper left 3x3 elements are a rotation matrix, and
* the upper three elements in the fourth column are the translation. The bottom row is assumed to be [0, 0, 0, 1].
* The matrix is not verified to be in the proper form.
*
* @returns An array of polyline segment objects containing the Cartesian position and indices.
*
* @see Polyline
* @see PolylineCollection
*
* @example
* var polylines = new PolylineCollection();
* var polyline = polylines.add(...);
* var positions = polyline.getPositions();
* var modelMatrix = polylines.modelMatrix;
* var segments = PolylinePipeline.wrapLongitude(positions, modelMatrix);
*/
PolylinePipeline.wrapLongitude = function(positions, modelMatrix) {
var segments = [];

if (typeof positions !== 'undefined' && positions.length > 0) {
modelMatrix = defaultValue(modelMatrix, Matrix4.IDENTITY);
var inverseModelMatrix = Matrix4.inverseTransformation(modelMatrix, wrapLongitudeInversMatrix);

var origin = Matrix4.multiplyByPoint(inverseModelMatrix, Cartesian3.ZERO, wrapLongitudeOrigin);
var xzNormal = Matrix4.multiplyByVector(inverseModelMatrix, Cartesian4.UNIT_Y, wrapLongitudeXZNormal);
var xzConstant = -Cartesian3.dot(xzNormal, origin);
var yzNormal = Matrix4.multiplyByVector(inverseModelMatrix, Cartesian4.UNIT_X, wrapLongitudeYZNormal);
var yzConstant = -Cartesian3.dot(yzNormal, origin);

var currentSegment = [{
cartesian : Cartesian3.clone(positions[0]),
index : 0
}];
var prev = currentSegment[0].cartesian;

var length = positions.length;
for ( var i = 1; i < length; ++i) {
var cur = positions[i];

// intersects the IDL if either endpoint is on the negative side of the yz-plane
if (Cartesian3.dot(prev, yzNormal) + yzConstant < 0.0 || Cartesian3.dot(cur, yzNormal) + yzNormal < 0.0) {
// and intersects the xz-plane
var intersection = IntersectionTests.lineSegmentPlane(prev, cur, xzNormal, xzConstant, wrapLongitudeIntersection);
if (typeof intersection !== 'undefined') {
// move point on the xz-plane slightly away from the plane
var offset = Cartesian3.multiplyByScalar(xzNormal, 5.0e-9, wrapLongitudeOffset);
if (Cartesian3.dot(prev, xzNormal) + xzConstant < 0.0) {
Cartesian3.negate(offset, offset);
}

currentSegment.push({
cartesian : ellipsoid.cartographicToCartesian(new Cartographic(interpolatedLongitude, interpolatedLatitude, interpolatedHeight)),
cartographic : new Cartographic(interpolatedLongitude, interpolatedLatitude, interpolatedHeight),
cartesian : Cartesian3.add(intersection, offset),
index : i
});
segments.push(currentSegment);

Cartesian3.negate(offset, offset);

currentSegment = [];
currentSegment.push({
cartesian : ellipsoid.cartographicToCartesian(new Cartographic(-interpolatedLongitude, interpolatedLatitude, interpolatedHeight)),
cartographic : new Cartographic(-interpolatedLongitude, interpolatedLatitude, interpolatedHeight),
cartesian : Cartesian3.add(intersection, offset),
index : i
});
}

currentSegment.push({
cartesian : Cartesian3.clone(positions[i]),
cartographic : ellipsoid.cartesianToCartographic(positions[i]),
index : i
});

prev = cur.clone();
}

if (currentSegment.length > 1) {
segments.push(currentSegment);
}
currentSegment.push({
cartesian : Cartesian3.clone(positions[i]),
index : i
});

prev = cur;
}

return segments;
if (currentSegment.length > 1) {
segments.push(currentSegment);
}
}

return segments;
};

return PolylinePipeline;
Expand Down
4 changes: 2 additions & 2 deletions Source/Scene/Polyline.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ define([
return positions;
};

Polyline.prototype._createSegments = function(ellipsoid) {
return PolylinePipeline.wrapLongitude(ellipsoid, this.getPositions());
Polyline.prototype._createSegments = function(modelMatrix) {
return PolylinePipeline.wrapLongitude(this.getPositions(), modelMatrix);
};

Polyline.prototype._setSegments = function(segments) {
Expand Down
4 changes: 2 additions & 2 deletions Source/Scene/PolylineCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ define([
var changedProperties = polyline._propertiesChanged;
if (changedProperties[POSITION_INDEX]) {
if (intersectsIDL(polyline)) {
var newSegments = polyline._createSegments(this._projection._ellipsoid);
var newSegments = polyline._createSegments(this.modelMatrix);
if (polyline._segmentsLengthChanged(newSegments)) {
createVertexArrays = true;
break;
Expand Down Expand Up @@ -1129,7 +1129,7 @@ define([
if (this.mode === SceneMode.SCENE3D || !intersectsIDL(polyline)) {
return polyline.getPositions().length;
}
var segments = polyline._createSegments(this.ellipsoid);
var segments = polyline._createSegments(this.modelMatrix);
return polyline._setSegments(segments);
};

Expand Down
73 changes: 73 additions & 0 deletions Specs/Core/IntersectionTestsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,77 @@ defineSuite([
var ray = new Ray(Cartesian3.ZERO, Cartesian3.UNIT_Z);
expect(IntersectionTests.grazingAltitudeLocation(ray, ellipsoid)).not.toBeDefined();
});

it('lineSegmentPlane intersects', function() {
var planeNormal = Cartesian3.UNIT_Y.clone();
var pointOnPlane = new Cartesian3(0.0, 2.0, 0.0);
var planeConstant = -Cartesian3.dot(planeNormal, pointOnPlane);

var endPoint0 = new Cartesian3(1.0, 1.0, 0.0);
var endPoint1 = new Cartesian3(1.0, 3.0, 0.0);

var intersectionPoint = IntersectionTests.lineSegmentPlane(endPoint0, endPoint1, planeNormal, planeConstant);

expect(intersectionPoint).toEqual(new Cartesian3(1.0, 2.0, 0.0));
});

it('lineSegmentPlane misses (entire segment behind plane)', function() {
var planeNormal = new Cartesian3(1.0, 0.0, 0.0);
var planeConstant = 0.0;

var endPoint0 = new Cartesian3(-2.0, 0.0, 0.0);
var endPoint1 = new Cartesian3(-5.0, 0.0, 0.0);

var intersectionPoint = IntersectionTests.lineSegmentPlane(endPoint0, endPoint1, planeNormal, planeConstant);

expect(intersectionPoint).not.toBeDefined();
});

it('lineSegmentPlane misses (entire segment in front of plane)', function() {
var planeNormal = new Cartesian3(1.0, 0.0, 0.0);
var planeConstant = 0.0;

var endPoint0 = new Cartesian3(5.0, 0.0, 0.0);
var endPoint1 = new Cartesian3(2.0, 0.0, 0.0);

var intersectionPoint = IntersectionTests.lineSegmentPlane(endPoint0, endPoint1, planeNormal, planeConstant);

expect(intersectionPoint).not.toBeDefined();
});

it('lineSegmentPlane misses (parallel)', function() {
var planeNormal = new Cartesian3(1.0, 0.0, 0.0);
var planeConstant = 0.0;

var endPoint0 = new Cartesian3(0.0, -1.0, 0.0);
var endPoint1 = new Cartesian3(0.0, 1.0, 0.0);

var intersectionPoint = IntersectionTests.lineSegmentPlane(endPoint0, endPoint1, planeNormal, planeConstant);

expect(intersectionPoint).not.toBeDefined();
});

it('lineSegmentPlane throws without endPoint0', function() {
expect(function() {
IntersectionTests.lineSegmentPlane();
}).toThrow();
});

it('lineSegmentPlane throws without endPoint1', function() {
expect(function() {
IntersectionTests.lineSegmentPlane(new Cartesian3());
}).toThrow();
});

it('lineSegmentPlane throws without planeNormal', function() {
expect(function() {
IntersectionTests.lineSegmentPlane(new Cartesian3(), new Cartesian3());
}).toThrow();
});

it('lineSegmentPlane throws without planeD', function() {
expect(function() {
IntersectionTests.lineSegmentPlane(new Cartesian3(), new Cartesian3(), new Cartesian3());
}).toThrow();
});
});
Loading

0 comments on commit ff5b561

Please sign in to comment.