From de660fb35b2aa3700d2e94f01076aa6ccfa5aba0 Mon Sep 17 00:00:00 2001 From: Florian Bischof Date: Sat, 18 Jun 2022 15:01:15 +0200 Subject: [PATCH] Extend Bounds to have the same functions as LatLngBounds (#7882) * Extend Bounds to have the same functions as LatLngBounds * Fix lint --- spec/suites/geometry/BoundsSpec.js | 38 ++++++++++++++++ src/geometry/Bounds.js | 70 +++++++++++++++++++++++++----- 2 files changed, 96 insertions(+), 12 deletions(-) diff --git a/spec/suites/geometry/BoundsSpec.js b/spec/suites/geometry/BoundsSpec.js index b63c5eb0c..cd2c40981 100644 --- a/spec/suites/geometry/BoundsSpec.js +++ b/spec/suites/geometry/BoundsSpec.js @@ -35,6 +35,29 @@ describe('Bounds', function () { expect(b.min).to.eql(L.point(14, 12)); expect(b.max).to.eql(L.point(30, 50)); }); + + it('extends the bounds by given bounds', function () { + a.extend([20, 50]); + expect(a.max).to.eql(L.point(30, 50)); + }); + + it('extends the bounds by given bounds', function () { + a.extend([[20, 50], [8, 40]]); + expect(a.getBottomLeft()).to.eql(L.point(8, 50)); + }); + + it('extends the bounds by undefined', function () { + expect(a.extend()).to.eql(a); + }); + + it('extends the bounds by raw object', function () { + a.extend({x: 20, y: 50}); + expect(a.max).to.eql(L.point(30, 50)); + }); + + it('extend the bounds by an empty bounds object', function () { + expect(a.extend(L.bounds())).to.eql(a); + }); }); describe('#getCenter', function () { @@ -43,6 +66,13 @@ describe('Bounds', function () { }); }); + describe('#pad', function () { + it('pads the bounds by a given ratio', function () { + var bounds = a.pad(0.5); + expect(bounds).to.eql(L.bounds([[6, -2], [38, 54]])); + }); + }); + describe('#contains', function () { it('contains other bounds or point', function () { a.extend([50, 10]); @@ -135,4 +165,12 @@ describe('Bounds', function () { expect(bounds).to.eql(a); }); }); + + describe('#equals', function () { + it('returns true if bounds equal', function () { + expect(a.equals([[14, 12], [30, 40]])).to.eql(true); + expect(a.equals([[14, 13], [30, 40]])).to.eql(false); + expect(a.equals(null)).to.eql(false); + }); + }); }); diff --git a/src/geometry/Bounds.js b/src/geometry/Bounds.js index d33c949ee..a99fe3dd9 100644 --- a/src/geometry/Bounds.js +++ b/src/geometry/Bounds.js @@ -38,21 +38,36 @@ export function Bounds(a, b) { Bounds.prototype = { // @method extend(point: Point): this // Extends the bounds to contain the given point. - extend: function (point) { // (Point) - point = toPoint(point); + + // @alternative + // @method extend(otherBounds: Bounds): this + // Extend the bounds to contain the given bounds + extend: function (obj) { + var min2, max2; + if (!obj) { return this; } + + if (obj instanceof Point || typeof obj[0] === 'number' || 'x' in obj) { + min2 = max2 = toPoint(obj); + } else { + obj = toBounds(obj); + min2 = obj.min; + max2 = obj.max; + + if (!min2 || !max2) { return this; } + } // @property min: Point // The top left corner of the rectangle. // @property max: Point // The bottom right corner of the rectangle. if (!this.min && !this.max) { - this.min = point.clone(); - this.max = point.clone(); + this.min = min2.clone(); + this.max = max2.clone(); } else { - this.min.x = Math.min(point.x, this.min.x); - this.max.x = Math.max(point.x, this.max.x); - this.min.y = Math.min(point.y, this.min.y); - this.max.y = Math.max(point.y, this.max.y); + this.min.x = Math.min(min2.x, this.min.x); + this.max.x = Math.max(max2.x, this.max.x); + this.min.y = Math.min(min2.y, this.min.y); + this.max.y = Math.max(max2.y, this.max.y); } return this; }, @@ -60,7 +75,7 @@ Bounds.prototype = { // @method getCenter(round?: Boolean): Point // Returns the center point of the bounds. getCenter: function (round) { - return new Point( + return toPoint( (this.min.x + this.max.x) / 2, (this.min.y + this.max.y) / 2, round); }, @@ -68,13 +83,13 @@ Bounds.prototype = { // @method getBottomLeft(): Point // Returns the bottom-left point of the bounds. getBottomLeft: function () { - return new Point(this.min.x, this.max.y); + return toPoint(this.min.x, this.max.y); }, // @method getTopRight(): Point // Returns the top-right point of the bounds. getTopRight: function () { // -> Point - return new Point(this.max.x, this.min.y); + return toPoint(this.max.x, this.min.y); }, // @method getTopLeft(): Point @@ -154,9 +169,40 @@ Bounds.prototype = { return xOverlaps && yOverlaps; }, + // @method isValid(): Boolean + // Returns `true` if the bounds are properly initialized. isValid: function () { return !!(this.min && this.max); - } + }, + + + // @method pad(bufferRatio: Number): Bounds + // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. + // For example, a ratio of 0.5 extends the bounds by 50% in each direction. + // Negative values will retract the bounds. + pad: function (bufferRatio) { + var min = this.min, + max = this.max, + heightBuffer = Math.abs(min.x - max.x) * bufferRatio, + widthBuffer = Math.abs(min.y - max.y) * bufferRatio; + + + return toBounds( + toPoint(min.x - heightBuffer, min.y - widthBuffer), + toPoint(max.x + heightBuffer, max.y + widthBuffer)); + }, + + + // @method equals(otherBounds: Bounds, maxMargin?: Number): Boolean + // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number. + equals: function (bounds) { + if (!bounds) { return false; } + + bounds = toBounds(bounds); + + return this.min.equals(bounds.getTopLeft()) && + this.max.equals(bounds.getBottomRight()); + }, };