bruce-cesium 7.1.4 → 7.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bruce-cesium.es5.js +330 -43
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +328 -41
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +1 -1
- package/dist/lib/rendering/displaced-surface-primitive.js +42 -31
- package/dist/lib/rendering/displaced-surface-primitive.js.map +1 -1
- package/dist/lib/rendering/entity-render-engine-polygon.js +20 -4
- package/dist/lib/rendering/entity-render-engine-polygon.js.map +1 -1
- package/dist/lib/rendering/texture-frame-series-animator.js +135 -1
- package/dist/lib/rendering/texture-frame-series-animator.js.map +1 -1
- package/dist/lib/utils/simplify-geometry.js +130 -4
- package/dist/lib/utils/simplify-geometry.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +1 -1
- package/dist/types/rendering/displaced-surface-primitive.d.ts +5 -0
- package/dist/types/rendering/texture-frame-series-animator.d.ts +28 -0
- package/dist/types/utils/simplify-geometry.d.ts +7 -0
- package/package.json +2 -2
package/dist/bruce-cesium.umd.js
CHANGED
|
@@ -3576,6 +3576,7 @@
|
|
|
3576
3576
|
if (!geometry || !isTurfAvailable() || !turf.simplify) {
|
|
3577
3577
|
return geometry;
|
|
3578
3578
|
}
|
|
3579
|
+
const original = geometry;
|
|
3579
3580
|
// Convert to geojson so that we can interact with turf.
|
|
3580
3581
|
const gFeature = BModels.Geometry.ToGeoJsonFeature({
|
|
3581
3582
|
geometry: geometry
|
|
@@ -3596,13 +3597,97 @@
|
|
|
3596
3597
|
});
|
|
3597
3598
|
}
|
|
3598
3599
|
// Converting back to Bruce geometry.
|
|
3599
|
-
|
|
3600
|
+
const simplified = (_a = BModels.Geometry.FromGeoJson({
|
|
3600
3601
|
geoJson: gFeature
|
|
3601
3602
|
})) === null || _a === void 0 ? void 0 : _a.geometry;
|
|
3602
|
-
|
|
3603
|
+
// A polygon we cannot vouch for is worse than an unsimplified one, so fall back instead of shipping it.
|
|
3604
|
+
if (!simplified || !isSafeToRender(simplified)) {
|
|
3605
|
+
return original;
|
|
3606
|
+
}
|
|
3607
|
+
return simplified;
|
|
3603
3608
|
}
|
|
3604
3609
|
SimplifyGeometry.Simplify = Simplify;
|
|
3610
|
+
/**
|
|
3611
|
+
* Whether a simplified geometry still describes something a renderer can triangulate.
|
|
3612
|
+
* @param geometry the simplification result.
|
|
3613
|
+
* @returns false when any polygon has a collapsed ring, a hole outside its outer ring, or a hole
|
|
3614
|
+
* wound the same way as its outer ring.
|
|
3615
|
+
*/
|
|
3616
|
+
function IsSafeToRender(geometry) {
|
|
3617
|
+
return isSafeToRender(geometry);
|
|
3618
|
+
}
|
|
3619
|
+
SimplifyGeometry.IsSafeToRender = IsSafeToRender;
|
|
3605
3620
|
})(SimplifyGeometry || (SimplifyGeometry = {}));
|
|
3621
|
+
// A ring needs three distinct corners plus the closing repeat before it encloses any area.
|
|
3622
|
+
const MIN_RING_POSITIONS = 4;
|
|
3623
|
+
function isSafeToRender(geometry) {
|
|
3624
|
+
let safe = true;
|
|
3625
|
+
const check = (g) => {
|
|
3626
|
+
if (!safe) {
|
|
3627
|
+
return;
|
|
3628
|
+
}
|
|
3629
|
+
if (g === null || g === void 0 ? void 0 : g.MultiGeometry) {
|
|
3630
|
+
g.MultiGeometry.forEach(check);
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
if (!(g === null || g === void 0 ? void 0 : g.Polygon) || g.Polygon.length === 0) {
|
|
3634
|
+
return;
|
|
3635
|
+
}
|
|
3636
|
+
let outer = null;
|
|
3637
|
+
let outerIsClockwise = false;
|
|
3638
|
+
for (let i = 0; i < g.Polygon.length; i++) {
|
|
3639
|
+
const points = BModels.Geometry.ParsePoints(g.Polygon[i].LinearRing);
|
|
3640
|
+
if (!points || points.length < MIN_RING_POSITIONS) {
|
|
3641
|
+
safe = false;
|
|
3642
|
+
return;
|
|
3643
|
+
}
|
|
3644
|
+
const clockwise = signedRingArea(points) < 0;
|
|
3645
|
+
if (g.Polygon[i].Facing !== "in") {
|
|
3646
|
+
outer = points;
|
|
3647
|
+
outerIsClockwise = clockwise;
|
|
3648
|
+
continue;
|
|
3649
|
+
}
|
|
3650
|
+
// A hole with no outer ring ahead of it cannot be subtracted from anything.
|
|
3651
|
+
if (!outer) {
|
|
3652
|
+
safe = false;
|
|
3653
|
+
return;
|
|
3654
|
+
}
|
|
3655
|
+
if (clockwise === outerIsClockwise) {
|
|
3656
|
+
safe = false;
|
|
3657
|
+
return;
|
|
3658
|
+
}
|
|
3659
|
+
for (let j = 0; j < points.length; j++) {
|
|
3660
|
+
if (!isPointInRing(points[j], outer)) {
|
|
3661
|
+
safe = false;
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3667
|
+
check(geometry);
|
|
3668
|
+
return safe;
|
|
3669
|
+
}
|
|
3670
|
+
function signedRingArea(points) {
|
|
3671
|
+
let sum = 0;
|
|
3672
|
+
for (let i = 0; i < points.length; i++) {
|
|
3673
|
+
const a = points[i];
|
|
3674
|
+
const b = points[(i + 1) % points.length];
|
|
3675
|
+
sum += a.longitude * b.latitude - b.longitude * a.latitude;
|
|
3676
|
+
}
|
|
3677
|
+
return sum / 2;
|
|
3678
|
+
}
|
|
3679
|
+
function isPointInRing(point, ring) {
|
|
3680
|
+
let inside = false;
|
|
3681
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
3682
|
+
const a = ring[i];
|
|
3683
|
+
const b = ring[j];
|
|
3684
|
+
if ((a.latitude > point.latitude) !== (b.latitude > point.latitude)
|
|
3685
|
+
&& point.longitude < (b.longitude - a.longitude) * (point.latitude - a.latitude) / (b.latitude - a.latitude) + a.longitude) {
|
|
3686
|
+
inside = !inside;
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
return inside;
|
|
3690
|
+
}
|
|
3606
3691
|
/**
|
|
3607
3692
|
* Runs through all found polygons and runs unkink through turf on them.
|
|
3608
3693
|
* @param feature
|
|
@@ -3644,9 +3729,26 @@
|
|
|
3644
3729
|
}
|
|
3645
3730
|
// Recreate the rings and reapply to the collection.
|
|
3646
3731
|
if (outer.length > 0) {
|
|
3647
|
-
|
|
3732
|
+
// Unkinking a self touching boundary splits it into pieces. Keeping whichever piece
|
|
3733
|
+
// happened to come back first can discard most of the shape, so keep the biggest one.
|
|
3734
|
+
let keep = outer[0];
|
|
3735
|
+
if (outer.length > 1) {
|
|
3736
|
+
let best = Math.abs(ringArea(keep));
|
|
3737
|
+
for (let i = 1; i < outer.length; i++) {
|
|
3738
|
+
const area = Math.abs(ringArea(outer[i]));
|
|
3739
|
+
if (area > best) {
|
|
3740
|
+
best = area;
|
|
3741
|
+
keep = outer[i];
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
const combinedCoords = [keep];
|
|
3648
3746
|
for (let i = 0; i < inner.length; i++) {
|
|
3649
|
-
|
|
3747
|
+
// A hole belonging to a piece that was just discarded is no longer inside anything,
|
|
3748
|
+
// and a hole crossing the boundary is what tears the fill.
|
|
3749
|
+
if (isRingInsideRing(inner[i], keep)) {
|
|
3750
|
+
combinedCoords.push(inner[i]);
|
|
3751
|
+
}
|
|
3650
3752
|
}
|
|
3651
3753
|
// Add the combined coordinates to the collection
|
|
3652
3754
|
const polygon = {
|
|
@@ -3794,6 +3896,30 @@
|
|
|
3794
3896
|
feature.geometry = collection;
|
|
3795
3897
|
}
|
|
3796
3898
|
}
|
|
3899
|
+
function ringArea(ring) {
|
|
3900
|
+
let sum = 0;
|
|
3901
|
+
for (let i = 0; i < ring.length; i++) {
|
|
3902
|
+
const a = ring[i];
|
|
3903
|
+
const b = ring[(i + 1) % ring.length];
|
|
3904
|
+
sum += a[0] * b[1] - b[0] * a[1];
|
|
3905
|
+
}
|
|
3906
|
+
return sum / 2;
|
|
3907
|
+
}
|
|
3908
|
+
function isRingInsideRing(inner, outer) {
|
|
3909
|
+
for (let i = 0; i < inner.length; i++) {
|
|
3910
|
+
let inside = false;
|
|
3911
|
+
for (let a = 0, b = outer.length - 1; a < outer.length; b = a++) {
|
|
3912
|
+
if ((outer[a][1] > inner[i][1]) !== (outer[b][1] > inner[i][1])
|
|
3913
|
+
&& inner[i][0] < (outer[b][0] - outer[a][0]) * (inner[i][1] - outer[a][1]) / (outer[b][1] - outer[a][1]) + outer[a][0]) {
|
|
3914
|
+
inside = !inside;
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
if (!inside) {
|
|
3918
|
+
return false;
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
return true;
|
|
3922
|
+
}
|
|
3797
3923
|
function dedupeCoordinates(inputCoords) {
|
|
3798
3924
|
return inputCoords.map(ring => {
|
|
3799
3925
|
if (ring.length < 2) {
|
|
@@ -39622,15 +39748,68 @@
|
|
|
39622
39748
|
return Boolean(data && data.generation && Array.isArray(data.generation.Frames) && data.generation.Frames.length > 0);
|
|
39623
39749
|
}
|
|
39624
39750
|
TextureFrameSeriesAnimator.IsFrameArchiveMetadata = IsFrameArchiveMetadata;
|
|
39751
|
+
// How far covered values bleed outward into uncovered texels before the GPU samples them.
|
|
39752
|
+
const VALUE_DILATE_PASSES = 2;
|
|
39753
|
+
/*
|
|
39754
|
+
* Bleeds covered values outward into uncovered texels, leaving alpha untouched.
|
|
39755
|
+
*
|
|
39756
|
+
* The GPU samples the value texture with linear filtering, so an uncovered texel's RGB still gets
|
|
39757
|
+
* averaged into the vertices next to it even though its alpha is zero. Land sits at one end of the
|
|
39758
|
+
* ramp, so without this the coastline grows a row of spikes exactly where the data stops. Masking the
|
|
39759
|
+
* baseline creates more of these edges, which is what makes this necessary rather than cosmetic.
|
|
39760
|
+
*/
|
|
39761
|
+
function dilateValues(value, width, height, passes) {
|
|
39762
|
+
const texels = width * height;
|
|
39763
|
+
const filled = new Uint8Array(texels);
|
|
39764
|
+
for (let p = 0; p < texels; p++) {
|
|
39765
|
+
filled[p] = value[p * 4 + 3] !== 0 ? 1 : 0;
|
|
39766
|
+
}
|
|
39767
|
+
for (let pass = 0; pass < passes; pass++) {
|
|
39768
|
+
const source = value.slice();
|
|
39769
|
+
const wasFilled = filled.slice();
|
|
39770
|
+
for (let y = 0; y < height; y++) {
|
|
39771
|
+
for (let x = 0; x < width; x++) {
|
|
39772
|
+
const p = y * width + x;
|
|
39773
|
+
if (wasFilled[p]) {
|
|
39774
|
+
continue;
|
|
39775
|
+
}
|
|
39776
|
+
let sum = 0;
|
|
39777
|
+
let hits = 0;
|
|
39778
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
39779
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
39780
|
+
const nx = x + dx;
|
|
39781
|
+
const ny = y + dy;
|
|
39782
|
+
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !wasFilled[ny * width + nx]) {
|
|
39783
|
+
continue;
|
|
39784
|
+
}
|
|
39785
|
+
sum += source[(ny * width + nx) * 4];
|
|
39786
|
+
hits++;
|
|
39787
|
+
}
|
|
39788
|
+
}
|
|
39789
|
+
if (hits > 0) {
|
|
39790
|
+
const v = Math.round(sum / hits);
|
|
39791
|
+
value[p * 4] = v;
|
|
39792
|
+
value[p * 4 + 1] = v;
|
|
39793
|
+
value[p * 4 + 2] = v;
|
|
39794
|
+
filled[p] = 1;
|
|
39795
|
+
}
|
|
39796
|
+
}
|
|
39797
|
+
}
|
|
39798
|
+
}
|
|
39799
|
+
}
|
|
39625
39800
|
const DEFAULT_CROSSFADE_MS = 900;
|
|
39626
39801
|
const DEFAULT_LOW_COLOR = { red: 255, green: 255, blue: 255, alpha: 0.12 };
|
|
39627
39802
|
const DEFAULT_HIGH_COLOR = { red: 21, green: 96, blue: 196, alpha: 0.92 };
|
|
39628
39803
|
class Animator {
|
|
39629
39804
|
constructor(options) {
|
|
39630
|
-
var _a;
|
|
39805
|
+
var _a, _b;
|
|
39631
39806
|
this.removeOnTick = null;
|
|
39632
39807
|
this.removeCrossfadeTick = null;
|
|
39633
39808
|
this.disposed = false;
|
|
39809
|
+
// One alpha byte per texel, 1 meaning the texel is baseline. Fetched once, the mesh does not move.
|
|
39810
|
+
this.baselineFlags = null;
|
|
39811
|
+
this.baselineDims = null;
|
|
39812
|
+
this.baselineLoad = null;
|
|
39634
39813
|
this.currentFrameIndex = -1;
|
|
39635
39814
|
// Single-in-flight fetch queue: only one Range GET outstanding at a time, and the NEXT one
|
|
39636
39815
|
// is chosen right before sending, this is what makes reprioritizing mid-scrub take effect
|
|
@@ -39670,6 +39849,8 @@
|
|
|
39670
39849
|
this.frameDims = new Array(this.frames.length).fill(null);
|
|
39671
39850
|
this.valueCache = new Array(this.frames.length).fill(null);
|
|
39672
39851
|
this.produceValueCanvas = Boolean(options.produceValueCanvas);
|
|
39852
|
+
this.baselineMaskEntry = options.baselineMask || null;
|
|
39853
|
+
this.maskBaseline = (_b = options.maskBaseline) !== null && _b !== void 0 ? _b : Boolean(options.baselineMask);
|
|
39673
39854
|
this.valueCanvas = this.produceValueCanvas ? document.createElement("canvas") : null;
|
|
39674
39855
|
this.driveMaterial = options.driveMaterial !== false;
|
|
39675
39856
|
this.originalMaterial = options.entity.polygon.material;
|
|
@@ -39839,6 +40020,12 @@
|
|
|
39839
40020
|
if (this.disposed) {
|
|
39840
40021
|
return;
|
|
39841
40022
|
}
|
|
40023
|
+
// Awaited before decoding rather than applied later, so no frame is ever cached unmasked and
|
|
40024
|
+
// then corrected once the mask lands.
|
|
40025
|
+
await this.ensureBaselineMask();
|
|
40026
|
+
if (this.disposed) {
|
|
40027
|
+
return;
|
|
40028
|
+
}
|
|
39842
40029
|
const decoded = await this.decodeAndTint(buffer);
|
|
39843
40030
|
if (this.disposed) {
|
|
39844
40031
|
return;
|
|
@@ -39873,8 +40060,81 @@
|
|
|
39873
40060
|
// recovered from the tinted pixels afterwards.
|
|
39874
40061
|
const valuePixels = this.produceValueCanvas ? new Uint8ClampedArray(imageData.data) : undefined;
|
|
39875
40062
|
ApplyGrayscaleColorMask(imageData, this.lowColor, this.highColor);
|
|
40063
|
+
this.applyBaselineMask(imageData.data, valuePixels, canvas.width, canvas.height);
|
|
40064
|
+
if (valuePixels) {
|
|
40065
|
+
dilateValues(valuePixels, canvas.width, canvas.height, VALUE_DILATE_PASSES);
|
|
40066
|
+
}
|
|
39876
40067
|
return { pixels: imageData.data, width: canvas.width, height: canvas.height, valuePixels };
|
|
39877
40068
|
}
|
|
40069
|
+
/**
|
|
40070
|
+
* Fetches and decodes the archive's static baseline mask, at most once.
|
|
40071
|
+
*/
|
|
40072
|
+
ensureBaselineMask() {
|
|
40073
|
+
if (!this.maskBaseline || !this.baselineMaskEntry) {
|
|
40074
|
+
return Promise.resolve();
|
|
40075
|
+
}
|
|
40076
|
+
if (this.baselineLoad) {
|
|
40077
|
+
return this.baselineLoad;
|
|
40078
|
+
}
|
|
40079
|
+
const entry = this.baselineMaskEntry;
|
|
40080
|
+
const start = entry.ByteOffset;
|
|
40081
|
+
const end = entry.ByteOffset + entry.ByteLength - 1;
|
|
40082
|
+
this.baselineLoad = fetch(this.archiveUrl, { headers: { Range: `bytes=${start}-${end}` } })
|
|
40083
|
+
.then((response) => response.arrayBuffer())
|
|
40084
|
+
.then(async (buffer) => {
|
|
40085
|
+
if (this.disposed) {
|
|
40086
|
+
return;
|
|
40087
|
+
}
|
|
40088
|
+
const blob = new Blob([buffer], { type: "image/png" });
|
|
40089
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
40090
|
+
let image;
|
|
40091
|
+
try {
|
|
40092
|
+
image = await loadImage(objectUrl);
|
|
40093
|
+
}
|
|
40094
|
+
finally {
|
|
40095
|
+
URL.revokeObjectURL(objectUrl);
|
|
40096
|
+
}
|
|
40097
|
+
const canvas = document.createElement("canvas");
|
|
40098
|
+
canvas.width = image.width;
|
|
40099
|
+
canvas.height = image.height;
|
|
40100
|
+
const ctx = canvas.getContext("2d");
|
|
40101
|
+
ctx.drawImage(image, 0, 0);
|
|
40102
|
+
const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
|
|
40103
|
+
const flags = new Uint8Array(canvas.width * canvas.height);
|
|
40104
|
+
for (let p = 0; p < flags.length; p++) {
|
|
40105
|
+
flags[p] = pixels[p * 4 + 3] >= 128 ? 1 : 0;
|
|
40106
|
+
}
|
|
40107
|
+
this.baselineFlags = flags;
|
|
40108
|
+
this.baselineDims = { width: canvas.width, height: canvas.height };
|
|
40109
|
+
})
|
|
40110
|
+
.catch((e) => {
|
|
40111
|
+
// A missing or unreadable mask must not stop the animation; it just means nothing is hidden.
|
|
40112
|
+
console.warn("TextureFrameSeriesAnimator: could not load the baseline mask, drawing it instead.", e);
|
|
40113
|
+
});
|
|
40114
|
+
return this.baselineLoad;
|
|
40115
|
+
}
|
|
40116
|
+
/**
|
|
40117
|
+
* Clears alpha on baseline texels, which is what makes hiding the dry/land sentinel a render choice.
|
|
40118
|
+
*/
|
|
40119
|
+
applyBaselineMask(pixels, valuePixels, width, height) {
|
|
40120
|
+
const flags = this.baselineFlags;
|
|
40121
|
+
if (!flags || !this.baselineDims) {
|
|
40122
|
+
return;
|
|
40123
|
+
}
|
|
40124
|
+
// A mask from a different raster size cannot be indexed against this frame, and guessing a
|
|
40125
|
+
// resampling would silently hide the wrong ground.
|
|
40126
|
+
if (this.baselineDims.width !== width || this.baselineDims.height !== height) {
|
|
40127
|
+
return;
|
|
40128
|
+
}
|
|
40129
|
+
for (let p = 0; p < flags.length; p++) {
|
|
40130
|
+
if (flags[p]) {
|
|
40131
|
+
pixels[p * 4 + 3] = 0;
|
|
40132
|
+
if (valuePixels) {
|
|
40133
|
+
valuePixels[p * 4 + 3] = 0;
|
|
40134
|
+
}
|
|
40135
|
+
}
|
|
40136
|
+
}
|
|
40137
|
+
}
|
|
39878
40138
|
beginCrossfadeTo(idx) {
|
|
39879
40139
|
const to = this.frameCache[idx];
|
|
39880
40140
|
this.fadeFromValuePixels = this.displayedValuePixels === null && this.valueCache[idx]
|
|
@@ -40116,11 +40376,13 @@ void main() {
|
|
|
40116
40376
|
return _glsl300Support;
|
|
40117
40377
|
}
|
|
40118
40378
|
/*
|
|
40119
|
-
* Places grid node (fx, fy) on the ellipsoid,
|
|
40120
|
-
*
|
|
40121
|
-
* linear in a projected CRS, so stepping in degrees instead would skew the surface against the flat-textured polygon it replaces.
|
|
40379
|
+
* Places grid node (fx, fy) on the ellipsoid, stepping the way the raster was sampled.
|
|
40380
|
+
* A raster sampled at lon/lat has to be walked in degrees; one sampled linearly across a projected CRS has to be walked in tangent-plane metres.
|
|
40122
40381
|
*/
|
|
40123
|
-
function makePlacer(extent) {
|
|
40382
|
+
function makePlacer(extent, pixelPlacement) {
|
|
40383
|
+
if (pixelPlacement === "lonlat") {
|
|
40384
|
+
return (fx, fy) => Cesium.Cartesian3.fromDegrees(Cesium.Math.lerp(extent.West, extent.East, fx), Cesium.Math.lerp(extent.North, extent.South, fy), 0);
|
|
40385
|
+
}
|
|
40124
40386
|
const ellipsoid = Cesium.Ellipsoid.WGS84;
|
|
40125
40387
|
const centre = Cesium.Cartesian3.fromDegrees((extent.West + extent.East) / 2, (extent.South + extent.North) / 2, 0);
|
|
40126
40388
|
const frame = Cesium.Transforms.eastNorthUpToFixedFrame(centre);
|
|
@@ -40134,21 +40396,21 @@ void main() {
|
|
|
40134
40396
|
};
|
|
40135
40397
|
}
|
|
40136
40398
|
/*
|
|
40137
|
-
* Builds one tile's grid, plus a duplicated perimeter ring the shader pushes downwards to
|
|
40138
|
-
* the crack where a neighbouring tile at a different density interpolates the shared edge.
|
|
40399
|
+
* Builds one tile's grid, optionally plus a duplicated perimeter ring the shader pushes downwards to
|
|
40400
|
+
* cover the crack where a neighbouring tile at a different density interpolates the shared edge.
|
|
40139
40401
|
*/
|
|
40140
|
-
function buildGeometry(extent, resolution, patch) {
|
|
40402
|
+
function buildGeometry(extent, resolution, patch, pixelPlacement, withSkirt = false) {
|
|
40141
40403
|
const cols = resolution;
|
|
40142
40404
|
const rows = resolution;
|
|
40143
40405
|
const gridCount = (cols + 1) * (rows + 1);
|
|
40144
|
-
const perimeterCount = 2 * (cols + 1) + 2 * (rows + 1) - 4;
|
|
40406
|
+
const perimeterCount = withSkirt ? 2 * (cols + 1) + 2 * (rows + 1) - 4 : 0;
|
|
40145
40407
|
const count = gridCount + perimeterCount;
|
|
40146
40408
|
const positions = new Float64Array(count * 3);
|
|
40147
40409
|
const normals = new Float32Array(count * 3);
|
|
40148
40410
|
const sts = new Float32Array(count * 2);
|
|
40149
40411
|
const skirts = new Float32Array(count);
|
|
40150
40412
|
const ellipsoid = Cesium.Ellipsoid.WGS84;
|
|
40151
|
-
const place = makePlacer(extent);
|
|
40413
|
+
const place = makePlacer(extent, pixelPlacement);
|
|
40152
40414
|
let v = 0;
|
|
40153
40415
|
for (let row = 0; row <= rows; row++) {
|
|
40154
40416
|
// Texture row 0 is the north edge, so t runs opposite to northing.
|
|
@@ -40199,26 +40461,28 @@ void main() {
|
|
|
40199
40461
|
loop.push(row * (cols + 1));
|
|
40200
40462
|
}
|
|
40201
40463
|
const skirtStart = gridCount;
|
|
40202
|
-
|
|
40203
|
-
|
|
40204
|
-
|
|
40205
|
-
|
|
40206
|
-
|
|
40207
|
-
|
|
40208
|
-
|
|
40209
|
-
|
|
40210
|
-
|
|
40211
|
-
|
|
40212
|
-
|
|
40213
|
-
|
|
40214
|
-
|
|
40215
|
-
|
|
40216
|
-
|
|
40217
|
-
|
|
40218
|
-
|
|
40219
|
-
|
|
40220
|
-
|
|
40221
|
-
|
|
40464
|
+
if (withSkirt) {
|
|
40465
|
+
loop.forEach((gridIndex, k) => {
|
|
40466
|
+
const target = skirtStart + k;
|
|
40467
|
+
positions[target * 3] = positions[gridIndex * 3];
|
|
40468
|
+
positions[target * 3 + 1] = positions[gridIndex * 3 + 1];
|
|
40469
|
+
positions[target * 3 + 2] = positions[gridIndex * 3 + 2];
|
|
40470
|
+
normals[target * 3] = normals[gridIndex * 3];
|
|
40471
|
+
normals[target * 3 + 1] = normals[gridIndex * 3 + 1];
|
|
40472
|
+
normals[target * 3 + 2] = normals[gridIndex * 3 + 2];
|
|
40473
|
+
sts[target * 2] = sts[gridIndex * 2];
|
|
40474
|
+
sts[target * 2 + 1] = sts[gridIndex * 2 + 1];
|
|
40475
|
+
skirts[target] = 1;
|
|
40476
|
+
});
|
|
40477
|
+
for (let k = 0; k < loop.length; k++) {
|
|
40478
|
+
const nextK = (k + 1) % loop.length;
|
|
40479
|
+
indices[i++] = loop[k];
|
|
40480
|
+
indices[i++] = skirtStart + k;
|
|
40481
|
+
indices[i++] = loop[nextK];
|
|
40482
|
+
indices[i++] = loop[nextK];
|
|
40483
|
+
indices[i++] = skirtStart + k;
|
|
40484
|
+
indices[i++] = skirtStart + nextK;
|
|
40485
|
+
}
|
|
40222
40486
|
}
|
|
40223
40487
|
const geometry = new Cesium.Geometry({
|
|
40224
40488
|
attributes: {
|
|
@@ -40276,6 +40540,8 @@ void main() {
|
|
|
40276
40540
|
this.valueMax = (_b = options.valueMax) !== null && _b !== void 0 ? _b : 1;
|
|
40277
40541
|
this.exaggeration = (_c = options.exaggeration) !== null && _c !== void 0 ? _c : autoExaggeration(this.extent, this.valueMin, this.valueMax);
|
|
40278
40542
|
this.baseHeight = (_d = options.baseHeight) !== null && _d !== void 0 ? _d : 0;
|
|
40543
|
+
this.pixelPlacement = options.pixelPlacement;
|
|
40544
|
+
this.tileSkirts = Boolean(options.tileSkirts);
|
|
40279
40545
|
this.lowColor = toCesiumColor(options.lowColor || DEFAULT_LOW_COLOR);
|
|
40280
40546
|
this.highColor = toCesiumColor(options.highColor || DEFAULT_HIGH_COLOR);
|
|
40281
40547
|
this.tiles = buildTiles(hasCoverage);
|
|
@@ -40283,6 +40549,9 @@ void main() {
|
|
|
40283
40549
|
GetFollowsGround() {
|
|
40284
40550
|
return this.followGround;
|
|
40285
40551
|
}
|
|
40552
|
+
GetPixelPlacement() {
|
|
40553
|
+
return this.pixelPlacement;
|
|
40554
|
+
}
|
|
40286
40555
|
GetBaseHeight() {
|
|
40287
40556
|
return this.baseHeight;
|
|
40288
40557
|
}
|
|
@@ -40497,7 +40766,7 @@ void main() {
|
|
|
40497
40766
|
if (cached) {
|
|
40498
40767
|
return cached;
|
|
40499
40768
|
}
|
|
40500
|
-
const geometry = buildGeometry(this.extent, tier, tile.patch);
|
|
40769
|
+
const geometry = buildGeometry(this.extent, tier, tile.patch, this.pixelPlacement, this.tileSkirts);
|
|
40501
40770
|
const attributeLocations = Cesium.GeometryPipeline.createAttributeLocations(geometry);
|
|
40502
40771
|
const mesh = {
|
|
40503
40772
|
attributeLocations,
|
|
@@ -40590,7 +40859,9 @@ void main() {
|
|
|
40590
40859
|
u_valueRange: () => self.valueMax - self.valueMin,
|
|
40591
40860
|
u_exaggeration: () => self.exaggeration,
|
|
40592
40861
|
u_baseHeight: () => self.baseHeight,
|
|
40593
|
-
u_skirtDepth: () =>
|
|
40862
|
+
u_skirtDepth: () => self.tileSkirts
|
|
40863
|
+
? Math.max(2, Math.abs(self.valueMax - self.valueMin) * self.exaggeration * SKIRT_FRACTION)
|
|
40864
|
+
: 0,
|
|
40594
40865
|
u_coverageCutoff: () => COVERAGE_CUTOFF,
|
|
40595
40866
|
u_lowColor: () => self.lowColor,
|
|
40596
40867
|
u_highColor: () => self.highColor,
|
|
@@ -40722,6 +40993,8 @@ void main() {
|
|
|
40722
40993
|
}
|
|
40723
40994
|
}
|
|
40724
40995
|
const drawsDisplacedSurface = Boolean(extrusionArchive);
|
|
40996
|
+
// The displaced surface IS the entity's fill, drawn as its own primitive at the value's height.
|
|
40997
|
+
// Leaving the flat polygon filled as well shows a second copy of the parcel on the ground under it.
|
|
40725
40998
|
const hasFill = (drawsDisplacedSurface ? true
|
|
40726
40999
|
: fillType === BModels.Style.EPolygonFillType.Texture
|
|
40727
41000
|
? Boolean(textureDataUri || frameArchive || cFillColor.alpha > 0)
|
|
@@ -40810,6 +41083,7 @@ void main() {
|
|
|
40810
41083
|
polygon: {
|
|
40811
41084
|
hierarchy: new Cesium.PolygonHierarchy(posses, holePosses.map(x => new Cesium.PolygonHierarchy(x))),
|
|
40812
41085
|
material: fillMaterial,
|
|
41086
|
+
fill: !drawsDisplacedSurface,
|
|
40813
41087
|
extrudedHeight: extrusion.value,
|
|
40814
41088
|
extrudedHeightReference: extrusion.exHeightRef,
|
|
40815
41089
|
shadows: Cesium.ShadowMode.ENABLED,
|
|
@@ -40840,6 +41114,12 @@ void main() {
|
|
|
40840
41114
|
if (!CompareCPolygonHierarchies(oldHierarchy, newHierarchy)) {
|
|
40841
41115
|
cEntity.polygon.hierarchy = new Cesium.ConstantProperty(newHierarchy);
|
|
40842
41116
|
}
|
|
41117
|
+
// Toggling extrusion styling on an already-rendered entity flips this,
|
|
41118
|
+
// so it has to be reconciled rather than set only at creation.
|
|
41119
|
+
const oldFill = GetCValue(params.viewer, cEntity.polygon.fill);
|
|
41120
|
+
if (oldFill !== !drawsDisplacedSurface) {
|
|
41121
|
+
cEntity.polygon.fill = new Cesium.ConstantProperty(!drawsDisplacedSurface);
|
|
41122
|
+
}
|
|
40843
41123
|
const oldExtrusion = GetCValue(params.viewer, cEntity.polygon.extrudedHeight);
|
|
40844
41124
|
if (oldExtrusion != extrusion.value) {
|
|
40845
41125
|
cEntity.polygon.extrudedHeight = new Cesium.ConstantProperty(extrusion.value);
|
|
@@ -40893,7 +41173,7 @@ void main() {
|
|
|
40893
41173
|
}
|
|
40894
41174
|
// Must run before any material is baked below: the Animator takes over polygon.material, so a bake
|
|
40895
41175
|
// has to know whether it is baking over a live animation or over a disposed one's restored material.
|
|
40896
|
-
const animator = syncTextureFrameArchive(cEntity, drawsDisplacedSurface ? null : frameArchive, params.viewer, style.textureColorMask);
|
|
41176
|
+
const animator = syncTextureFrameArchive(cEntity, drawsDisplacedSurface ? null : frameArchive, params.viewer, style.textureColorMask, style.maskTextureBaseline);
|
|
40897
41177
|
syncDisplacedSurface({
|
|
40898
41178
|
cEntity,
|
|
40899
41179
|
extrusionArchive,
|
|
@@ -41378,12 +41658,17 @@ void main() {
|
|
|
41378
41658
|
frames: extrusionArchive.metadata.Frames,
|
|
41379
41659
|
textureColorMask: style.textureColorMask,
|
|
41380
41660
|
driveMaterial: false,
|
|
41381
|
-
produceValueCanvas: true
|
|
41661
|
+
produceValueCanvas: true,
|
|
41662
|
+
baselineMask: extrusionArchive.metadata.BaselineMask,
|
|
41663
|
+
// Off-type for the same reason as the fill path: the pinned bruce-models predates the field.
|
|
41664
|
+
maskBaseline: style.maskTextureBaseline
|
|
41382
41665
|
});
|
|
41383
41666
|
cEntity[EXTRUSION_ANIMATOR_KEY] = animator;
|
|
41384
41667
|
const mask = style.textureColorMask;
|
|
41385
41668
|
const surface = new exports.DisplacedSurfacePrimitive.Surface({
|
|
41386
41669
|
extent,
|
|
41670
|
+
// The grid has to be walked the way the raster was sampled, or the surface skews across the extent.
|
|
41671
|
+
pixelPlacement: extrusionArchive.metadata.PixelPlacement,
|
|
41387
41672
|
source: null,
|
|
41388
41673
|
baseHeight: placement.baseHeight,
|
|
41389
41674
|
followGround: placement.followGround,
|
|
@@ -41475,7 +41760,7 @@ void main() {
|
|
|
41475
41760
|
* @param viewer
|
|
41476
41761
|
* @param textureColorMask
|
|
41477
41762
|
*/
|
|
41478
|
-
function syncTextureFrameArchive(cEntity, frameArchive, viewer, textureColorMask) {
|
|
41763
|
+
function syncTextureFrameArchive(cEntity, frameArchive, viewer, textureColorMask, maskTextureBaseline) {
|
|
41479
41764
|
const existing = cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY];
|
|
41480
41765
|
if (frameArchive && existing && !existing.IsDisposed() && existing.GetArchiveUrl() === frameArchive.url) {
|
|
41481
41766
|
return existing;
|
|
@@ -41492,7 +41777,9 @@ void main() {
|
|
|
41492
41777
|
entity: cEntity,
|
|
41493
41778
|
archiveUrl: frameArchive.url,
|
|
41494
41779
|
frames: frameArchive.metadata.Frames,
|
|
41495
|
-
textureColorMask
|
|
41780
|
+
textureColorMask,
|
|
41781
|
+
baselineMask: frameArchive.metadata.BaselineMask,
|
|
41782
|
+
maskBaseline: maskTextureBaseline
|
|
41496
41783
|
});
|
|
41497
41784
|
cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY] = animator;
|
|
41498
41785
|
return animator;
|
|
@@ -43089,7 +43376,7 @@ void main() {
|
|
|
43089
43376
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
43090
43377
|
})(exports.StyleUtils || (exports.StyleUtils = {}));
|
|
43091
43378
|
|
|
43092
|
-
const VERSION = "7.1.
|
|
43379
|
+
const VERSION = "7.1.6";
|
|
43093
43380
|
/**
|
|
43094
43381
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
43095
43382
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|