segment/SegmentCube.tstypescript
import { Entity } from "../entity/Entity";
import { Extent } from "../Extent";
import { Layer } from "../layer/Layer";
import { LonLat } from "../LonLat";
import { CUBE_BOTTOM, CUBE_OPSIDE, CUBE_TOP } from "../proj/cube";
import type { CubeProj } from "../proj/cube";
import { Vec3 } from "../math/Vec3";
import { PlanetCamera } from "../camera/PlanetCamera";
import { Node } from "../quadTree/Node";
import { E, N, S, W } from "../quadTree/quadTree";
import { QuadTreeStrategy } from "../quadTree";
import { Segment } from "./Segment";
/**
* Planet segment of a cube face. Its extent is in the native coordinates of the cube, see proj/cube.
* @class
* @extends {Segment}
*/
class SegmentCube extends Segment {
public override _projection: CubeProj;
constructor(node: Node, quadTreeStrategy: QuadTreeStrategy, tileZoom: number, extent: Extent) {
super(node, quadTreeStrategy, tileZoom, extent);
this._projection = quadTreeStrategy.projection as CubeProj;
}
public override getLonLat(lon: number, lat: number): LonLat {
return this._projection.toLonLat(lon, lat, this.tileGroup);
}
protected override _assignTileIndexes() {
const e = this._extent,
face = Math.floor(e.getCenter().lon);
this.tileGroup = face;
this.tileX = Math.round((e.southWest.lon - face) / e.getWidth());
this.tileY = Math.round((1.0 - e.northEast.lat) / e.getHeight());
this.tileIndex = Layer.getTileIndex(this.tileX, this.tileY, this.tileZoom, face);
const x = this.tileX,
y = this.tileY,
last = this.powTileZoom - 1;
this.opside =
CUBE_OPSIDE[face][
(y === 0 ? 1 << N : 0) | (x === last ? 1 << E : 0) | (y === last ? 1 << S : 0) | (x === 0 ? 1 << W : 0)
];
}
/**
* The points of the extent that reach the farthest in any direction: its corners and the ones that are the
* closest to the center of the face.
*/
protected _getExtremePoints(): LonLat[] {
const e = this._extent,
west = e.southWest.lon,
south = e.southWest.lat,
east = e.northEast.lon,
north = e.northEast.lat,
centerLon = Math.min(Math.max(this.tileGroup + 0.5, west), east),
centerLat = Math.min(Math.max(0.5, south), north),
points = [];
for (const lat of [south, centerLat, north]) {
for (const lon of [west, centerLon, east]) {
points.push(new LonLat(lon, lat));
}
}
return points;
}
/**
* The lon lat rectangle around the tile. An extent can't wrap, so the tile with a pole or on the antimeridian
* takes all the longitudes.
*/
public override _setExtentLonLat() {
let lonMin = 180,
lonMax = -180,
latMin = 90,
latMax = -90;
for (const p of this._getExtremePoints()) {
const ll = this.getLonLat(p.lon, p.lat);
lonMin = Math.min(lonMin, ll.lon);
lonMax = Math.max(lonMax, ll.lon);
latMin = Math.min(latMin, ll.lat);
latMax = Math.max(latMax, ll.lat);
}
const face = this.tileGroup,
e = this._extent,
hasPole =
(face === CUBE_TOP || face === CUBE_BOTTOM) &&
e.southWest.lon <= face + 0.5 &&
e.northEast.lon >= face + 0.5 &&
e.southWest.lat <= 0.5 &&
e.northEast.lat >= 0.5;
if (hasPole || lonMax - lonMin > 180) {
lonMin = -180;
lonMax = 180;
}
this._extentLonLat = new Extent(new LonLat(lonMin, latMin), new LonLat(lonMax, latMax));
}
public override projectNative(lonLat: LonLat): LonLat {
return this._projection.fromLonLat(lonLat);
}
public override getInsideLonLat(obj: Entity | PlanetCamera): LonLat {
return this._projection.fromLonLat(obj._lonLat);
}
public override isLonLatInside(lonLat: LonLat): boolean {
return this._extent.isInside(this._projection.fromLonLat(lonLat));
}
protected override _getExtentCorners(): [Vec3, Vec3, Vec3, Vec3] {
const e = this.planet.ellipsoid,
sw = this._extent.southWest,
ne = this._extent.northEast;
return [
e.lonLatToCartesian(this.getLonLat(sw.lon, sw.lat)),
e.lonLatToCartesian(this.getLonLat(sw.lon, ne.lat)),
e.lonLatToCartesian(this.getLonLat(ne.lon, ne.lat)),
e.lonLatToCartesian(this.getLonLat(ne.lon, sw.lat))
];
}
public override createBoundsByExtent() {
this._createExtentNormals();
const e = this.planet.ellipsoid,
min = new Vec3(Infinity, Infinity, Infinity),
max = new Vec3(-Infinity, -Infinity, -Infinity);
for (const p of this._getExtremePoints()) {
const v = e.lonLatToCartesian(this.getLonLat(p.lon, p.lat));
min.set(Math.min(min.x, v.x), Math.min(min.y, v.y), Math.min(min.z, v.z));
max.set(Math.max(max.x, v.x), Math.max(max.y, v.y), Math.max(max.z, v.z));
}
this.setBoundingVolume3v(min, max);
}
// A layer over the antimeridian goes on over 180 degrees,
// so it is taken around the world to the tile
public override layerOverlap(layer: Layer): boolean {
const segmentExtent = this._extentLonLat,
layerExtent = layer._extent;
if (segmentExtent.overlaps(layerExtent)) {
return true;
}
if (
segmentExtent.southWest.lat > layerExtent.northEast.lat ||
segmentExtent.northEast.lat < layerExtent.southWest.lat
) {
return false;
}
const lonShift = this._getCyclicLonShift(layerExtent, segmentExtent, 360.0);
return (
segmentExtent.southWest.lon <= layerExtent.northEast.lon + lonShift &&
segmentExtent.northEast.lon >= layerExtent.southWest.lon + lonShift
);
}
// The night and specular textures are in the Web Mercator
protected override _assignGlobalTextureCoordinates() {}
}
export { SegmentCube };