Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap Longitude #502

Merged
merged 3 commits into from
Feb 2, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not an @example?

* @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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bummer, the Plane I added is still in a branch. No need to address it now.

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is only one line now. Do we still need it? Actually, the whole segment management in PolylineCollection.js could use some work. However, we can hold off if we think there are bigger changes to come with the material improvements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should wait until we start the material improvements.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeap, OK with me.

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