Skip to content
Draft
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
36 changes: 36 additions & 0 deletions packages/engine/Source/Core/Rectangle.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,42 @@ Object.defineProperties(Rectangle.prototype, {
*/
Rectangle.packedLength = 4;

/**
* XXX_DRAPING
*
* Print the message and the rectangle for debugging, assuming that it stores values
* that are given in radians, and converting them to degrees for printing.
*
* Note that a rectangle may contain arbitrary values in arbitrary units (degrees, meters,
* furlongs, who knows). If the printed values do not make sense, try debugPrintDirectly.
*
* @param {string} message The message
* @param {Rectangle} rectangle The rectangle
*/
Rectangle.debugPrintRadiansAsDegrees = function (message, rectangle) {
console.log(message, " (converted from radians to degrees)");
console.log(" E ", CesiumMath.toDegrees(rectangle.east));
console.log(" S ", CesiumMath.toDegrees(rectangle.south));
console.log(" W ", CesiumMath.toDegrees(rectangle.west));
console.log(" N ", CesiumMath.toDegrees(rectangle.north));
};
/**
* XXX_DRAPING
*
* Print the message and the rectangle for debugging, directly, without assuming
* that the input is actually storing values in radians.
*
* @param {string} message The message
* @param {Rectangle} rectangle The rectangle
*/
Rectangle.debugPrintDirectly = function (message, rectangle) {
console.log(message, " (printed directly)");
console.log(" W ", rectangle.west);
console.log(" S ", rectangle.south);
console.log(" E ", rectangle.east);
console.log(" N ", rectangle.north);
};

/**
* Stores the provided instance into the provided array.
*
Expand Down
192 changes: 179 additions & 13 deletions packages/engine/Source/Scene/Model/ImageryCoverage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import defined from "../../Core/defined.js";
import CesiumMath from "../../Core/Math.js";
import Rectangle from "../../Core/Rectangle.js";

import CartesianRectangle from "./CartesianRectangle.js";
Expand All @@ -7,7 +8,7 @@ const imageryBoundsScratch = new Rectangle();
const overlappedRectangleScratch = new Rectangle();
const clippedRectangleScratch = new Rectangle();
const nativeInputRectangleScratch = new Rectangle();
const nativeImageryBoundsScratch = new Rectangle();
//const nativeImageryBoundsScratch = new Rectangle(); XXX_DRAPING unused
const nativeClippedImageryBoundsScratch = new Rectangle();

/**
Expand All @@ -31,7 +32,8 @@ const nativeClippedImageryBoundsScratch = new Rectangle();
* <code>ImageryLayer.prototype._createTileImagerySkeletons</code>
* See https://github.com/CesiumGS/cesium/blob/5eaa2280f495d8f300d9e1f0497118c97aec54c8/packages/engine/Source/Scene/ImageryLayer.js#L700
* An instance of this class roughly corresponds to the <code>TileImagery</code>
* that is created there.
* that is created there. Questions about the implementation here should
* be addressed to the original author.
*
* @private
*/
Expand Down Expand Up @@ -162,11 +164,15 @@ class ImageryCoverage {
inputRectangle,
nativeInputRectangle,
);

// XXX_DRAPING Was unused?
/*
const nativeImageryBounds = nativeImageryBoundsScratch;
imageryTilingScheme.rectangleToNativeRectangle(
imageryBounds,
nativeImageryBounds,
);
*/

// A function that returns an imagery rectangle, based on (x, y, level),
// clipped to the imagery bounds (or undefined if there is no intersection
Expand Down Expand Up @@ -236,7 +242,12 @@ class ImageryCoverage {
* the X/Y coordinates of the imagery that is overlapped by the given
* input rectangle, based on the given imagery rectangle.
*
* Extracted from _createTileImagerySkeletons.
* This was largely extracted from _createTileImagerySkeletons.
*
* Note that the resulting rectangle will always obey the `minX<=maxX`
* constraint, but `maxX` may be larger than the number of tiles along
* x in the given imagery level. The receiver is responsible for
* wrapping the results into the valid range.
*
* @param {Rectangle} inputRectangle The input rectangle
* @param {Rectangle} imageryBounds The imagery bounds
Expand All @@ -254,12 +265,22 @@ class ImageryCoverage {
inputRectangle,
imageryBounds,
);

// XXX_DRAPING: The wrapping that is happening here has to be
// verified. The "computeOverlappedRectangle" is doing some
// obscure "clamping" under certain conditions, as extracted
// from "_createTileImagerySkeletons", which may cause results
// that do not make sense here.
const nw = Rectangle.northwest(overlappedRectangle);
nw.longitude = CesiumMath.convertLongitudeRange(nw.longitude);
const northwestTileCoordinates = imageryTilingScheme.positionToTileXY(
Rectangle.northwest(overlappedRectangle),
nw,
imageryLevel,
);
const se = Rectangle.southeast(overlappedRectangle);
se.longitude = CesiumMath.convertLongitudeRange(se.longitude);
const southeastTileCoordinates = imageryTilingScheme.positionToTileXY(
Rectangle.southeast(overlappedRectangle),
se,
imageryLevel,
);

Expand All @@ -269,6 +290,7 @@ class ImageryCoverage {
result.maxX = southeastTileCoordinates.x;
result.maxY = southeastTileCoordinates.y;

/*/ XXX_DRAPING We have to get rid of this...
// As extracted from _createTileImagerySkeletons:
// If the southeast corner of the rectangle lies very close to the north or west side
// of the southeast tile, we don't actually need the southernmost or easternmost
Expand Down Expand Up @@ -314,6 +336,24 @@ class ImageryCoverage {
if (deltaEast < veryCloseX && result.maxX > result.minX) {
--result.maxX;
}
//*/

// For the case that the inputRectangle crosses the antimeridian,
// "west" (the westernmost side) may be 179° and "east" (the
// easternmost side) may be -179°.
// The "imageryTilingScheme.positionToTileXY" call returns coordinates
// that are wrapped into the valid range individually (!). This
// means that the tile x-coordinate for "west" (minX) may be larger
// than the tile x-coordinate for "east" (maxX). In this case,
// wrap the "maxX" around, based on the number of tiles along x.
if (result.minX > result.maxX) {
const numTilesX =
imageryTilingScheme.getNumberOfXTilesAtLevel(imageryLevel);
result.maxX += numTilesX;
}

// XXX_DRAPING
console.log("ImageryCoverage._computeImageryRange: result ", result);

return result;
}
Expand Down Expand Up @@ -383,6 +423,10 @@ class ImageryCoverage {
* the texture coordinates that are contained in the given range of
* imagery tile coordinates, referring to the given input rectangle.
*
* The given imageryRange may contain x-coordinates that are larger than the
* number of tiles along x for the given imagery level. This method will
* wrap the coordinates to be in a valid range.
*
* @param {ImageryLayer} imageryLayer The imagery layer
* @param {CartesianRectangle} imageryRange The range of imagery tile coordinates
* @param {number} imageryLevel The imagery level
Expand All @@ -402,11 +446,25 @@ class ImageryCoverage {
nativeInputRectangle,
computeClippedImageryRectangle,
) {
const imageryProvider = imageryLayer.imageryProvider;
const imageryTilingScheme = imageryProvider.tilingScheme;
const numTilesX =
imageryTilingScheme.getNumberOfXTilesAtLevel(imageryLevel);

const imageryCoverages = [];

for (let i = imageryRange.minX; i <= imageryRange.maxX; i++) {
for (let rawX = imageryRange.minX; rawX <= imageryRange.maxX; rawX++) {
let x = rawX;
if (rawX >= numTilesX) {
x = rawX % numTilesX;
/// XXX_DRAPING Debug log
console.log(
`ImageryCoverage._computeImageryCoverages: Wrapping ${rawX} to ${x} for ${numTilesX} tiles on level ${imageryLevel}`,
);
}

const clippedImageryRectangleU = computeClippedImageryRectangle(
i,
x,
imageryRange.maxY,
imageryLevel,
);
Expand All @@ -415,10 +473,10 @@ class ImageryCoverage {
continue;
}

for (let j = imageryRange.minY; j <= imageryRange.maxY; j++) {
for (let y = imageryRange.minY; y <= imageryRange.maxY; y++) {
const clippedImageryRectangleV = computeClippedImageryRectangle(
i,
j,
x,
y,
imageryLevel,
);

Expand All @@ -433,16 +491,30 @@ class ImageryCoverage {
undefined,
);

// XXX_DRAPING Debug log...
Rectangle.debugPrintDirectly(
"ImageryCoverage._computeImageryCoverages: clippedImageryRectangleV",
clippedImageryRectangleV,
);
Rectangle.debugPrintDirectly(
"ImageryCoverage._computeImageryCoverages: nativeInputRectangle",
nativeInputRectangle,
);
console.log(
"ImageryCoverage._computeImageryCoverages: textureCoordinateRectangle",
textureCoordinateRectangle,
);

// Note: The getImageryFromCache function will create the whole "chain"
// of ancestor imageries, up to the root, and increases the reference
// counter for each of them, even though it is not called
// getImageryFromCacheAndCreateAllAncestorsAndAddReferences.
// There is currently no way to have a single imagery, because
// somewhere in TileImagery, the parent is assumed to be present.
const imagery = imageryLayer.getImageryFromCache(i, j, imageryLevel);
const imagery = imageryLayer.getImageryFromCache(x, y, imageryLevel);
const imageryCoverage = new ImageryCoverage(
i,
j,
x,
y,
imageryLevel,
textureCoordinateRectangle,
imagery,
Expand All @@ -454,6 +526,10 @@ class ImageryCoverage {
}

/**
* XXX_DRAPING This function does not work for rectangles that have
* been converted to the "native" representation, because
* Rectangle.computeWidth is broken for these rectangles
*
* Compute the coordinates of the first rectangle relative to the
* second rectangle.
*
Expand Down Expand Up @@ -483,6 +559,96 @@ class ImageryCoverage {
result.maxY = (rectangleA.north - rectangleB.south) * invY;
return result;
}

/**
* XXX_DRAPING This function should replace _localizeToCartesianRectangle,
* but operates on Rectangles that are proper Cartographic rectangles,
* which is often not the case for the imagery-related computations that
* have been extracted from _createTileImagerySkeletons
*
* Compute the coordinates of the first rectangle relative to the
* second rectangle.
*
* The result will describe the bounds of the first rectangle
* in coordinates that are relative to the (west, south) and
* (width, height) of the second rectangle, wrapping the
* longitude at the antimeridian. This is suitable for
* describing the texture coordinates of the first
* rectangle within the second one.
*
* The result will be stored in the given result parameter, or
* in a new rectangle if the result was undefined.
*
* @param {Rectangle} rectangleA The first rectangle
* @param {Rectangle} rectangleB The second rectangle
* @param {CartesianRectangle} [result] The result
* @returns {CartesianRectangle} The result
*/
static _localizeCartographicRectanglesToCartesianRectangle(
rectangleA,
rectangleB,
result,
) {
if (!defined(result)) {
result = new CartesianRectangle();
}
const invX = 1.0 / rectangleB.width;
const invY = 1.0 / rectangleB.height;

const wa = CesiumMath.zeroToTwoPi(rectangleA.west);
const ea = CesiumMath.zeroToTwoPi(rectangleA.east);
const wb = CesiumMath.zeroToTwoPi(rectangleB.west);

const rawMinX = ImageryCoverage.wrappedDifference(
wa,
wb,
CesiumMath.TWO_PI,
);
const rawMaxX = ImageryCoverage.wrappedDifference(
ea,
wb,
CesiumMath.TWO_PI,
);

result.minX = rawMinX * invX;
result.minY = (rectangleA.south - rectangleB.south) * invY;
result.maxX = rawMaxX * invX;
result.maxY = (rectangleA.north - rectangleB.south) * invY;
return result;
}

/**
* Computes the difference between the given values, wrapped to
* the given wrapping value.
*
* The values will be brought into the range [0, wrap]. The
* result will be the signed (!) difference between both values,
* considering the wrapping of the values.
*
* For example:
* <code>wrappedDifference(0.9, 0.7, 1.0) = 0.2</code>
* <code>wrappedDifference(0.7, 0.9, 1.0) = -0.2</code>
* <code>wrappedDifference(1.1, 0.9, 1.0) = 0.2</code>
* <code>wrappedDifference(0.9, 1.1, 1.0) = -0.2</code>
*
* @param {number} a The first value
* @param {number} b The second value
* @param {number} wrap The wrapping value
* @returns The wrapped difference
*/
static wrappedDifference(a, b, wrap) {
const wrappedA = (a %= wrap);
const wrappedB = (b %= wrap);
const diff = wrappedA - wrappedB;
const absDiff = Math.abs(diff);
if (absDiff < wrap - absDiff) {
return diff;
}
if (diff < 0) {
return diff + wrap;
}
return diff - wrap;
}
}

export default ImageryCoverage;
3 changes: 3 additions & 0 deletions packages/engine/Source/Scene/Model/ImageryPipelineStage.js
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,9 @@ class ImageryPipelineStage {
}

/**
* XXX_DRAPING: This should essentially do the same as _localizeCartographicRectanglesToCartesianRectangle,
* but whether or not this should (or has to) operate on the so-called "native" rectangles has to be checked.
*
* Compute the translation and scale that has to be applied to
* the texture coordinates for mapping the given imagery to
* the geometry.
Expand Down
Loading