Skip to content

Commit

Permalink
Simplify, wind polygons counterclockwise, make dimensions Vec2
Browse files Browse the repository at this point in the history
  • Loading branch information
bhousel committed Mar 1, 2024
1 parent 192d21a commit f36a55f
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 62 deletions.
14 changes: 7 additions & 7 deletions packages/math/src/Tiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class Tiler {
* const t0 = new Tiler();
* const v0 = new Viewport()
* .transform({ x: 128, y: 128, k: 128 / Math.PI }) // z0
* .dimensions([[0, 0], [256, 256]]); // entire world visible
* .dimensions([256, 256]); // entire world visible
* const result = t0.getTiles(v0);
*
* At zoom 1:
Expand All @@ -91,7 +91,7 @@ export class Tiler {
* const t1 = new Tiler();
* const v1 = new Viewport()
* .transform({ x: 256, y: 256, k: 256 / Math.PI }) // z1
* .dimensions([[0, 0], [512, 512]]); // entire world visible
* .dimensions([512, 512]); // entire world visible
* const result = t1.getTiles(v1);
*
* At zoom 2:
Expand All @@ -118,12 +118,12 @@ export class Tiler {
* const t2 = new Tiler();
* const v2 = new Viewport()
* .transform({ x: 512, y: 512, k: 512 / Math.PI }) // z2
* .dimensions([[0, 0], [1024, 1024]]); // entire world visible
* .dimensions([1024, 1024]); // entire world visible
* const result = t2.getTiles(v2);
*```
*/
getTiles(viewport: Viewport): TileResult {
const dimensions: Vec2[] = viewport.dimensions() as Vec2[];
const [w, h]: Vec2 = viewport.realDimensions() as Vec2;
const translate: Vec2 = viewport.translate() as Vec2;
const scale: number = viewport.scale() as number;

Expand All @@ -137,8 +137,8 @@ export class Tiler {

// perform calculations in "world" pixel coordinates, where origin is top left viewport pixel
const origin: Vec2 = [scale * Math.PI - translate[0], scale * Math.PI - translate[1]];
const viewMin: Vec2 = [origin[0] + dimensions[0][0], origin[1] + dimensions[0][1]];
const viewMax: Vec2 = [origin[0] + dimensions[1][0], origin[1] + dimensions[1][1]];
const viewMin: Vec2 = [origin[0], origin[1]];
const viewMax: Vec2 = [origin[0] + w, origin[1] + h];
const viewExtent: Extent = new Extent(viewMin, viewMax);

// a viewport centered at Null Island, so we can unproject back to lon/lat later
Expand Down Expand Up @@ -205,7 +205,7 @@ export class Tiler {
* @example ```
* const t = new Tiler();
* const v = new Viewport(256, 256, 256 / Math.PI) // z1
* .dimensions([[0, 0], [512, 512]]); // entire world visible
* .dimensions([512, 512]); // entire world visible
* const result = t.getTiles(v);
* const gj = t.getGeoJSON(result); // returns a GeoJSON FeatureCollection
* ```
Expand Down
83 changes: 45 additions & 38 deletions packages/math/src/Viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Extent } from './Extent';
import { numClamp, numWrap } from './number';
import { geoZoomToScale } from './geo';
import { geomRotatePoints } from './geom';
import { Vec2, vecRotate, vecSubtract } from './vector';
import { Vec2, vecRotate, vecScale, vecSubtract } from './vector';


/** The parameters that define the viewport */
Expand Down Expand Up @@ -40,20 +40,20 @@ export interface Transform {
* | E__
* |r/ ''--..__
* |/ r''--..__
* [0,0] A=======================B__
* /‖ ‖ ''F N
* [0,0] A=======================D__
* /‖ ‖ ''H N
* /r‖ ‖ / W._/
* / ‖ + ‖ / /'-E
* / ‖ ‖r/ S
* H__ ‖ ‖/
* ''D=======================C [w,h]
* D__ ‖ ‖/
* ''B=======================C [w,h]
* ''--..__r /|
* ''--,,__ /r|
* ''G |
*/
export class Viewport {
private _transform: Transform;
private _dimensions: Extent;
private _dimensions: Vec2;


/** Constructs a new Viewport
Expand All @@ -65,17 +65,15 @@ export class Viewport {
* const view2 = new Viewport({x: 20, y: 30, k: 512 / Math.PI });
* ```
*/
constructor(transform?: any, dimensions?: Extent) {
constructor(transform?: any, dimensions?: Vec2) {
this._transform = {
x: transform?.x || 0,
y: transform?.y || 0,
k: numClamp(transform?.k || 256 / Math.PI, MIN_K, MAX_K), // constrain to z0..z24, default z1
r: numWrap(transform?.r || 0, 0, TAU) // constrain to 0..2π
};

this._dimensions = dimensions ? new Extent(dimensions) : new Extent([0, 0], [0, 0]);


this._dimensions = dimensions || [0, 0];
}


Expand All @@ -97,7 +95,8 @@ export class Viewport {
const mercatorY: number = Math.log(Math.tan((HALF_PI + phi) / 2));
const point: Vec2 = [mercatorX * k + x, y - mercatorY * k];
if (includeRotation && r) {
return vecRotate(point, r, this._dimensions.center());
const center = vecScale(this._dimensions, 0.5);
return vecRotate(point, r, center);
} else {
return point;
}
Expand All @@ -117,7 +116,8 @@ export class Viewport {
unproject(point: Vec2, includeRotation?: boolean): Vec2 {
const { x, y, k, r } = this._transform;
if (includeRotation && r) {
point = vecRotate(point, -r, this._dimensions.center());
const center = vecScale(this._dimensions, 0.5);
point = vecRotate(point, -r, center);
}
const mercatorX: number = (point[0] - x) / k;
const mercatorY: number = numClamp((y - point[1]) / k, -Math.PI, Math.PI);
Expand Down Expand Up @@ -204,14 +204,14 @@ export class Viewport {
* @returns When argument is provided, sets the viewport min/max dimensions and returns `this` for method chaining.
* Returns the viewport min/max dimensions otherwise
* @example ```
* const view = new Viewport().dimensions([[0, 0], [800, 600]]); // sets viewport dimensions
* p.dimensions(); // gets viewport dimensions - returns [[0, 0], [800, 600]]
* const view = new Viewport().dimensions([800, 600]); // sets viewport dimensions
* p.dimensions(); // gets viewport dimensions - returns [800, 600]
* ```
*/
dimensions(val?: Vec2[]): Vec2[] | Viewport {
if (val === undefined) return [this._dimensions.min, this._dimensions.max];
this._dimensions.min = val[0];
this._dimensions.max = val[1];
dimensions(val?: Vec2): Vec2 | Viewport {
if (val === undefined) return this._dimensions;
this._dimensions[0] = +val[0];
this._dimensions[1] = +val[1];
return this;
}

Expand All @@ -222,50 +222,57 @@ export class Viewport {
* | E__
* |r/ ''--..__
* |/ r''--..__
* [0,0] A=======================B__
* /‖ ‖ ''F N
* [0,0] A=======================D__
* /‖ ‖ ''H N
* /r‖ ‖ / W._/
* / ‖ + ‖ / /'-E
* / ‖ ‖r/ S
* H__ ‖ ‖/
* ''D=======================C [w,h]
* F__ ‖ ‖/
* ''B=======================C [w,h]
* ''--..__r /|
* ''--,,__ /r|
* ''G |
*/
polygon(): Vec2[] {
const [w, h] = this._dimensions;
const r = this._transform.r;

if (r) {
const A: Vec2 = this._dimensions.min;
const B: Vec2 = [this._dimensions.max[0], this._dimensions.min[1]];
const C: Vec2 = this._dimensions.max;
const D: Vec2 = [this._dimensions.min[0], this._dimensions.max[1]];
const sinr = Math.abs(Math.sin(r));
const cosr = Math.abs(Math.cos(r));

const [w, h] = vecSubtract(this._dimensions.max, this._dimensions.min);
const AE: number = Math.abs(w * Math.sin(r));
const CF: number = Math.abs(h * Math.cos(r));
const ae: number = w * sinr;
const af: number = h * cosr;

const E: Vec2 = [A[0] + (AE * Math.sin(r)), A[1] - (AE * Math.cos(r))];
const F: Vec2 = [C[0] + (CF * Math.sin(r)), C[1] - (CF * Math.cos(r))];
const G: Vec2 = [C[0] + (AE * -Math.sin(r)), C[1] - (AE * -Math.cos(r))];
const H: Vec2 = [A[0] + (CF * -Math.sin(r)), A[1] - (CF * -Math.cos(r))];
const ex: number = ae * sinr;
const ey: number = ae * cosr;
const fx: number = af * sinr;
const fy: number = af * cosr;

return [E, H, G, F, E];
const E: Vec2 = [ex, -ey];
const F: Vec2 = [-fx, fy];
const G: Vec2 = [w - ex, h + ey];
const H: Vec2 = [w + fx, h - fy];

return [E, F, G, H, E];
} else {
return this._dimensions.polygon();
return [[0, 0], [0, h], [w, h], [0, w], [0, 0]];
}
}


// needs better name
realDimensions(): Vec2 {
const [w, h] = vecSubtract(this._dimensions.max, this._dimensions.min);
const [w, h] = this._dimensions;
const r = this._transform.r;

if (r) {
const w2 = Math.abs(w * Math.cos(r)) + Math.abs(h * Math.sin(r));
const h2 = Math.abs(h * Math.cos(r)) + Math.abs(w * Math.sin(r));
const sinr = Math.abs(Math.sin(r));
const cosr = Math.abs(Math.cos(r));

const w2 = w * cosr + h * sinr; // ed + fb
const h2 = h * cosr + w * sinr; // af + ae

return [ Math.max(w, w2), Math.max(h, h2) ];
} else {
return [w, h];
Expand Down
22 changes: 11 additions & 11 deletions packages/math/test/Tiler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('math/tiler', () => {
const t = new Tiler().tileSize(TS);
const v = new Viewport()
.transform({ x: HALFTS, y: HALFTS, k: k })
.dimensions([[0, 0], [TS, TS]]); // entire world visible
.dimensions([TS, TS]); // entire world visible

const result = t.getTiles(v);
const tiles = result.tiles;
Expand Down Expand Up @@ -96,7 +96,7 @@ describe('math/tiler', () => {
const t = new Tiler().tileSize(TS);
const v = new Viewport()
.transform({ x: TS, y: TS, k: k })
.dimensions([[0, 0], [TWOTS, TWOTS]]); // entire world visible
.dimensions([TWOTS, TWOTS]); // entire world visible

const result = t.getTiles(v);
const tiles = result.tiles;
Expand Down Expand Up @@ -181,7 +181,7 @@ describe('math/tiler', () => {
const t = new Tiler().tileSize(TS);
const v = new Viewport()
.transform({ x: TWOTS, y: TWOTS, k: k })
.dimensions([[0, 0], [FOURTS, FOURTS]]); // entire world visible
.dimensions([FOURTS, FOURTS]); // entire world visible

const result = t.getTiles(v);
const tiles = result.tiles;
Expand Down Expand Up @@ -265,7 +265,7 @@ describe('math/tiler', () => {
const t = new Tiler().tileSize(TS);
const v = new Viewport()
.transform({ x: HALFTS, y: HALFTS, k: k })
.dimensions([[0, 0], [TS, TS]]);
.dimensions([TS, TS]);

const result = t.getTiles(v);
const tiles = result.tiles;
Expand Down Expand Up @@ -306,7 +306,7 @@ describe('math/tiler', () => {
const t = (new Tiler().tileSize(TS)).margin(1);
const v = new Viewport()
.transform({ x: HALFTS, y: HALFTS, k: k })
.dimensions([[0, 0], [TS, TS]]);
.dimensions([TS, TS]);

const result = t.getTiles(v);
const tiles = result.tiles;
Expand Down Expand Up @@ -359,8 +359,8 @@ describe('math/tiler', () => {
const k = (TS * Math.pow(2, 2)) / TAU; // z2
const t = new Tiler().tileSize(TS);
const v = new Viewport()
.transform({ x: 0, y: HALFTS, k: k })
.dimensions([[1, 0], [TS - 2, TS]]);
.transform({ x: -1, y: HALFTS, k: k })
.dimensions([TS - 2, TS]);

const result = t.getTiles(v);
const tiles = result.tiles;
Expand Down Expand Up @@ -400,8 +400,8 @@ describe('math/tiler', () => {
const k = (TS * Math.pow(2, 2)) / TAU; // z2
const t = (new Tiler().tileSize(TS)).margin(1);
const v = new Viewport()
.transform({ x: 0, y: HALFTS, k: k })
.dimensions([[1, 0], [TS - 2, TS]]);
.transform({ x: -1, y: HALFTS, k: k })
.dimensions([TS - 2, TS]);

const result = t.getTiles(v);
const tiles = result.tiles;
Expand Down Expand Up @@ -455,7 +455,7 @@ describe('math/tiler', () => {
const t = ((new Tiler().tileSize(TS)).margin(1)).skipNullIsland(true);
const v = new Viewport()
.transform({ x: -HALFTS, y: HALFTS, k: k })
.dimensions([[0, 0], [TS, TS]]);
.dimensions([TS, TS]);

const result = t.getTiles(v);
const tiles = result.tiles;
Expand Down Expand Up @@ -493,7 +493,7 @@ describe('math/tiler', () => {
const t = new Tiler().tileSize(TS);
const v = new Viewport()
.transform({ x: HALFTS, y: HALFTS, k: k })
.dimensions([[0, 0], [TS, TS]]);
.dimensions([TS, TS]);

const result = t.getTiles(v);
const geojson = t.getGeoJSON(result);
Expand Down
12 changes: 6 additions & 6 deletions packages/math/test/Viewport.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ describe('math/viewport', () => {
});

it('creates a Viewport with a dimensions param', () => {
const view = new Viewport(null, new Extent([0, 0], [800, 600]));
assert.deepEqual(view.dimensions(), [[0, 0], [800, 600]]);
const view = new Viewport(null, [800, 600]);
assert.deepEqual(view.dimensions(), [800, 600]);
});
});

Expand Down Expand Up @@ -266,8 +266,8 @@ describe('math/viewport', () => {

describe('#dimensions', () => {
it('sets/gets dimensions', () => {
const view = new Viewport().dimensions([[0, 0], [800, 600]]);
assert.deepEqual(view.dimensions(), [[0, 0], [800, 600]]);
const view = new Viewport().dimensions([800, 600]);
assert.deepEqual(view.dimensions(), [800, 600]);
});
});

Expand All @@ -290,7 +290,7 @@ describe('math/viewport', () => {
//
const view = new Viewport()
.transform({ x: 150, y: 100, k: geoZoomToScale(1) })
.dimensions([[0, 0], [300, 200]]);
.dimensions([300, 200]);
const result = view.extent();
assert.ok(result instanceof Extent);
assert.closeTo(result.min[0], -105.46875);
Expand All @@ -317,7 +317,7 @@ describe('math/viewport', () => {
//
const view = new Viewport()
.transform({ x: 150, y: 100, k: geoZoomToScale(1), r: Math.PI / 2 }) // quarter turn clockwise
.dimensions([[0, 0], [300, 200]]);
.dimensions([300, 200]);
const result = view.extent();
assert.ok(result instanceof Extent);
assert.closeTo(result.min[0], -70.3125);
Expand Down

0 comments on commit f36a55f

Please sign in to comment.