bruce-cesium 7.1.4 → 7.1.5

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.
@@ -1,6 +1,6 @@
1
1
  import * as Cesium from 'cesium';
2
- import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, ShaderSource, Ellipsoid, Cartesian3, Transforms, Matrix4, Math as Math$1, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, Cartesian2, DistanceDisplayCondition, HeightReference, ColorBlendMode, HeadingPitchRoll, ShadowMode, ClassificationType, Model, ArcType, CornerType, ConstantPositionProperty, JulianDate, Quaternion, HorizontalOrigin, VerticalOrigin, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, SceneTransforms, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileset, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, Cesium3DTileColorBlendMode, Intersect, OrthographicFrustum, EasingFunction, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, CesiumInspector, ClockRange, CustomDataSource, ScreenSpaceEventHandler, ScreenSpaceEventType, GeometryInstance, Fullscreen, CzmlDataSource } from 'cesium';
3
- import { Cartes, Entity as Entity$1, ENVIRONMENT, Calculator, ClientFile, EntityLod, EntityTag, EntityType, LRUCache, ObjectUtils, Style, Carto, Geometry as Geometry$1, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, Api, Bounds, Color as Color$1, BruceEvent, EntityCoords, DataLab, DelayQueue, BruceApi, EntityHistoricData, EntityRelation, AccountConcept, RecordChangeFeed, ProgramKey, EntitySource, ProjectViewLegacyTile, ProjectViewTile, Camera, Account, AccountLimits, Session, UrlUtils, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, ProjectViewBookmarkGroup, EntityRelationType } from 'bruce-models';
2
+ import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, DistanceDisplayCondition, ShaderSource, Cartesian3, Math as Math$1, Ellipsoid, Transforms, Matrix4, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, Cartesian2, HeightReference, HorizontalOrigin, VerticalOrigin, ClassificationType, ConstantPositionProperty, ArcType, CornerType, ShadowMode, ColorBlendMode, HeadingPitchRoll, Model, JulianDate, Quaternion, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, SceneTransforms, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Ion, IonResource, Cesium3DTileStyle, HeadingPitchRange, Cesium3DTileColorBlendMode, OrthographicFrustum, EasingFunction, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, Cesium3DTileset, Intersect, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, CustomDataSource, PolygonPipeline, CesiumInspector, ClockRange, GeometryInstance, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
3
+ import { Cartes, Entity as Entity$1, ENVIRONMENT, Api, Calculator, ClientFile, EntityTag, EntityType, LRUCache, ObjectUtils, Style, Carto, Geometry as Geometry$1, EntityLod, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, Bounds, Color as Color$1, BruceEvent, EntityCoords, DataLab, DelayQueue, EntityHistoricData, BruceApi, AccountConcept, RecordChangeFeed, EntityRelation, ProgramKey, EntitySource, Camera, ProjectViewTile, ProjectViewLegacyTile, Account, AccountLimits, Session, UrlUtils, ProjectViewBookmarkGroup, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, EntityRelationType } from 'bruce-models';
4
4
 
5
5
  /**
6
6
  * Ensures a number is returned from a given value.
@@ -39734,15 +39734,68 @@ var TextureFrameSeriesAnimator;
39734
39734
  return Boolean(data && data.generation && Array.isArray(data.generation.Frames) && data.generation.Frames.length > 0);
39735
39735
  }
39736
39736
  TextureFrameSeriesAnimator.IsFrameArchiveMetadata = IsFrameArchiveMetadata;
39737
+ // How far covered values bleed outward into uncovered texels before the GPU samples them.
39738
+ const VALUE_DILATE_PASSES = 2;
39739
+ /*
39740
+ * Bleeds covered values outward into uncovered texels, leaving alpha untouched.
39741
+ *
39742
+ * The GPU samples the value texture with linear filtering, so an uncovered texel's RGB still gets
39743
+ * averaged into the vertices next to it even though its alpha is zero. Land sits at one end of the
39744
+ * ramp, so without this the coastline grows a row of spikes exactly where the data stops. Masking the
39745
+ * baseline creates more of these edges, which is what makes this necessary rather than cosmetic.
39746
+ */
39747
+ function dilateValues(value, width, height, passes) {
39748
+ const texels = width * height;
39749
+ const filled = new Uint8Array(texels);
39750
+ for (let p = 0; p < texels; p++) {
39751
+ filled[p] = value[p * 4 + 3] !== 0 ? 1 : 0;
39752
+ }
39753
+ for (let pass = 0; pass < passes; pass++) {
39754
+ const source = value.slice();
39755
+ const wasFilled = filled.slice();
39756
+ for (let y = 0; y < height; y++) {
39757
+ for (let x = 0; x < width; x++) {
39758
+ const p = y * width + x;
39759
+ if (wasFilled[p]) {
39760
+ continue;
39761
+ }
39762
+ let sum = 0;
39763
+ let hits = 0;
39764
+ for (let dy = -1; dy <= 1; dy++) {
39765
+ for (let dx = -1; dx <= 1; dx++) {
39766
+ const nx = x + dx;
39767
+ const ny = y + dy;
39768
+ if (nx < 0 || ny < 0 || nx >= width || ny >= height || !wasFilled[ny * width + nx]) {
39769
+ continue;
39770
+ }
39771
+ sum += source[(ny * width + nx) * 4];
39772
+ hits++;
39773
+ }
39774
+ }
39775
+ if (hits > 0) {
39776
+ const v = Math.round(sum / hits);
39777
+ value[p * 4] = v;
39778
+ value[p * 4 + 1] = v;
39779
+ value[p * 4 + 2] = v;
39780
+ filled[p] = 1;
39781
+ }
39782
+ }
39783
+ }
39784
+ }
39785
+ }
39737
39786
  const DEFAULT_CROSSFADE_MS = 900;
39738
39787
  const DEFAULT_LOW_COLOR = { red: 255, green: 255, blue: 255, alpha: 0.12 };
39739
39788
  const DEFAULT_HIGH_COLOR = { red: 21, green: 96, blue: 196, alpha: 0.92 };
39740
39789
  class Animator {
39741
39790
  constructor(options) {
39742
- var _a;
39791
+ var _a, _b;
39743
39792
  this.removeOnTick = null;
39744
39793
  this.removeCrossfadeTick = null;
39745
39794
  this.disposed = false;
39795
+ // One alpha byte per texel, 1 meaning the texel is baseline. Fetched once, the mesh does not move.
39796
+ this.baselineFlags = null;
39797
+ this.baselineDims = null;
39798
+ this.baselineLoad = null;
39746
39799
  this.currentFrameIndex = -1;
39747
39800
  // Single-in-flight fetch queue: only one Range GET outstanding at a time, and the NEXT one
39748
39801
  // is chosen right before sending, this is what makes reprioritizing mid-scrub take effect
@@ -39782,6 +39835,8 @@ var TextureFrameSeriesAnimator;
39782
39835
  this.frameDims = new Array(this.frames.length).fill(null);
39783
39836
  this.valueCache = new Array(this.frames.length).fill(null);
39784
39837
  this.produceValueCanvas = Boolean(options.produceValueCanvas);
39838
+ this.baselineMaskEntry = options.baselineMask || null;
39839
+ this.maskBaseline = (_b = options.maskBaseline) !== null && _b !== void 0 ? _b : Boolean(options.baselineMask);
39785
39840
  this.valueCanvas = this.produceValueCanvas ? document.createElement("canvas") : null;
39786
39841
  this.driveMaterial = options.driveMaterial !== false;
39787
39842
  this.originalMaterial = options.entity.polygon.material;
@@ -39951,6 +40006,12 @@ var TextureFrameSeriesAnimator;
39951
40006
  if (this.disposed) {
39952
40007
  return;
39953
40008
  }
40009
+ // Awaited before decoding rather than applied later, so no frame is ever cached unmasked and
40010
+ // then corrected once the mask lands.
40011
+ await this.ensureBaselineMask();
40012
+ if (this.disposed) {
40013
+ return;
40014
+ }
39954
40015
  const decoded = await this.decodeAndTint(buffer);
39955
40016
  if (this.disposed) {
39956
40017
  return;
@@ -39985,8 +40046,81 @@ var TextureFrameSeriesAnimator;
39985
40046
  // recovered from the tinted pixels afterwards.
39986
40047
  const valuePixels = this.produceValueCanvas ? new Uint8ClampedArray(imageData.data) : undefined;
39987
40048
  ApplyGrayscaleColorMask(imageData, this.lowColor, this.highColor);
40049
+ this.applyBaselineMask(imageData.data, valuePixels, canvas.width, canvas.height);
40050
+ if (valuePixels) {
40051
+ dilateValues(valuePixels, canvas.width, canvas.height, VALUE_DILATE_PASSES);
40052
+ }
39988
40053
  return { pixels: imageData.data, width: canvas.width, height: canvas.height, valuePixels };
39989
40054
  }
40055
+ /**
40056
+ * Fetches and decodes the archive's static baseline mask, at most once.
40057
+ */
40058
+ ensureBaselineMask() {
40059
+ if (!this.maskBaseline || !this.baselineMaskEntry) {
40060
+ return Promise.resolve();
40061
+ }
40062
+ if (this.baselineLoad) {
40063
+ return this.baselineLoad;
40064
+ }
40065
+ const entry = this.baselineMaskEntry;
40066
+ const start = entry.ByteOffset;
40067
+ const end = entry.ByteOffset + entry.ByteLength - 1;
40068
+ this.baselineLoad = fetch(this.archiveUrl, { headers: { Range: `bytes=${start}-${end}` } })
40069
+ .then((response) => response.arrayBuffer())
40070
+ .then(async (buffer) => {
40071
+ if (this.disposed) {
40072
+ return;
40073
+ }
40074
+ const blob = new Blob([buffer], { type: "image/png" });
40075
+ const objectUrl = URL.createObjectURL(blob);
40076
+ let image;
40077
+ try {
40078
+ image = await loadImage(objectUrl);
40079
+ }
40080
+ finally {
40081
+ URL.revokeObjectURL(objectUrl);
40082
+ }
40083
+ const canvas = document.createElement("canvas");
40084
+ canvas.width = image.width;
40085
+ canvas.height = image.height;
40086
+ const ctx = canvas.getContext("2d");
40087
+ ctx.drawImage(image, 0, 0);
40088
+ const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
40089
+ const flags = new Uint8Array(canvas.width * canvas.height);
40090
+ for (let p = 0; p < flags.length; p++) {
40091
+ flags[p] = pixels[p * 4 + 3] >= 128 ? 1 : 0;
40092
+ }
40093
+ this.baselineFlags = flags;
40094
+ this.baselineDims = { width: canvas.width, height: canvas.height };
40095
+ })
40096
+ .catch((e) => {
40097
+ // A missing or unreadable mask must not stop the animation; it just means nothing is hidden.
40098
+ console.warn("TextureFrameSeriesAnimator: could not load the baseline mask, drawing it instead.", e);
40099
+ });
40100
+ return this.baselineLoad;
40101
+ }
40102
+ /**
40103
+ * Clears alpha on baseline texels, which is what makes hiding the dry/land sentinel a render choice.
40104
+ */
40105
+ applyBaselineMask(pixels, valuePixels, width, height) {
40106
+ const flags = this.baselineFlags;
40107
+ if (!flags || !this.baselineDims) {
40108
+ return;
40109
+ }
40110
+ // A mask from a different raster size cannot be indexed against this frame, and guessing a
40111
+ // resampling would silently hide the wrong ground.
40112
+ if (this.baselineDims.width !== width || this.baselineDims.height !== height) {
40113
+ return;
40114
+ }
40115
+ for (let p = 0; p < flags.length; p++) {
40116
+ if (flags[p]) {
40117
+ pixels[p * 4 + 3] = 0;
40118
+ if (valuePixels) {
40119
+ valuePixels[p * 4 + 3] = 0;
40120
+ }
40121
+ }
40122
+ }
40123
+ }
39990
40124
  beginCrossfadeTo(idx) {
39991
40125
  const to = this.frameCache[idx];
39992
40126
  this.fadeFromValuePixels = this.displayedValuePixels === null && this.valueCache[idx]
@@ -40233,11 +40367,13 @@ void main() {
40233
40367
  return _glsl300Support;
40234
40368
  }
40235
40369
  /*
40236
- * Places grid node (fx, fy) on the ellipsoid, walking equal steps of east/north metres.
40237
- * Cesium maps a polygon's material across a tangent-plane rectangle and the generated raster is
40238
- * linear in a projected CRS, so stepping in degrees instead would skew the surface against the flat-textured polygon it replaces.
40370
+ * Places grid node (fx, fy) on the ellipsoid, stepping the way the raster was sampled.
40371
+ * 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.
40239
40372
  */
40240
- function makePlacer(extent) {
40373
+ function makePlacer(extent, pixelPlacement) {
40374
+ if (pixelPlacement === "lonlat") {
40375
+ return (fx, fy) => Cartesian3.fromDegrees(Math$1.lerp(extent.West, extent.East, fx), Math$1.lerp(extent.North, extent.South, fy), 0);
40376
+ }
40241
40377
  const ellipsoid = Ellipsoid.WGS84;
40242
40378
  const centre = Cartesian3.fromDegrees((extent.West + extent.East) / 2, (extent.South + extent.North) / 2, 0);
40243
40379
  const frame = Transforms.eastNorthUpToFixedFrame(centre);
@@ -40251,21 +40387,21 @@ void main() {
40251
40387
  };
40252
40388
  }
40253
40389
  /*
40254
- * Builds one tile's grid, plus a duplicated perimeter ring the shader pushes downwards to cover
40255
- * the crack where a neighbouring tile at a different density interpolates the shared edge.
40390
+ * Builds one tile's grid, optionally plus a duplicated perimeter ring the shader pushes downwards to
40391
+ * cover the crack where a neighbouring tile at a different density interpolates the shared edge.
40256
40392
  */
40257
- function buildGeometry(extent, resolution, patch) {
40393
+ function buildGeometry(extent, resolution, patch, pixelPlacement, withSkirt = false) {
40258
40394
  const cols = resolution;
40259
40395
  const rows = resolution;
40260
40396
  const gridCount = (cols + 1) * (rows + 1);
40261
- const perimeterCount = 2 * (cols + 1) + 2 * (rows + 1) - 4;
40397
+ const perimeterCount = withSkirt ? 2 * (cols + 1) + 2 * (rows + 1) - 4 : 0;
40262
40398
  const count = gridCount + perimeterCount;
40263
40399
  const positions = new Float64Array(count * 3);
40264
40400
  const normals = new Float32Array(count * 3);
40265
40401
  const sts = new Float32Array(count * 2);
40266
40402
  const skirts = new Float32Array(count);
40267
40403
  const ellipsoid = Ellipsoid.WGS84;
40268
- const place = makePlacer(extent);
40404
+ const place = makePlacer(extent, pixelPlacement);
40269
40405
  let v = 0;
40270
40406
  for (let row = 0; row <= rows; row++) {
40271
40407
  // Texture row 0 is the north edge, so t runs opposite to northing.
@@ -40316,26 +40452,28 @@ void main() {
40316
40452
  loop.push(row * (cols + 1));
40317
40453
  }
40318
40454
  const skirtStart = gridCount;
40319
- loop.forEach((gridIndex, k) => {
40320
- const target = skirtStart + k;
40321
- positions[target * 3] = positions[gridIndex * 3];
40322
- positions[target * 3 + 1] = positions[gridIndex * 3 + 1];
40323
- positions[target * 3 + 2] = positions[gridIndex * 3 + 2];
40324
- normals[target * 3] = normals[gridIndex * 3];
40325
- normals[target * 3 + 1] = normals[gridIndex * 3 + 1];
40326
- normals[target * 3 + 2] = normals[gridIndex * 3 + 2];
40327
- sts[target * 2] = sts[gridIndex * 2];
40328
- sts[target * 2 + 1] = sts[gridIndex * 2 + 1];
40329
- skirts[target] = 1;
40330
- });
40331
- for (let k = 0; k < loop.length; k++) {
40332
- const nextK = (k + 1) % loop.length;
40333
- indices[i++] = loop[k];
40334
- indices[i++] = skirtStart + k;
40335
- indices[i++] = loop[nextK];
40336
- indices[i++] = loop[nextK];
40337
- indices[i++] = skirtStart + k;
40338
- indices[i++] = skirtStart + nextK;
40455
+ if (withSkirt) {
40456
+ loop.forEach((gridIndex, k) => {
40457
+ const target = skirtStart + k;
40458
+ positions[target * 3] = positions[gridIndex * 3];
40459
+ positions[target * 3 + 1] = positions[gridIndex * 3 + 1];
40460
+ positions[target * 3 + 2] = positions[gridIndex * 3 + 2];
40461
+ normals[target * 3] = normals[gridIndex * 3];
40462
+ normals[target * 3 + 1] = normals[gridIndex * 3 + 1];
40463
+ normals[target * 3 + 2] = normals[gridIndex * 3 + 2];
40464
+ sts[target * 2] = sts[gridIndex * 2];
40465
+ sts[target * 2 + 1] = sts[gridIndex * 2 + 1];
40466
+ skirts[target] = 1;
40467
+ });
40468
+ for (let k = 0; k < loop.length; k++) {
40469
+ const nextK = (k + 1) % loop.length;
40470
+ indices[i++] = loop[k];
40471
+ indices[i++] = skirtStart + k;
40472
+ indices[i++] = loop[nextK];
40473
+ indices[i++] = loop[nextK];
40474
+ indices[i++] = skirtStart + k;
40475
+ indices[i++] = skirtStart + nextK;
40476
+ }
40339
40477
  }
40340
40478
  const geometry = new Geometry({
40341
40479
  attributes: {
@@ -40393,6 +40531,8 @@ void main() {
40393
40531
  this.valueMax = (_b = options.valueMax) !== null && _b !== void 0 ? _b : 1;
40394
40532
  this.exaggeration = (_c = options.exaggeration) !== null && _c !== void 0 ? _c : autoExaggeration(this.extent, this.valueMin, this.valueMax);
40395
40533
  this.baseHeight = (_d = options.baseHeight) !== null && _d !== void 0 ? _d : 0;
40534
+ this.pixelPlacement = options.pixelPlacement;
40535
+ this.tileSkirts = Boolean(options.tileSkirts);
40396
40536
  this.lowColor = toCesiumColor(options.lowColor || DEFAULT_LOW_COLOR);
40397
40537
  this.highColor = toCesiumColor(options.highColor || DEFAULT_HIGH_COLOR);
40398
40538
  this.tiles = buildTiles(hasCoverage);
@@ -40400,6 +40540,9 @@ void main() {
40400
40540
  GetFollowsGround() {
40401
40541
  return this.followGround;
40402
40542
  }
40543
+ GetPixelPlacement() {
40544
+ return this.pixelPlacement;
40545
+ }
40403
40546
  GetBaseHeight() {
40404
40547
  return this.baseHeight;
40405
40548
  }
@@ -40614,7 +40757,7 @@ void main() {
40614
40757
  if (cached) {
40615
40758
  return cached;
40616
40759
  }
40617
- const geometry = buildGeometry(this.extent, tier, tile.patch);
40760
+ const geometry = buildGeometry(this.extent, tier, tile.patch, this.pixelPlacement, this.tileSkirts);
40618
40761
  const attributeLocations = GeometryPipeline.createAttributeLocations(geometry);
40619
40762
  const mesh = {
40620
40763
  attributeLocations,
@@ -40707,7 +40850,9 @@ void main() {
40707
40850
  u_valueRange: () => self.valueMax - self.valueMin,
40708
40851
  u_exaggeration: () => self.exaggeration,
40709
40852
  u_baseHeight: () => self.baseHeight,
40710
- u_skirtDepth: () => Math.max(2, Math.abs(self.valueMax - self.valueMin) * self.exaggeration * SKIRT_FRACTION),
40853
+ u_skirtDepth: () => self.tileSkirts
40854
+ ? Math.max(2, Math.abs(self.valueMax - self.valueMin) * self.exaggeration * SKIRT_FRACTION)
40855
+ : 0,
40711
40856
  u_coverageCutoff: () => COVERAGE_CUTOFF,
40712
40857
  u_lowColor: () => self.lowColor,
40713
40858
  u_highColor: () => self.highColor,
@@ -40840,6 +40985,8 @@ var EntityRenderEnginePolygon;
40840
40985
  }
40841
40986
  }
40842
40987
  const drawsDisplacedSurface = Boolean(extrusionArchive);
40988
+ // The displaced surface IS the entity's fill, drawn as its own primitive at the value's height.
40989
+ // Leaving the flat polygon filled as well shows a second copy of the parcel on the ground under it.
40843
40990
  const hasFill = (drawsDisplacedSurface ? true
40844
40991
  : fillType === Style.EPolygonFillType.Texture
40845
40992
  ? Boolean(textureDataUri || frameArchive || cFillColor.alpha > 0)
@@ -40928,6 +41075,7 @@ var EntityRenderEnginePolygon;
40928
41075
  polygon: {
40929
41076
  hierarchy: new PolygonHierarchy(posses, holePosses.map(x => new PolygonHierarchy(x))),
40930
41077
  material: fillMaterial,
41078
+ fill: !drawsDisplacedSurface,
40931
41079
  extrudedHeight: extrusion.value,
40932
41080
  extrudedHeightReference: extrusion.exHeightRef,
40933
41081
  shadows: ShadowMode.ENABLED,
@@ -40958,6 +41106,12 @@ var EntityRenderEnginePolygon;
40958
41106
  if (!CompareCPolygonHierarchies(oldHierarchy, newHierarchy)) {
40959
41107
  cEntity.polygon.hierarchy = new ConstantProperty(newHierarchy);
40960
41108
  }
41109
+ // Toggling extrusion styling on an already-rendered entity flips this,
41110
+ // so it has to be reconciled rather than set only at creation.
41111
+ const oldFill = GetCValue(params.viewer, cEntity.polygon.fill);
41112
+ if (oldFill !== !drawsDisplacedSurface) {
41113
+ cEntity.polygon.fill = new ConstantProperty(!drawsDisplacedSurface);
41114
+ }
40961
41115
  const oldExtrusion = GetCValue(params.viewer, cEntity.polygon.extrudedHeight);
40962
41116
  if (oldExtrusion != extrusion.value) {
40963
41117
  cEntity.polygon.extrudedHeight = new ConstantProperty(extrusion.value);
@@ -41011,7 +41165,7 @@ var EntityRenderEnginePolygon;
41011
41165
  }
41012
41166
  // Must run before any material is baked below: the Animator takes over polygon.material, so a bake
41013
41167
  // has to know whether it is baking over a live animation or over a disposed one's restored material.
41014
- const animator = syncTextureFrameArchive(cEntity, drawsDisplacedSurface ? null : frameArchive, params.viewer, style.textureColorMask);
41168
+ const animator = syncTextureFrameArchive(cEntity, drawsDisplacedSurface ? null : frameArchive, params.viewer, style.textureColorMask, style.maskTextureBaseline);
41015
41169
  syncDisplacedSurface({
41016
41170
  cEntity,
41017
41171
  extrusionArchive,
@@ -41496,12 +41650,17 @@ function syncDisplacedSurface(params) {
41496
41650
  frames: extrusionArchive.metadata.Frames,
41497
41651
  textureColorMask: style.textureColorMask,
41498
41652
  driveMaterial: false,
41499
- produceValueCanvas: true
41653
+ produceValueCanvas: true,
41654
+ baselineMask: extrusionArchive.metadata.BaselineMask,
41655
+ // Off-type for the same reason as the fill path: the pinned bruce-models predates the field.
41656
+ maskBaseline: style.maskTextureBaseline
41500
41657
  });
41501
41658
  cEntity[EXTRUSION_ANIMATOR_KEY] = animator;
41502
41659
  const mask = style.textureColorMask;
41503
41660
  const surface = new DisplacedSurfacePrimitive.Surface({
41504
41661
  extent,
41662
+ // The grid has to be walked the way the raster was sampled, or the surface skews across the extent.
41663
+ pixelPlacement: extrusionArchive.metadata.PixelPlacement,
41505
41664
  source: null,
41506
41665
  baseHeight: placement.baseHeight,
41507
41666
  followGround: placement.followGround,
@@ -41593,7 +41752,7 @@ function disposeDisplacedSurface(cEntity, viewer) {
41593
41752
  * @param viewer
41594
41753
  * @param textureColorMask
41595
41754
  */
41596
- function syncTextureFrameArchive(cEntity, frameArchive, viewer, textureColorMask) {
41755
+ function syncTextureFrameArchive(cEntity, frameArchive, viewer, textureColorMask, maskTextureBaseline) {
41597
41756
  const existing = cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY];
41598
41757
  if (frameArchive && existing && !existing.IsDisposed() && existing.GetArchiveUrl() === frameArchive.url) {
41599
41758
  return existing;
@@ -41610,7 +41769,9 @@ function syncTextureFrameArchive(cEntity, frameArchive, viewer, textureColorMask
41610
41769
  entity: cEntity,
41611
41770
  archiveUrl: frameArchive.url,
41612
41771
  frames: frameArchive.metadata.Frames,
41613
- textureColorMask
41772
+ textureColorMask,
41773
+ baselineMask: frameArchive.metadata.BaselineMask,
41774
+ maskBaseline: maskTextureBaseline
41614
41775
  });
41615
41776
  cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY] = animator;
41616
41777
  return animator;
@@ -43222,7 +43383,7 @@ var StyleUtils;
43222
43383
  StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
43223
43384
  })(StyleUtils || (StyleUtils = {}));
43224
43385
 
43225
- const VERSION = "7.1.4";
43386
+ const VERSION = "7.1.5";
43226
43387
  /**
43227
43388
  * Updates the environment instance used by bruce-cesium to one specified.
43228
43389
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.