From ce36947bdb403ac1a839b270a461230e0befc86c Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 25 Mar 2015 16:47:58 -0600 Subject: [PATCH] Allow orienting with the right-hand rule --- src/ol/geom/flat/orientflatgeom.js | 64 ++++++-- test/spec/ol/geom/flat/orientflatgeom.test.js | 139 +++++++++++++++++- 2 files changed, 186 insertions(+), 17 deletions(-) diff --git a/src/ol/geom/flat/orientflatgeom.js b/src/ol/geom/flat/orientflatgeom.js index 60aaa2e3b1..688b6a3f97 100644 --- a/src/ol/geom/flat/orientflatgeom.js +++ b/src/ol/geom/flat/orientflatgeom.js @@ -29,22 +29,34 @@ ol.geom.flat.orient.linearRingIsClockwise = /** + * Determines if linear rings are oriented. By default, left-hand orientation + * is tested (first ring must be clockwise, remaining rings counter-clockwise). + * To test for right-hand orientation, use the `opt_right` argument. + * * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. - * @param {Array.} ends Ends. + * @param {Array.} ends Array of end indexes. * @param {number} stride Stride. - * @return {boolean} `true` if all rings are correctly oriented, `false` - * otherwise. + * @param {boolean=} opt_right Test for right-hand orientation + * (counter-clockwise exterior ring and clockwise interior rings). + * @return {boolean} Rings are correctly oriented. */ ol.geom.flat.orient.linearRingsAreOriented = - function(flatCoordinates, offset, ends, stride) { + function(flatCoordinates, offset, ends, stride, opt_right) { + var right = goog.isDef(opt_right) ? opt_right : false; var i, ii; for (i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; var isClockwise = ol.geom.flat.orient.linearRingIsClockwise( flatCoordinates, offset, end, stride); - if (i === 0 ? !isClockwise : isClockwise) { - return false; + if (i === 0) { + if ((right && isClockwise) || (!right && !isClockwise)) { + return false; + } + } else { + if ((right && !isClockwise) || (!right && isClockwise)) { + return false; + } } offset = end; } @@ -53,19 +65,24 @@ ol.geom.flat.orient.linearRingsAreOriented = /** + * Determines if linear rings are oriented. By default, left-hand orientation + * is tested (first ring must be clockwise, remaining rings counter-clockwise). + * To test for right-hand orientation, use the `opt_right` argument. + * * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. - * @param {Array.>} endss Endss. + * @param {Array.>} endss Array of array of end indexes. * @param {number} stride Stride. - * @return {boolean} `true` if all rings are correctly oriented, `false` - * otherwise. + * @param {boolean=} opt_right Test for right-hand orientation + * (counter-clockwise exterior ring and clockwise interior rings). + * @return {boolean} Rings are correctly oriented. */ ol.geom.flat.orient.linearRingssAreOriented = - function(flatCoordinates, offset, endss, stride) { + function(flatCoordinates, offset, endss, stride, opt_right) { var i, ii; for (i = 0, ii = endss.length; i < ii; ++i) { if (!ol.geom.flat.orient.linearRingsAreOriented( - flatCoordinates, offset, endss[i], stride)) { + flatCoordinates, offset, endss[i], stride, opt_right)) { return false; } } @@ -74,20 +91,29 @@ ol.geom.flat.orient.linearRingssAreOriented = /** + * Orient coordinates in a flat array of linear rings. By default, rings + * are oriented following the left-hand rule (clockwise for exterior and + * counter-clockwise for interior rings). To orient according to the + * right-hand rule, use the `opt_right` argument. + * * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. * @param {Array.} ends Ends. * @param {number} stride Stride. + * @param {boolean=} opt_right Follow the right-hand rule for orientation. * @return {number} End. */ ol.geom.flat.orient.orientLinearRings = - function(flatCoordinates, offset, ends, stride) { + function(flatCoordinates, offset, ends, stride, opt_right) { + var right = goog.isDef(opt_right) ? opt_right : false; var i, ii; for (i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; var isClockwise = ol.geom.flat.orient.linearRingIsClockwise( flatCoordinates, offset, end, stride); - var reverse = i === 0 ? !isClockwise : isClockwise; + var reverse = i === 0 ? + (right && isClockwise) || (!right && !isClockwise) : + (right && !isClockwise) || (!right && isClockwise); if (reverse) { ol.geom.flat.reverse.coordinates(flatCoordinates, offset, end, stride); } @@ -98,18 +124,24 @@ ol.geom.flat.orient.orientLinearRings = /** + * Orient coordinates in a flat array of linear rings. By default, rings + * are oriented following the left-hand rule (clockwise for exterior and + * counter-clockwise for interior rings). To orient according to the + * right-hand rule, use the `opt_right` argument. + * * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. - * @param {Array.>} endss Endss. + * @param {Array.>} endss Array of array of end indexes. * @param {number} stride Stride. + * @param {boolean=} opt_right Follow the right-hand rule for orientation. * @return {number} End. */ ol.geom.flat.orient.orientLinearRingss = - function(flatCoordinates, offset, endss, stride) { + function(flatCoordinates, offset, endss, stride, opt_right) { var i, ii; for (i = 0, ii = endss.length; i < ii; ++i) { offset = ol.geom.flat.orient.orientLinearRings( - flatCoordinates, offset, endss[i], stride); + flatCoordinates, offset, endss[i], stride, opt_right); } return offset; }; diff --git a/test/spec/ol/geom/flat/orientflatgeom.test.js b/test/spec/ol/geom/flat/orientflatgeom.test.js index c8842eb5b2..f113eee8f4 100644 --- a/test/spec/ol/geom/flat/orientflatgeom.test.js +++ b/test/spec/ol/geom/flat/orientflatgeom.test.js @@ -2,7 +2,7 @@ goog.provide('ol.test.geom.flat.orient'); describe('ol.geom.flat.orient', function() { - describe('ol.geom.flat.orient.linearRingIsClockwise', function() { + describe('ol.geom.flat.orient.linearRingIsClockwise()', function() { it('identifies clockwise rings', function() { var flatCoordinates = [0, 1, 1, 4, 4, 3, 3, 0]; @@ -20,6 +20,143 @@ describe('ol.geom.flat.orient', function() { }); + describe('ol.geom.flat.orient.linearRingsAreOriented()', function() { + var oriented = ol.geom.flat.orient.linearRingsAreOriented; + + var rightCoords = [ + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45 + ]; + + var leftCoords = [ + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45 + ]; + + var ends = [10, 20]; + + it('checks for left-hand orientation by default', function() { + expect(oriented(rightCoords, 0, ends, 2)).to.be(false); + expect(oriented(leftCoords, 0, ends, 2)).to.be(true); + }); + + it('can check for right-hand orientation', function() { + expect(oriented(rightCoords, 0, ends, 2, true)).to.be(true); + expect(oriented(leftCoords, 0, ends, 2, true)).to.be(false); + }); + + }); + + describe('ol.geom.flat.orient.linearRingssAreOriented()', function() { + var oriented = ol.geom.flat.orient.linearRingssAreOriented; + + var rightCoords = [ + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45, + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45 + ]; + + var leftCoords = [ + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45, + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45 + ]; + + var ends = [[10, 20], [30, 40]]; + + it('checks for left-hand orientation by default', function() { + expect(oriented(rightCoords, 0, ends, 2)).to.be(false); + expect(oriented(leftCoords, 0, ends, 2)).to.be(true); + }); + + it('can check for right-hand orientation', function() { + expect(oriented(rightCoords, 0, ends, 2, true)).to.be(true); + expect(oriented(leftCoords, 0, ends, 2, true)).to.be(false); + }); + + }); + + describe('ol.geom.flat.orient.orientLinearRings()', function() { + var orient = ol.geom.flat.orient.orientLinearRings; + + var rightCoords = [ + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45 + ]; + + var leftCoords = [ + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45 + ]; + + var ends = [10, 20]; + + it('orients using the left-hand rule by default', function() { + var rightClone = rightCoords.slice(); + orient(rightClone, 0, ends, 2); + expect(rightClone).to.eql(leftCoords); + + var leftClone = leftCoords.slice(); + orient(leftClone, 0, ends, 2); + expect(leftClone).to.eql(leftCoords); + }); + + it('can orient using the right-hand rule', function() { + var rightClone = rightCoords.slice(); + orient(rightClone, 0, ends, 2, true); + expect(rightClone).to.eql(rightCoords); + + var leftClone = leftCoords.slice(); + orient(leftClone, 0, ends, 2, true); + expect(leftClone).to.eql(rightCoords); + }); + + }); + + describe('ol.geom.flat.orient.orientLinearRingss()', function() { + var orient = ol.geom.flat.orient.orientLinearRingss; + + var rightCoords = [ + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45, + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45 + ]; + + var leftCoords = [ + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45, + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45 + ]; + + var ends = [[10, 20], [30, 40]]; + + it('orients using the left-hand rule by default', function() { + var rightClone = rightCoords.slice(); + orient(rightClone, 0, ends, 2); + expect(rightClone).to.eql(leftCoords); + + var leftClone = leftCoords.slice(); + orient(leftClone, 0, ends, 2); + expect(leftClone).to.eql(leftCoords); + }); + + it('can orient using the right-hand rule', function() { + var rightClone = rightCoords.slice(); + orient(rightClone, 0, ends, 2, true); + expect(rightClone).to.eql(rightCoords); + + var leftClone = leftCoords.slice(); + orient(leftClone, 0, ends, 2, true); + expect(leftClone).to.eql(rightCoords); + }); + + }); + + }); goog.require('ol.geom.flat.orient');